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     Roo.bootstrap.Body.superclass.constructor.call(this, config);
395     this.el = Roo.get(document.body);
396     if (this.cls && this.cls.length) {
397         Roo.get(document.body).addClass(this.cls);
398     }
399 };
400
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
402     
403     is_body : true,// just to make sure it's constructed?
404     
405         autoCreate : {
406         cls: 'container'
407     },
408     onRender : function(ct, position)
409     {
410        /* Roo.log("Roo.bootstrap.Body - onRender");
411         if (this.cls && this.cls.length) {
412             Roo.get(document.body).addClass(this.cls);
413         }
414         // style??? xttr???
415         */
416     }
417     
418     
419  
420    
421 });
422
423  /*
424  * - LGPL
425  *
426  * button group
427  * 
428  */
429
430
431 /**
432  * @class Roo.bootstrap.ButtonGroup
433  * @extends Roo.bootstrap.Component
434  * Bootstrap ButtonGroup class
435  * @cfg {String} size lg | sm | xs (default empty normal)
436  * @cfg {String} align vertical | justified  (default none)
437  * @cfg {String} direction up | down (default down)
438  * @cfg {Boolean} toolbar false | true
439  * @cfg {Boolean} btn true | false
440  * 
441  * 
442  * @constructor
443  * Create a new Input
444  * @param {Object} config The config object
445  */
446
447 Roo.bootstrap.ButtonGroup = function(config){
448     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
449 };
450
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
452     
453     size: '',
454     align: '',
455     direction: '',
456     toolbar: false,
457     btn: true,
458
459     getAutoCreate : function(){
460         var cfg = {
461             cls: 'btn-group',
462             html : null
463         };
464         
465         cfg.html = this.html || cfg.html;
466         
467         if (this.toolbar) {
468             cfg = {
469                 cls: 'btn-toolbar',
470                 html: null
471             };
472             
473             return cfg;
474         }
475         
476         if (['vertical','justified'].indexOf(this.align)!==-1) {
477             cfg.cls = 'btn-group-' + this.align;
478             
479             if (this.align == 'justified') {
480                 console.log(this.items);
481             }
482         }
483         
484         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485             cfg.cls += ' btn-group-' + this.size;
486         }
487         
488         if (this.direction == 'up') {
489             cfg.cls += ' dropup' ;
490         }
491         
492         return cfg;
493     }
494    
495 });
496
497  /*
498  * - LGPL
499  *
500  * button
501  * 
502  */
503
504 /**
505  * @class Roo.bootstrap.Button
506  * @extends Roo.bootstrap.Component
507  * Bootstrap Button class
508  * @cfg {String} html The button content
509  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
510  * @cfg {String} size ( lg | sm | xs)
511  * @cfg {String} tag ( a | input | submit)
512  * @cfg {String} href empty or href
513  * @cfg {Boolean} disabled default false;
514  * @cfg {Boolean} isClose default false;
515  * @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)
516  * @cfg {String} badge text for badge
517  * @cfg {String} theme default 
518  * @cfg {Boolean} inverse 
519  * @cfg {Boolean} toggle 
520  * @cfg {String} ontext text for on toggle state
521  * @cfg {String} offtext text for off toggle state
522  * @cfg {Boolean} defaulton 
523  * @cfg {Boolean} preventDefault  default true
524  * @cfg {Boolean} removeClass remove the standard class..
525  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
526  * 
527  * @constructor
528  * Create a new button
529  * @param {Object} config The config object
530  */
531
532
533 Roo.bootstrap.Button = function(config){
534     Roo.bootstrap.Button.superclass.constructor.call(this, config);
535     this.addEvents({
536         // raw events
537         /**
538          * @event click
539          * When a butotn is pressed
540          * @param {Roo.bootstrap.Button} this
541          * @param {Roo.EventObject} e
542          */
543         "click" : true,
544          /**
545          * @event toggle
546          * After the button has been toggles
547          * @param {Roo.EventObject} e
548          * @param {boolean} pressed (also available as button.pressed)
549          */
550         "toggle" : true
551     });
552 };
553
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
555     html: false,
556     active: false,
557     weight: '',
558     size: '',
559     tag: 'button',
560     href: '',
561     disabled: false,
562     isClose: false,
563     glyphicon: '',
564     badge: '',
565     theme: 'default',
566     inverse: false,
567     
568     toggle: false,
569     ontext: 'ON',
570     offtext: 'OFF',
571     defaulton: true,
572     preventDefault: true,
573     removeClass: false,
574     name: false,
575     target: false,
576     
577     
578     pressed : null,
579      
580     
581     getAutoCreate : function(){
582         
583         var cfg = {
584             tag : 'button',
585             cls : 'roo-button',
586             html: ''
587         };
588         
589         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
591             this.tag = 'button';
592         } else {
593             cfg.tag = this.tag;
594         }
595         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
596         
597         if (this.toggle == true) {
598             cfg={
599                 tag: 'div',
600                 cls: 'slider-frame roo-button',
601                 cn: [
602                     {
603                         tag: 'span',
604                         'data-on-text':'ON',
605                         'data-off-text':'OFF',
606                         cls: 'slider-button',
607                         html: this.offtext
608                     }
609                 ]
610             };
611             
612             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613                 cfg.cls += ' '+this.weight;
614             }
615             
616             return cfg;
617         }
618         
619         if (this.isClose) {
620             cfg.cls += ' close';
621             
622             cfg["aria-hidden"] = true;
623             
624             cfg.html = "&times;";
625             
626             return cfg;
627         }
628         
629          
630         if (this.theme==='default') {
631             cfg.cls = 'btn roo-button';
632             
633             //if (this.parentType != 'Navbar') {
634             this.weight = this.weight.length ?  this.weight : 'default';
635             //}
636             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
637                 
638                 cfg.cls += ' btn-' + this.weight;
639             }
640         } else if (this.theme==='glow') {
641             
642             cfg.tag = 'a';
643             cfg.cls = 'btn-glow roo-button';
644             
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' ' + this.weight;
648             }
649         }
650    
651         
652         if (this.inverse) {
653             this.cls += ' inverse';
654         }
655         
656         
657         if (this.active) {
658             cfg.cls += ' active';
659         }
660         
661         if (this.disabled) {
662             cfg.disabled = 'disabled';
663         }
664         
665         if (this.items) {
666             Roo.log('changing to ul' );
667             cfg.tag = 'ul';
668             this.glyphicon = 'caret';
669         }
670         
671         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
672          
673         //gsRoo.log(this.parentType);
674         if (this.parentType === 'Navbar' && !this.parent().bar) {
675             Roo.log('changing to li?');
676             
677             cfg.tag = 'li';
678             
679             cfg.cls = '';
680             cfg.cn =  [{
681                 tag : 'a',
682                 cls : 'roo-button',
683                 html : this.html,
684                 href : this.href || '#'
685             }];
686             if (this.menu) {
687                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
688                 cfg.cls += ' dropdown';
689             }   
690             
691             delete cfg.html;
692             
693         }
694         
695        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
696         
697         if (this.glyphicon) {
698             cfg.html = ' ' + cfg.html;
699             
700             cfg.cn = [
701                 {
702                     tag: 'span',
703                     cls: 'glyphicon glyphicon-' + this.glyphicon
704                 }
705             ];
706         }
707         
708         if (this.badge) {
709             cfg.html += ' ';
710             
711             cfg.tag = 'a';
712             
713 //            cfg.cls='btn roo-button';
714             
715             cfg.href=this.href;
716             
717             var value = cfg.html;
718             
719             if(this.glyphicon){
720                 value = {
721                             tag: 'span',
722                             cls: 'glyphicon glyphicon-' + this.glyphicon,
723                             html: this.html
724                         };
725                 
726             }
727             
728             cfg.cn = [
729                 value,
730                 {
731                     tag: 'span',
732                     cls: 'badge',
733                     html: this.badge
734                 }
735             ];
736             
737             cfg.html='';
738         }
739         
740         if (this.menu) {
741             cfg.cls += ' dropdown';
742             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
743         }
744         
745         if (cfg.tag !== 'a' && this.href !== '') {
746             throw "Tag must be a to set href.";
747         } else if (this.href.length > 0) {
748             cfg.href = this.href;
749         }
750         
751         if(this.removeClass){
752             cfg.cls = '';
753         }
754         
755         if(this.target){
756             cfg.target = this.target;
757         }
758         
759         return cfg;
760     },
761     initEvents: function() {
762        // Roo.log('init events?');
763 //        Roo.log(this.el.dom);
764         // add the menu...
765         
766         if (typeof (this.menu) != 'undefined') {
767             this.menu.parentType = this.xtype;
768             this.menu.triggerEl = this.el;
769             this.addxtype(Roo.apply({}, this.menu));
770         }
771
772
773        if (this.el.hasClass('roo-button')) {
774             this.el.on('click', this.onClick, this);
775        } else {
776             this.el.select('.roo-button').on('click', this.onClick, this);
777        }
778        
779        if(this.removeClass){
780            this.el.on('click', this.onClick, this);
781        }
782        
783        this.el.enableDisplayMode();
784         
785     },
786     onClick : function(e)
787     {
788         if (this.disabled) {
789             return;
790         }
791         
792         
793         Roo.log('button on click ');
794         if(this.preventDefault){
795             e.preventDefault();
796         }
797         if (this.pressed === true || this.pressed === false) {
798             this.pressed = !this.pressed;
799             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800             this.fireEvent('toggle', this, e, this.pressed);
801         }
802         
803         
804         this.fireEvent('click', this, e);
805     },
806     
807     /**
808      * Enables this button
809      */
810     enable : function()
811     {
812         this.disabled = false;
813         this.el.removeClass('disabled');
814     },
815     
816     /**
817      * Disable this button
818      */
819     disable : function()
820     {
821         this.disabled = true;
822         this.el.addClass('disabled');
823     },
824      /**
825      * sets the active state on/off, 
826      * @param {Boolean} state (optional) Force a particular state
827      */
828     setActive : function(v) {
829         
830         this.el[v ? 'addClass' : 'removeClass']('active');
831     },
832      /**
833      * toggles the current active state 
834      */
835     toggleActive : function()
836     {
837        var active = this.el.hasClass('active');
838        this.setActive(!active);
839        
840         
841     },
842     setText : function(str)
843     {
844         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
845     },
846     getText : function()
847     {
848         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
849     },
850     hide: function() {
851        
852      
853         this.el.hide();   
854     },
855     show: function() {
856        
857         this.el.show();   
858     }
859     
860     
861 });
862
863  /*
864  * - LGPL
865  *
866  * column
867  * 
868  */
869
870 /**
871  * @class Roo.bootstrap.Column
872  * @extends Roo.bootstrap.Component
873  * Bootstrap Column class
874  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
882  *
883  * 
884  * @cfg {Boolean} hidden (true|false) hide the element
885  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886  * @cfg {String} fa (ban|check|...) font awesome icon
887  * @cfg {Number} fasize (1|2|....) font awsome size
888
889  * @cfg {String} icon (info-sign|check|...) glyphicon name
890
891  * @cfg {String} html content of column.
892  * 
893  * @constructor
894  * Create a new Column
895  * @param {Object} config The config object
896  */
897
898 Roo.bootstrap.Column = function(config){
899     Roo.bootstrap.Column.superclass.constructor.call(this, config);
900 };
901
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
903     
904     xs: false,
905     sm: false,
906     md: false,
907     lg: false,
908     xsoff: false,
909     smoff: false,
910     mdoff: false,
911     lgoff: false,
912     html: '',
913     offset: 0,
914     alert: false,
915     fa: false,
916     icon : false,
917     hidden : false,
918     fasize : 1,
919     
920     getAutoCreate : function(){
921         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
922         
923         cfg = {
924             tag: 'div',
925             cls: 'column'
926         };
927         
928         var settings=this;
929         ['xs','sm','md','lg'].map(function(size){
930             //Roo.log( size + ':' + settings[size]);
931             
932             if (settings[size+'off'] !== false) {
933                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
934             }
935             
936             if (settings[size] === false) {
937                 return;
938             }
939             
940             if (!settings[size]) { // 0 = hidden
941                 cfg.cls += ' hidden-' + size;
942                 return;
943             }
944             cfg.cls += ' col-' + size + '-' + settings[size];
945             
946         });
947         
948         if (this.hidden) {
949             cfg.cls += ' hidden';
950         }
951         
952         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953             cfg.cls +=' alert alert-' + this.alert;
954         }
955         
956         
957         if (this.html.length) {
958             cfg.html = this.html;
959         }
960         if (this.fa) {
961             var fasize = '';
962             if (this.fasize > 1) {
963                 fasize = ' fa-' + this.fasize + 'x';
964             }
965             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
966             
967             
968         }
969         if (this.icon) {
970             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
971         }
972         
973         return cfg;
974     }
975    
976 });
977
978  
979
980  /*
981  * - LGPL
982  *
983  * page container.
984  * 
985  */
986
987
988 /**
989  * @class Roo.bootstrap.Container
990  * @extends Roo.bootstrap.Component
991  * Bootstrap Container class
992  * @cfg {Boolean} jumbotron is it a jumbotron element
993  * @cfg {String} html content of element
994  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
996  * @cfg {String} header content of header (for panel)
997  * @cfg {String} footer content of footer (for panel)
998  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999  * @cfg {String} tag (header|aside|section) type of HTML tag.
1000  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001  * @cfg {String} fa font awesome icon
1002  * @cfg {String} icon (info-sign|check|...) glyphicon name
1003  * @cfg {Boolean} hidden (true|false) hide the element
1004  * @cfg {Boolean} expandable (true|false) default false
1005  * @cfg {Boolean} expanded (true|false) default true
1006  * @cfg {String} rheader contet on the right of header
1007  * @cfg {Boolean} clickable (true|false) default false
1008
1009  *     
1010  * @constructor
1011  * Create a new Container
1012  * @param {Object} config The config object
1013  */
1014
1015 Roo.bootstrap.Container = function(config){
1016     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020          /**
1021          * @event expand
1022          * After the panel has been expand
1023          * 
1024          * @param {Roo.bootstrap.Container} this
1025          */
1026         "expand" : true,
1027         /**
1028          * @event collapse
1029          * After the panel has been collapsed
1030          * 
1031          * @param {Roo.bootstrap.Container} this
1032          */
1033         "collapse" : true,
1034         /**
1035          * @event click
1036          * When a element is chick
1037          * @param {Roo.bootstrap.Container} this
1038          * @param {Roo.EventObject} e
1039          */
1040         "click" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1045     
1046     jumbotron : false,
1047     well: '',
1048     panel : '',
1049     header: '',
1050     footer : '',
1051     sticky: '',
1052     tag : false,
1053     alert : false,
1054     fa: false,
1055     icon : false,
1056     expandable : false,
1057     rheader : '',
1058     expanded : true,
1059     clickable: false,
1060   
1061      
1062     getChildContainer : function() {
1063         
1064         if(!this.el){
1065             return false;
1066         }
1067         
1068         if (this.panel.length) {
1069             return this.el.select('.panel-body',true).first();
1070         }
1071         
1072         return this.el;
1073     },
1074     
1075     
1076     getAutoCreate : function(){
1077         
1078         var cfg = {
1079             tag : this.tag || 'div',
1080             html : '',
1081             cls : ''
1082         };
1083         if (this.jumbotron) {
1084             cfg.cls = 'jumbotron';
1085         }
1086         
1087         
1088         
1089         // - this is applied by the parent..
1090         //if (this.cls) {
1091         //    cfg.cls = this.cls + '';
1092         //}
1093         
1094         if (this.sticky.length) {
1095             
1096             var bd = Roo.get(document.body);
1097             if (!bd.hasClass('bootstrap-sticky')) {
1098                 bd.addClass('bootstrap-sticky');
1099                 Roo.select('html',true).setStyle('height', '100%');
1100             }
1101              
1102             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1103         }
1104         
1105         
1106         if (this.well.length) {
1107             switch (this.well) {
1108                 case 'lg':
1109                 case 'sm':
1110                     cfg.cls +=' well well-' +this.well;
1111                     break;
1112                 default:
1113                     cfg.cls +=' well';
1114                     break;
1115             }
1116         }
1117         
1118         if (this.hidden) {
1119             cfg.cls += ' hidden';
1120         }
1121         
1122         
1123         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124             cfg.cls +=' alert alert-' + this.alert;
1125         }
1126         
1127         var body = cfg;
1128         
1129         if (this.panel.length) {
1130             cfg.cls += ' panel panel-' + this.panel;
1131             cfg.cn = [];
1132             if (this.header.length) {
1133                 
1134                 var h = [];
1135                 
1136                 if(this.expandable){
1137                     
1138                     cfg.cls = cfg.cls + ' expandable';
1139                     
1140                     h.push({
1141                         tag: 'i',
1142                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1143                     });
1144                     
1145                 }
1146                 
1147                 h.push(
1148                     {
1149                         tag: 'span',
1150                         cls : 'panel-title',
1151                         html : (this.expandable ? '&nbsp;' : '') + this.header
1152                     },
1153                     {
1154                         tag: 'span',
1155                         cls: 'panel-header-right',
1156                         html: this.rheader
1157                     }
1158                 );
1159                 
1160                 cfg.cn.push({
1161                     cls : 'panel-heading',
1162                     style : this.expandable ? 'cursor: pointer' : '',
1163                     cn : h
1164                 });
1165                 
1166             }
1167             
1168             body = false;
1169             cfg.cn.push({
1170                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1171                 html : this.html
1172             });
1173             
1174             
1175             if (this.footer.length) {
1176                 cfg.cn.push({
1177                     cls : 'panel-footer',
1178                     html : this.footer
1179                     
1180                 });
1181             }
1182             
1183         }
1184         
1185         if (body) {
1186             body.html = this.html || cfg.html;
1187             // prefix with the icons..
1188             if (this.fa) {
1189                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1190             }
1191             if (this.icon) {
1192                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1193             }
1194             
1195             
1196         }
1197         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198             cfg.cls =  'container';
1199         }
1200         
1201         return cfg;
1202     },
1203     
1204     initEvents: function() 
1205     {
1206         if(this.expandable){
1207             var headerEl = this.headerEl();
1208         
1209             if(headerEl){
1210                 headerEl.on('click', this.onToggleClick, this);
1211             }
1212         }
1213         
1214         if(this.clickable){
1215             this.el.on('click', this.onClick, this);
1216         }
1217         
1218     },
1219     
1220     onToggleClick : function()
1221     {
1222         var headerEl = this.headerEl();
1223         
1224         if(!headerEl){
1225             return;
1226         }
1227         
1228         if(this.expanded){
1229             this.collapse();
1230             return;
1231         }
1232         
1233         this.expand();
1234     },
1235     
1236     expand : function()
1237     {
1238         if(this.fireEvent('expand', this)) {
1239             
1240             this.expanded = true;
1241             
1242             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1243             
1244             this.el.select('.panel-body',true).first().removeClass('hide');
1245             
1246             var toggleEl = this.toggleEl();
1247
1248             if(!toggleEl){
1249                 return;
1250             }
1251
1252             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1253         }
1254         
1255     },
1256     
1257     collapse : function()
1258     {
1259         if(this.fireEvent('collapse', this)) {
1260             
1261             this.expanded = false;
1262             
1263             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264             this.el.select('.panel-body',true).first().addClass('hide');
1265         
1266             var toggleEl = this.toggleEl();
1267
1268             if(!toggleEl){
1269                 return;
1270             }
1271
1272             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1273         }
1274     },
1275     
1276     toggleEl : function()
1277     {
1278         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1279             return;
1280         }
1281         
1282         return this.el.select('.panel-heading .fa',true).first();
1283     },
1284     
1285     headerEl : function()
1286     {
1287         if(!this.el || !this.panel.length || !this.header.length){
1288             return;
1289         }
1290         
1291         return this.el.select('.panel-heading',true).first()
1292     },
1293     
1294     titleEl : function()
1295     {
1296         if(!this.el || !this.panel.length || !this.header.length){
1297             return;
1298         }
1299         
1300         return this.el.select('.panel-title',true).first();
1301     },
1302     
1303     setTitle : function(v)
1304     {
1305         var titleEl = this.titleEl();
1306         
1307         if(!titleEl){
1308             return;
1309         }
1310         
1311         titleEl.dom.innerHTML = v;
1312     },
1313     
1314     getTitle : function()
1315     {
1316         
1317         var titleEl = this.titleEl();
1318         
1319         if(!titleEl){
1320             return '';
1321         }
1322         
1323         return titleEl.dom.innerHTML;
1324     },
1325     
1326     setRightTitle : function(v)
1327     {
1328         var t = this.el.select('.panel-header-right',true).first();
1329         
1330         if(!t){
1331             return;
1332         }
1333         
1334         t.dom.innerHTML = v;
1335     },
1336     
1337     onClick : function(e)
1338     {
1339         e.preventDefault();
1340         
1341         this.fireEvent('click', this, e);
1342     }
1343    
1344 });
1345
1346  /*
1347  * - LGPL
1348  *
1349  * image
1350  * 
1351  */
1352
1353
1354 /**
1355  * @class Roo.bootstrap.Img
1356  * @extends Roo.bootstrap.Component
1357  * Bootstrap Img class
1358  * @cfg {Boolean} imgResponsive false | true
1359  * @cfg {String} border rounded | circle | thumbnail
1360  * @cfg {String} src image source
1361  * @cfg {String} alt image alternative text
1362  * @cfg {String} href a tag href
1363  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364  * @cfg {String} xsUrl xs image source
1365  * @cfg {String} smUrl sm image source
1366  * @cfg {String} mdUrl md image source
1367  * @cfg {String} lgUrl lg image source
1368  * 
1369  * @constructor
1370  * Create a new Input
1371  * @param {Object} config The config object
1372  */
1373
1374 Roo.bootstrap.Img = function(config){
1375     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1376     
1377     this.addEvents({
1378         // img events
1379         /**
1380          * @event click
1381          * The img click event for the img.
1382          * @param {Roo.EventObject} e
1383          */
1384         "click" : true
1385     });
1386 };
1387
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1389     
1390     imgResponsive: true,
1391     border: '',
1392     src: 'about:blank',
1393     href: false,
1394     target: false,
1395     xsUrl: '',
1396     smUrl: '',
1397     mdUrl: '',
1398     lgUrl: '',
1399
1400     getAutoCreate : function()
1401     {   
1402         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403             return this.createSingleImg();
1404         }
1405         
1406         var cfg = {
1407             tag: 'div',
1408             cls: 'roo-image-responsive-group',
1409             cn: []
1410         };
1411         var _this = this;
1412         
1413         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1414             
1415             if(!_this[size + 'Url']){
1416                 return;
1417             }
1418             
1419             var img = {
1420                 tag: 'img',
1421                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422                 html: _this.html || cfg.html,
1423                 src: _this[size + 'Url']
1424             };
1425             
1426             img.cls += ' roo-image-responsive-' + size;
1427             
1428             var s = ['xs', 'sm', 'md', 'lg'];
1429             
1430             s.splice(s.indexOf(size), 1);
1431             
1432             Roo.each(s, function(ss){
1433                 img.cls += ' hidden-' + ss;
1434             });
1435             
1436             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437                 cfg.cls += ' img-' + _this.border;
1438             }
1439             
1440             if(_this.alt){
1441                 cfg.alt = _this.alt;
1442             }
1443             
1444             if(_this.href){
1445                 var a = {
1446                     tag: 'a',
1447                     href: _this.href,
1448                     cn: [
1449                         img
1450                     ]
1451                 };
1452
1453                 if(this.target){
1454                     a.target = _this.target;
1455                 }
1456             }
1457             
1458             cfg.cn.push((_this.href) ? a : img);
1459             
1460         });
1461         
1462         return cfg;
1463     },
1464     
1465     createSingleImg : function()
1466     {
1467         var cfg = {
1468             tag: 'img',
1469             cls: (this.imgResponsive) ? 'img-responsive' : '',
1470             html : null,
1471             src : 'about:blank'  // just incase src get's set to undefined?!?
1472         };
1473         
1474         cfg.html = this.html || cfg.html;
1475         
1476         cfg.src = this.src || cfg.src;
1477         
1478         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479             cfg.cls += ' img-' + this.border;
1480         }
1481         
1482         if(this.alt){
1483             cfg.alt = this.alt;
1484         }
1485         
1486         if(this.href){
1487             var a = {
1488                 tag: 'a',
1489                 href: this.href,
1490                 cn: [
1491                     cfg
1492                 ]
1493             };
1494             
1495             if(this.target){
1496                 a.target = this.target;
1497             }
1498             
1499         }
1500         
1501         return (this.href) ? a : cfg;
1502     },
1503     
1504     initEvents: function() 
1505     {
1506         if(!this.href){
1507             this.el.on('click', this.onClick, this);
1508         }
1509         
1510     },
1511     
1512     onClick : function(e)
1513     {
1514         Roo.log('img onclick');
1515         this.fireEvent('click', this, e);
1516     }
1517    
1518 });
1519
1520  /*
1521  * - LGPL
1522  *
1523  * image
1524  * 
1525  */
1526
1527
1528 /**
1529  * @class Roo.bootstrap.Link
1530  * @extends Roo.bootstrap.Component
1531  * Bootstrap Link Class
1532  * @cfg {String} alt image alternative text
1533  * @cfg {String} href a tag href
1534  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1535  * @cfg {String} html the content of the link.
1536  * @cfg {String} anchor name for the anchor link
1537  * @cfg {String} fa - favicon
1538
1539  * @cfg {Boolean} preventDefault (true | false) default false
1540
1541  * 
1542  * @constructor
1543  * Create a new Input
1544  * @param {Object} config The config object
1545  */
1546
1547 Roo.bootstrap.Link = function(config){
1548     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1549     
1550     this.addEvents({
1551         // img events
1552         /**
1553          * @event click
1554          * The img click event for the img.
1555          * @param {Roo.EventObject} e
1556          */
1557         "click" : true
1558     });
1559 };
1560
1561 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1562     
1563     href: false,
1564     target: false,
1565     preventDefault: false,
1566     anchor : false,
1567     alt : false,
1568     fa: false,
1569
1570
1571     getAutoCreate : function()
1572     {
1573         var html = this.html || '';
1574         
1575         if (this.fa !== false) {
1576             html = '<i class="fa fa-' + this.fa + '"></i>';
1577         }
1578         var cfg = {
1579             tag: 'a'
1580         };
1581         // anchor's do not require html/href...
1582         if (this.anchor === false) {
1583             cfg.html = html;
1584             cfg.href = this.href || '#';
1585         } else {
1586             cfg.name = this.anchor;
1587             if (this.html !== false || this.fa !== false) {
1588                 cfg.html = html;
1589             }
1590             if (this.href !== false) {
1591                 cfg.href = this.href;
1592             }
1593         }
1594         
1595         if(this.alt !== false){
1596             cfg.alt = this.alt;
1597         }
1598         
1599         
1600         if(this.target !== false) {
1601             cfg.target = this.target;
1602         }
1603         
1604         return cfg;
1605     },
1606     
1607     initEvents: function() {
1608         
1609         if(!this.href || this.preventDefault){
1610             this.el.on('click', this.onClick, this);
1611         }
1612     },
1613     
1614     onClick : function(e)
1615     {
1616         if(this.preventDefault){
1617             e.preventDefault();
1618         }
1619         //Roo.log('img onclick');
1620         this.fireEvent('click', this, e);
1621     }
1622    
1623 });
1624
1625  /*
1626  * - LGPL
1627  *
1628  * header
1629  * 
1630  */
1631
1632 /**
1633  * @class Roo.bootstrap.Header
1634  * @extends Roo.bootstrap.Component
1635  * Bootstrap Header class
1636  * @cfg {String} html content of header
1637  * @cfg {Number} level (1|2|3|4|5|6) default 1
1638  * 
1639  * @constructor
1640  * Create a new Header
1641  * @param {Object} config The config object
1642  */
1643
1644
1645 Roo.bootstrap.Header  = function(config){
1646     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1650     
1651     //href : false,
1652     html : false,
1653     level : 1,
1654     
1655     
1656     
1657     getAutoCreate : function(){
1658         
1659         
1660         
1661         var cfg = {
1662             tag: 'h' + (1 *this.level),
1663             html: this.html || ''
1664         } ;
1665         
1666         return cfg;
1667     }
1668    
1669 });
1670
1671  
1672
1673  /*
1674  * Based on:
1675  * Ext JS Library 1.1.1
1676  * Copyright(c) 2006-2007, Ext JS, LLC.
1677  *
1678  * Originally Released Under LGPL - original licence link has changed is not relivant.
1679  *
1680  * Fork - LGPL
1681  * <script type="text/javascript">
1682  */
1683  
1684 /**
1685  * @class Roo.bootstrap.MenuMgr
1686  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1687  * @singleton
1688  */
1689 Roo.bootstrap.MenuMgr = function(){
1690    var menus, active, groups = {}, attached = false, lastShow = new Date();
1691
1692    // private - called when first menu is created
1693    function init(){
1694        menus = {};
1695        active = new Roo.util.MixedCollection();
1696        Roo.get(document).addKeyListener(27, function(){
1697            if(active.length > 0){
1698                hideAll();
1699            }
1700        });
1701    }
1702
1703    // private
1704    function hideAll(){
1705        if(active && active.length > 0){
1706            var c = active.clone();
1707            c.each(function(m){
1708                m.hide();
1709            });
1710        }
1711    }
1712
1713    // private
1714    function onHide(m){
1715        active.remove(m);
1716        if(active.length < 1){
1717            Roo.get(document).un("mouseup", onMouseDown);
1718             
1719            attached = false;
1720        }
1721    }
1722
1723    // private
1724    function onShow(m){
1725        var last = active.last();
1726        lastShow = new Date();
1727        active.add(m);
1728        if(!attached){
1729           Roo.get(document).on("mouseup", onMouseDown);
1730            
1731            attached = true;
1732        }
1733        if(m.parentMenu){
1734           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1735           m.parentMenu.activeChild = m;
1736        }else if(last && last.isVisible()){
1737           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1738        }
1739    }
1740
1741    // private
1742    function onBeforeHide(m){
1743        if(m.activeChild){
1744            m.activeChild.hide();
1745        }
1746        if(m.autoHideTimer){
1747            clearTimeout(m.autoHideTimer);
1748            delete m.autoHideTimer;
1749        }
1750    }
1751
1752    // private
1753    function onBeforeShow(m){
1754        var pm = m.parentMenu;
1755        if(!pm && !m.allowOtherMenus){
1756            hideAll();
1757        }else if(pm && pm.activeChild && active != m){
1758            pm.activeChild.hide();
1759        }
1760    }
1761
1762    // private this should really trigger on mouseup..
1763    function onMouseDown(e){
1764         Roo.log("on Mouse Up");
1765         
1766         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1767             Roo.log("MenuManager hideAll");
1768             hideAll();
1769             e.stopEvent();
1770         }
1771         
1772         
1773    }
1774
1775    // private
1776    function onBeforeCheck(mi, state){
1777        if(state){
1778            var g = groups[mi.group];
1779            for(var i = 0, l = g.length; i < l; i++){
1780                if(g[i] != mi){
1781                    g[i].setChecked(false);
1782                }
1783            }
1784        }
1785    }
1786
1787    return {
1788
1789        /**
1790         * Hides all menus that are currently visible
1791         */
1792        hideAll : function(){
1793             hideAll();  
1794        },
1795
1796        // private
1797        register : function(menu){
1798            if(!menus){
1799                init();
1800            }
1801            menus[menu.id] = menu;
1802            menu.on("beforehide", onBeforeHide);
1803            menu.on("hide", onHide);
1804            menu.on("beforeshow", onBeforeShow);
1805            menu.on("show", onShow);
1806            var g = menu.group;
1807            if(g && menu.events["checkchange"]){
1808                if(!groups[g]){
1809                    groups[g] = [];
1810                }
1811                groups[g].push(menu);
1812                menu.on("checkchange", onCheck);
1813            }
1814        },
1815
1816         /**
1817          * Returns a {@link Roo.menu.Menu} object
1818          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1819          * be used to generate and return a new Menu instance.
1820          */
1821        get : function(menu){
1822            if(typeof menu == "string"){ // menu id
1823                return menus[menu];
1824            }else if(menu.events){  // menu instance
1825                return menu;
1826            }
1827            /*else if(typeof menu.length == 'number'){ // array of menu items?
1828                return new Roo.bootstrap.Menu({items:menu});
1829            }else{ // otherwise, must be a config
1830                return new Roo.bootstrap.Menu(menu);
1831            }
1832            */
1833            return false;
1834        },
1835
1836        // private
1837        unregister : function(menu){
1838            delete menus[menu.id];
1839            menu.un("beforehide", onBeforeHide);
1840            menu.un("hide", onHide);
1841            menu.un("beforeshow", onBeforeShow);
1842            menu.un("show", onShow);
1843            var g = menu.group;
1844            if(g && menu.events["checkchange"]){
1845                groups[g].remove(menu);
1846                menu.un("checkchange", onCheck);
1847            }
1848        },
1849
1850        // private
1851        registerCheckable : function(menuItem){
1852            var g = menuItem.group;
1853            if(g){
1854                if(!groups[g]){
1855                    groups[g] = [];
1856                }
1857                groups[g].push(menuItem);
1858                menuItem.on("beforecheckchange", onBeforeCheck);
1859            }
1860        },
1861
1862        // private
1863        unregisterCheckable : function(menuItem){
1864            var g = menuItem.group;
1865            if(g){
1866                groups[g].remove(menuItem);
1867                menuItem.un("beforecheckchange", onBeforeCheck);
1868            }
1869        }
1870    };
1871 }();/*
1872  * - LGPL
1873  *
1874  * menu
1875  * 
1876  */
1877
1878 /**
1879  * @class Roo.bootstrap.Menu
1880  * @extends Roo.bootstrap.Component
1881  * Bootstrap Menu class - container for MenuItems
1882  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1883  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1884  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1885  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1886  * 
1887  * @constructor
1888  * Create a new Menu
1889  * @param {Object} config The config object
1890  */
1891
1892
1893 Roo.bootstrap.Menu = function(config){
1894     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1895     if (this.registerMenu && this.type != 'treeview')  {
1896         Roo.bootstrap.MenuMgr.register(this);
1897     }
1898     this.addEvents({
1899         /**
1900          * @event beforeshow
1901          * Fires before this menu is displayed
1902          * @param {Roo.menu.Menu} this
1903          */
1904         beforeshow : true,
1905         /**
1906          * @event beforehide
1907          * Fires before this menu is hidden
1908          * @param {Roo.menu.Menu} this
1909          */
1910         beforehide : true,
1911         /**
1912          * @event show
1913          * Fires after this menu is displayed
1914          * @param {Roo.menu.Menu} this
1915          */
1916         show : true,
1917         /**
1918          * @event hide
1919          * Fires after this menu is hidden
1920          * @param {Roo.menu.Menu} this
1921          */
1922         hide : true,
1923         /**
1924          * @event click
1925          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1926          * @param {Roo.menu.Menu} this
1927          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928          * @param {Roo.EventObject} e
1929          */
1930         click : true,
1931         /**
1932          * @event mouseover
1933          * Fires when the mouse is hovering over this menu
1934          * @param {Roo.menu.Menu} this
1935          * @param {Roo.EventObject} e
1936          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1937          */
1938         mouseover : true,
1939         /**
1940          * @event mouseout
1941          * Fires when the mouse exits this menu
1942          * @param {Roo.menu.Menu} this
1943          * @param {Roo.EventObject} e
1944          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1945          */
1946         mouseout : true,
1947         /**
1948          * @event itemclick
1949          * Fires when a menu item contained in this menu is clicked
1950          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1951          * @param {Roo.EventObject} e
1952          */
1953         itemclick: true
1954     });
1955     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1956 };
1957
1958 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1959     
1960    /// html : false,
1961     //align : '',
1962     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1963     type: false,
1964     /**
1965      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1966      */
1967     registerMenu : true,
1968     
1969     menuItems :false, // stores the menu items..
1970     
1971     hidden:true,
1972         
1973     parentMenu : false,
1974     
1975     stopEvent : true,
1976     
1977     isLink : false,
1978     
1979     getChildContainer : function() {
1980         return this.el;  
1981     },
1982     
1983     getAutoCreate : function(){
1984          
1985         //if (['right'].indexOf(this.align)!==-1) {
1986         //    cfg.cn[1].cls += ' pull-right'
1987         //}
1988         
1989         
1990         var cfg = {
1991             tag : 'ul',
1992             cls : 'dropdown-menu' ,
1993             style : 'z-index:1000'
1994             
1995         };
1996         
1997         if (this.type === 'submenu') {
1998             cfg.cls = 'submenu active';
1999         }
2000         if (this.type === 'treeview') {
2001             cfg.cls = 'treeview-menu';
2002         }
2003         
2004         return cfg;
2005     },
2006     initEvents : function() {
2007         
2008        // Roo.log("ADD event");
2009        // Roo.log(this.triggerEl.dom);
2010         
2011         this.triggerEl.on('click', this.onTriggerClick, this);
2012         
2013         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2014         
2015         this.triggerEl.addClass('dropdown-toggle');
2016         
2017         if (Roo.isTouch) {
2018             this.el.on('touchstart'  , this.onTouch, this);
2019         }
2020         this.el.on('click' , this.onClick, this);
2021
2022         this.el.on("mouseover", this.onMouseOver, this);
2023         this.el.on("mouseout", this.onMouseOut, this);
2024         
2025     },
2026     
2027     findTargetItem : function(e)
2028     {
2029         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2030         if(!t){
2031             return false;
2032         }
2033         //Roo.log(t);         Roo.log(t.id);
2034         if(t && t.id){
2035             //Roo.log(this.menuitems);
2036             return this.menuitems.get(t.id);
2037             
2038             //return this.items.get(t.menuItemId);
2039         }
2040         
2041         return false;
2042     },
2043     
2044     onTouch : function(e) 
2045     {
2046         Roo.log("menu.onTouch");
2047         //e.stopEvent(); this make the user popdown broken
2048         this.onClick(e);
2049     },
2050     
2051     onClick : function(e)
2052     {
2053         Roo.log("menu.onClick");
2054         
2055         var t = this.findTargetItem(e);
2056         if(!t || t.isContainer){
2057             return;
2058         }
2059         Roo.log(e);
2060         /*
2061         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2062             if(t == this.activeItem && t.shouldDeactivate(e)){
2063                 this.activeItem.deactivate();
2064                 delete this.activeItem;
2065                 return;
2066             }
2067             if(t.canActivate){
2068                 this.setActiveItem(t, true);
2069             }
2070             return;
2071             
2072             
2073         }
2074         */
2075        
2076         Roo.log('pass click event');
2077         
2078         t.onClick(e);
2079         
2080         this.fireEvent("click", this, t, e);
2081         
2082         var _this = this;
2083         
2084         (function() { _this.hide(); }).defer(500);
2085     },
2086     
2087     onMouseOver : function(e){
2088         var t  = this.findTargetItem(e);
2089         //Roo.log(t);
2090         //if(t){
2091         //    if(t.canActivate && !t.disabled){
2092         //        this.setActiveItem(t, true);
2093         //    }
2094         //}
2095         
2096         this.fireEvent("mouseover", this, e, t);
2097     },
2098     isVisible : function(){
2099         return !this.hidden;
2100     },
2101      onMouseOut : function(e){
2102         var t  = this.findTargetItem(e);
2103         
2104         //if(t ){
2105         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2106         //        this.activeItem.deactivate();
2107         //        delete this.activeItem;
2108         //    }
2109         //}
2110         this.fireEvent("mouseout", this, e, t);
2111     },
2112     
2113     
2114     /**
2115      * Displays this menu relative to another element
2116      * @param {String/HTMLElement/Roo.Element} element The element to align to
2117      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2118      * the element (defaults to this.defaultAlign)
2119      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2120      */
2121     show : function(el, pos, parentMenu){
2122         this.parentMenu = parentMenu;
2123         if(!this.el){
2124             this.render();
2125         }
2126         this.fireEvent("beforeshow", this);
2127         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2128     },
2129      /**
2130      * Displays this menu at a specific xy position
2131      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2132      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2133      */
2134     showAt : function(xy, parentMenu, /* private: */_e){
2135         this.parentMenu = parentMenu;
2136         if(!this.el){
2137             this.render();
2138         }
2139         if(_e !== false){
2140             this.fireEvent("beforeshow", this);
2141             //xy = this.el.adjustForConstraints(xy);
2142         }
2143         
2144         //this.el.show();
2145         this.hideMenuItems();
2146         this.hidden = false;
2147         this.triggerEl.addClass('open');
2148         
2149         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2150             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2151         }
2152         
2153         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2154             this.el.setXY(xy);
2155         }
2156         
2157         this.focus();
2158         this.fireEvent("show", this);
2159     },
2160     
2161     focus : function(){
2162         return;
2163         if(!this.hidden){
2164             this.doFocus.defer(50, this);
2165         }
2166     },
2167
2168     doFocus : function(){
2169         if(!this.hidden){
2170             this.focusEl.focus();
2171         }
2172     },
2173
2174     /**
2175      * Hides this menu and optionally all parent menus
2176      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2177      */
2178     hide : function(deep)
2179     {
2180         
2181         this.hideMenuItems();
2182         if(this.el && this.isVisible()){
2183             this.fireEvent("beforehide", this);
2184             if(this.activeItem){
2185                 this.activeItem.deactivate();
2186                 this.activeItem = null;
2187             }
2188             this.triggerEl.removeClass('open');;
2189             this.hidden = true;
2190             this.fireEvent("hide", this);
2191         }
2192         if(deep === true && this.parentMenu){
2193             this.parentMenu.hide(true);
2194         }
2195     },
2196     
2197     onTriggerClick : function(e)
2198     {
2199         Roo.log('trigger click');
2200         
2201         var target = e.getTarget();
2202         
2203         Roo.log(target.nodeName.toLowerCase());
2204         
2205         if(target.nodeName.toLowerCase() === 'i'){
2206             e.preventDefault();
2207         }
2208         
2209     },
2210     
2211     onTriggerPress  : function(e)
2212     {
2213         Roo.log('trigger press');
2214         //Roo.log(e.getTarget());
2215        // Roo.log(this.triggerEl.dom);
2216        
2217         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2218         var pel = Roo.get(e.getTarget());
2219         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2220             Roo.log('is treeview or dropdown?');
2221             return;
2222         }
2223         
2224         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2225             return;
2226         }
2227         
2228         if (this.isVisible()) {
2229             Roo.log('hide');
2230             this.hide();
2231         } else {
2232             Roo.log('show');
2233             this.show(this.triggerEl, false, false);
2234         }
2235         
2236         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2237             e.stopEvent();
2238         }
2239         
2240     },
2241        
2242     
2243     hideMenuItems : function()
2244     {
2245         Roo.log("hide Menu Items");
2246         if (!this.el) { 
2247             return;
2248         }
2249         //$(backdrop).remove()
2250         this.el.select('.open',true).each(function(aa) {
2251             
2252             aa.removeClass('open');
2253           //var parent = getParent($(this))
2254           //var relatedTarget = { relatedTarget: this }
2255           
2256            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2257           //if (e.isDefaultPrevented()) return
2258            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2259         });
2260     },
2261     addxtypeChild : function (tree, cntr) {
2262         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2263           
2264         this.menuitems.add(comp);
2265         return comp;
2266
2267     },
2268     getEl : function()
2269     {
2270         Roo.log(this.el);
2271         return this.el;
2272     }
2273 });
2274
2275  
2276  /*
2277  * - LGPL
2278  *
2279  * menu item
2280  * 
2281  */
2282
2283
2284 /**
2285  * @class Roo.bootstrap.MenuItem
2286  * @extends Roo.bootstrap.Component
2287  * Bootstrap MenuItem class
2288  * @cfg {String} html the menu label
2289  * @cfg {String} href the link
2290  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2291  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2292  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2293  * @cfg {String} fa favicon to show on left of menu item.
2294  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2295  * 
2296  * 
2297  * @constructor
2298  * Create a new MenuItem
2299  * @param {Object} config The config object
2300  */
2301
2302
2303 Roo.bootstrap.MenuItem = function(config){
2304     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2305     this.addEvents({
2306         // raw events
2307         /**
2308          * @event click
2309          * The raw click event for the entire grid.
2310          * @param {Roo.bootstrap.MenuItem} this
2311          * @param {Roo.EventObject} e
2312          */
2313         "click" : true
2314     });
2315 };
2316
2317 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2318     
2319     href : false,
2320     html : false,
2321     preventDefault: true,
2322     isContainer : false,
2323     active : false,
2324     fa: false,
2325     
2326     getAutoCreate : function(){
2327         
2328         if(this.isContainer){
2329             return {
2330                 tag: 'li',
2331                 cls: 'dropdown-menu-item'
2332             };
2333         }
2334         var ctag = {
2335             tag: 'span',
2336             html: 'Link'
2337         };
2338         
2339         var anc = {
2340             tag : 'a',
2341             href : '#',
2342             cn : [  ]
2343         };
2344         
2345         if (this.fa !== false) {
2346             anc.cn.push({
2347                 tag : 'i',
2348                 cls : 'fa fa-' + this.fa
2349             });
2350         }
2351         
2352         anc.cn.push(ctag);
2353         
2354         
2355         var cfg= {
2356             tag: 'li',
2357             cls: 'dropdown-menu-item',
2358             cn: [ anc ]
2359         };
2360         if (this.parent().type == 'treeview') {
2361             cfg.cls = 'treeview-menu';
2362         }
2363         if (this.active) {
2364             cfg.cls += ' active';
2365         }
2366         
2367         
2368         
2369         anc.href = this.href || cfg.cn[0].href ;
2370         ctag.html = this.html || cfg.cn[0].html ;
2371         return cfg;
2372     },
2373     
2374     initEvents: function()
2375     {
2376         if (this.parent().type == 'treeview') {
2377             this.el.select('a').on('click', this.onClick, this);
2378         }
2379         if (this.menu) {
2380             this.menu.parentType = this.xtype;
2381             this.menu.triggerEl = this.el;
2382             this.menu = this.addxtype(Roo.apply({}, this.menu));
2383         }
2384         
2385     },
2386     onClick : function(e)
2387     {
2388         Roo.log('item on click ');
2389         //if(this.preventDefault){
2390         //    e.preventDefault();
2391         //}
2392         //this.parent().hideMenuItems();
2393         
2394         this.fireEvent('click', this, e);
2395     },
2396     getEl : function()
2397     {
2398         return this.el;
2399     } 
2400 });
2401
2402  
2403
2404  /*
2405  * - LGPL
2406  *
2407  * menu separator
2408  * 
2409  */
2410
2411
2412 /**
2413  * @class Roo.bootstrap.MenuSeparator
2414  * @extends Roo.bootstrap.Component
2415  * Bootstrap MenuSeparator class
2416  * 
2417  * @constructor
2418  * Create a new MenuItem
2419  * @param {Object} config The config object
2420  */
2421
2422
2423 Roo.bootstrap.MenuSeparator = function(config){
2424     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2428     
2429     getAutoCreate : function(){
2430         var cfg = {
2431             cls: 'divider',
2432             tag : 'li'
2433         };
2434         
2435         return cfg;
2436     }
2437    
2438 });
2439
2440  
2441
2442  
2443 /*
2444 * Licence: LGPL
2445 */
2446
2447 /**
2448  * @class Roo.bootstrap.Modal
2449  * @extends Roo.bootstrap.Component
2450  * Bootstrap Modal class
2451  * @cfg {String} title Title of dialog
2452  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2453  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2454  * @cfg {Boolean} specificTitle default false
2455  * @cfg {Array} buttons Array of buttons or standard button set..
2456  * @cfg {String} buttonPosition (left|right|center) default right
2457  * @cfg {Boolean} animate default true
2458  * @cfg {Boolean} allow_close default true
2459  * @cfg {Boolean} fitwindow default true
2460  * @cfg {String} size (sm|lg) default empty
2461  * 
2462  * 
2463  * @constructor
2464  * Create a new Modal Dialog
2465  * @param {Object} config The config object
2466  */
2467
2468 Roo.bootstrap.Modal = function(config){
2469     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2470     this.addEvents({
2471         // raw events
2472         /**
2473          * @event btnclick
2474          * The raw btnclick event for the button
2475          * @param {Roo.EventObject} e
2476          */
2477         "btnclick" : true
2478     });
2479     this.buttons = this.buttons || [];
2480      
2481     if (this.tmpl) {
2482         this.tmpl = Roo.factory(this.tmpl);
2483     }
2484     
2485 };
2486
2487 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2488     
2489     title : 'test dialog',
2490    
2491     buttons : false,
2492     
2493     // set on load...
2494      
2495     html: false,
2496     
2497     tmp: false,
2498     
2499     specificTitle: false,
2500     
2501     buttonPosition: 'right',
2502     
2503     allow_close : true,
2504     
2505     animate : true,
2506     
2507     fitwindow: false,
2508     
2509     
2510      // private
2511     dialogEl: false,
2512     bodyEl:  false,
2513     footerEl:  false,
2514     titleEl:  false,
2515     closeEl:  false,
2516     
2517     size: '',
2518     
2519     
2520     onRender : function(ct, position)
2521     {
2522         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2523      
2524         if(!this.el){
2525             var cfg = Roo.apply({},  this.getAutoCreate());
2526             cfg.id = Roo.id();
2527             //if(!cfg.name){
2528             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2529             //}
2530             //if (!cfg.name.length) {
2531             //    delete cfg.name;
2532            // }
2533             if (this.cls) {
2534                 cfg.cls += ' ' + this.cls;
2535             }
2536             if (this.style) {
2537                 cfg.style = this.style;
2538             }
2539             this.el = Roo.get(document.body).createChild(cfg, position);
2540         }
2541         //var type = this.el.dom.type;
2542         
2543         
2544         if(this.tabIndex !== undefined){
2545             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2546         }
2547         
2548         this.dialogEl = this.el.select('.modal-dialog',true).first();
2549         this.bodyEl = this.el.select('.modal-body',true).first();
2550         this.closeEl = this.el.select('.modal-header .close', true).first();
2551         this.footerEl = this.el.select('.modal-footer',true).first();
2552         this.titleEl = this.el.select('.modal-title',true).first();
2553         
2554         
2555          
2556         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2557         this.maskEl.enableDisplayMode("block");
2558         this.maskEl.hide();
2559         //this.el.addClass("x-dlg-modal");
2560     
2561         if (this.buttons.length) {
2562             Roo.each(this.buttons, function(bb) {
2563                 var b = Roo.apply({}, bb);
2564                 b.xns = b.xns || Roo.bootstrap;
2565                 b.xtype = b.xtype || 'Button';
2566                 if (typeof(b.listeners) == 'undefined') {
2567                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2568                 }
2569                 
2570                 var btn = Roo.factory(b);
2571                 
2572                 btn.render(this.el.select('.modal-footer div').first());
2573                 
2574             },this);
2575         }
2576         // render the children.
2577         var nitems = [];
2578         
2579         if(typeof(this.items) != 'undefined'){
2580             var items = this.items;
2581             delete this.items;
2582
2583             for(var i =0;i < items.length;i++) {
2584                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2585             }
2586         }
2587         
2588         this.items = nitems;
2589         
2590         // where are these used - they used to be body/close/footer
2591         
2592        
2593         this.initEvents();
2594         //this.el.addClass([this.fieldClass, this.cls]);
2595         
2596     },
2597     
2598     getAutoCreate : function(){
2599         
2600         
2601         var bdy = {
2602                 cls : 'modal-body',
2603                 html : this.html || ''
2604         };
2605         
2606         var title = {
2607             tag: 'h4',
2608             cls : 'modal-title',
2609             html : this.title
2610         };
2611         
2612         if(this.specificTitle){
2613             title = this.title;
2614             
2615         };
2616         
2617         var header = [];
2618         if (this.allow_close) {
2619             header.push({
2620                 tag: 'button',
2621                 cls : 'close',
2622                 html : '&times'
2623             });
2624         }
2625         
2626         header.push(title);
2627         
2628         var size = '';
2629         
2630         if(this.size.length){
2631             size = 'modal-' + this.size;
2632         }
2633         
2634         var modal = {
2635             cls: "modal",
2636             style : 'display: none',
2637             cn : [
2638                 {
2639                     cls: "modal-dialog " + size,
2640                     cn : [
2641                         {
2642                             cls : "modal-content",
2643                             cn : [
2644                                 {
2645                                     cls : 'modal-header',
2646                                     cn : header
2647                                 },
2648                                 bdy,
2649                                 {
2650                                     cls : 'modal-footer',
2651                                     cn : [
2652                                         {
2653                                             tag: 'div',
2654                                             cls: 'btn-' + this.buttonPosition
2655                                         }
2656                                     ]
2657                                     
2658                                 }
2659                                 
2660                                 
2661                             ]
2662                             
2663                         }
2664                     ]
2665                         
2666                 }
2667             ]
2668         };
2669         
2670         if(this.animate){
2671             modal.cls += ' fade';
2672         }
2673         
2674         return modal;
2675           
2676     },
2677     getChildContainer : function() {
2678          
2679          return this.bodyEl;
2680         
2681     },
2682     getButtonContainer : function() {
2683          return this.el.select('.modal-footer div',true).first();
2684         
2685     },
2686     initEvents : function()
2687     {
2688         if (this.allow_close) {
2689             this.closeEl.on('click', this.hide, this);
2690         }
2691         Roo.EventManager.onWindowResize(this.resize, this, true);
2692         
2693  
2694     },
2695     
2696     resize : function()
2697     {
2698         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2699         if (this.fitwindow) {
2700             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2701             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 30;
2702             this.setSize(w,h)
2703         }
2704     },
2705     
2706     setSize : function(w,h)
2707     {
2708         if (!w && !h) {
2709             return;
2710         }
2711         this.resizeTo(w,h);
2712     },
2713     
2714     show : function() {
2715         
2716         if (!this.rendered) {
2717             this.render();
2718         }
2719         
2720         this.el.setStyle('display', 'block');
2721         
2722         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2723             var _this = this;
2724             (function(){
2725                 this.el.addClass('in');
2726             }).defer(50, this);
2727         }else{
2728             this.el.addClass('in');
2729             
2730         }
2731         
2732         // not sure how we can show data in here.. 
2733         //if (this.tmpl) {
2734         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2735         //}
2736         
2737         Roo.get(document.body).addClass("x-body-masked");
2738         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2739         this.maskEl.show();
2740         this.el.setStyle('zIndex', '10001');
2741        
2742         this.fireEvent('show', this);
2743         this.items.forEach(function(e) {
2744             e.layout ? e.layout() : false;
2745                 
2746         });
2747         this.resize();
2748         
2749         
2750         
2751     },
2752     hide : function()
2753     {
2754         this.maskEl.hide();
2755         Roo.get(document.body).removeClass("x-body-masked");
2756         this.el.removeClass('in');
2757         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2758         
2759         if(this.animate){ // why
2760             var _this = this;
2761             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2762         }else{
2763             this.el.setStyle('display', 'none');
2764         }
2765         
2766         this.fireEvent('hide', this);
2767     },
2768     
2769     addButton : function(str, cb)
2770     {
2771          
2772         
2773         var b = Roo.apply({}, { html : str } );
2774         b.xns = b.xns || Roo.bootstrap;
2775         b.xtype = b.xtype || 'Button';
2776         if (typeof(b.listeners) == 'undefined') {
2777             b.listeners = { click : cb.createDelegate(this)  };
2778         }
2779         
2780         var btn = Roo.factory(b);
2781            
2782         btn.render(this.el.select('.modal-footer div').first());
2783         
2784         return btn;   
2785        
2786     },
2787     
2788     setDefaultButton : function(btn)
2789     {
2790         //this.el.select('.modal-footer').()
2791     },
2792     diff : false,
2793     
2794     resizeTo: function(w,h)
2795     {
2796         // skip.. ?? why??
2797         
2798         this.dialogEl.setWidth(w);
2799         if (this.diff === false) {
2800             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2801         }
2802         
2803         this.bodyEl.setHeight(h-this.diff);
2804         
2805         
2806     },
2807     setContentSize  : function(w, h)
2808     {
2809         
2810     },
2811     onButtonClick: function(btn,e)
2812     {
2813         //Roo.log([a,b,c]);
2814         this.fireEvent('btnclick', btn.name, e);
2815     },
2816      /**
2817      * Set the title of the Dialog
2818      * @param {String} str new Title
2819      */
2820     setTitle: function(str) {
2821         this.titleEl.dom.innerHTML = str;    
2822     },
2823     /**
2824      * Set the body of the Dialog
2825      * @param {String} str new Title
2826      */
2827     setBody: function(str) {
2828         this.bodyEl.dom.innerHTML = str;    
2829     },
2830     /**
2831      * Set the body of the Dialog using the template
2832      * @param {Obj} data - apply this data to the template and replace the body contents.
2833      */
2834     applyBody: function(obj)
2835     {
2836         if (!this.tmpl) {
2837             Roo.log("Error - using apply Body without a template");
2838             //code
2839         }
2840         this.tmpl.overwrite(this.bodyEl, obj);
2841     }
2842     
2843 });
2844
2845
2846 Roo.apply(Roo.bootstrap.Modal,  {
2847     /**
2848          * Button config that displays a single OK button
2849          * @type Object
2850          */
2851         OK :  [{
2852             name : 'ok',
2853             weight : 'primary',
2854             html : 'OK'
2855         }], 
2856         /**
2857          * Button config that displays Yes and No buttons
2858          * @type Object
2859          */
2860         YESNO : [
2861             {
2862                 name  : 'no',
2863                 html : 'No'
2864             },
2865             {
2866                 name  :'yes',
2867                 weight : 'primary',
2868                 html : 'Yes'
2869             }
2870         ],
2871         
2872         /**
2873          * Button config that displays OK and Cancel buttons
2874          * @type Object
2875          */
2876         OKCANCEL : [
2877             {
2878                name : 'cancel',
2879                 html : 'Cancel'
2880             },
2881             {
2882                 name : 'ok',
2883                 weight : 'primary',
2884                 html : 'OK'
2885             }
2886         ],
2887         /**
2888          * Button config that displays Yes, No and Cancel buttons
2889          * @type Object
2890          */
2891         YESNOCANCEL : [
2892             {
2893                 name : 'yes',
2894                 weight : 'primary',
2895                 html : 'Yes'
2896             },
2897             {
2898                 name : 'no',
2899                 html : 'No'
2900             },
2901             {
2902                 name : 'cancel',
2903                 html : 'Cancel'
2904             }
2905         ]
2906 });
2907  
2908  /*
2909  * - LGPL
2910  *
2911  * messagebox - can be used as a replace
2912  * 
2913  */
2914 /**
2915  * @class Roo.MessageBox
2916  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2917  * Example usage:
2918  *<pre><code>
2919 // Basic alert:
2920 Roo.Msg.alert('Status', 'Changes saved successfully.');
2921
2922 // Prompt for user data:
2923 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2924     if (btn == 'ok'){
2925         // process text value...
2926     }
2927 });
2928
2929 // Show a dialog using config options:
2930 Roo.Msg.show({
2931    title:'Save Changes?',
2932    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2933    buttons: Roo.Msg.YESNOCANCEL,
2934    fn: processResult,
2935    animEl: 'elId'
2936 });
2937 </code></pre>
2938  * @singleton
2939  */
2940 Roo.bootstrap.MessageBox = function(){
2941     var dlg, opt, mask, waitTimer;
2942     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2943     var buttons, activeTextEl, bwidth;
2944
2945     
2946     // private
2947     var handleButton = function(button){
2948         dlg.hide();
2949         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2950     };
2951
2952     // private
2953     var handleHide = function(){
2954         if(opt && opt.cls){
2955             dlg.el.removeClass(opt.cls);
2956         }
2957         //if(waitTimer){
2958         //    Roo.TaskMgr.stop(waitTimer);
2959         //    waitTimer = null;
2960         //}
2961     };
2962
2963     // private
2964     var updateButtons = function(b){
2965         var width = 0;
2966         if(!b){
2967             buttons["ok"].hide();
2968             buttons["cancel"].hide();
2969             buttons["yes"].hide();
2970             buttons["no"].hide();
2971             //dlg.footer.dom.style.display = 'none';
2972             return width;
2973         }
2974         dlg.footerEl.dom.style.display = '';
2975         for(var k in buttons){
2976             if(typeof buttons[k] != "function"){
2977                 if(b[k]){
2978                     buttons[k].show();
2979                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2980                     width += buttons[k].el.getWidth()+15;
2981                 }else{
2982                     buttons[k].hide();
2983                 }
2984             }
2985         }
2986         return width;
2987     };
2988
2989     // private
2990     var handleEsc = function(d, k, e){
2991         if(opt && opt.closable !== false){
2992             dlg.hide();
2993         }
2994         if(e){
2995             e.stopEvent();
2996         }
2997     };
2998
2999     return {
3000         /**
3001          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3002          * @return {Roo.BasicDialog} The BasicDialog element
3003          */
3004         getDialog : function(){
3005            if(!dlg){
3006                 dlg = new Roo.bootstrap.Modal( {
3007                     //draggable: true,
3008                     //resizable:false,
3009                     //constraintoviewport:false,
3010                     //fixedcenter:true,
3011                     //collapsible : false,
3012                     //shim:true,
3013                     //modal: true,
3014                   //  width:400,
3015                   //  height:100,
3016                     //buttonAlign:"center",
3017                     closeClick : function(){
3018                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3019                             handleButton("no");
3020                         }else{
3021                             handleButton("cancel");
3022                         }
3023                     }
3024                 });
3025                 dlg.render();
3026                 dlg.on("hide", handleHide);
3027                 mask = dlg.mask;
3028                 //dlg.addKeyListener(27, handleEsc);
3029                 buttons = {};
3030                 this.buttons = buttons;
3031                 var bt = this.buttonText;
3032                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3033                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3034                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3035                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3036                 //Roo.log(buttons);
3037                 bodyEl = dlg.bodyEl.createChild({
3038
3039                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3040                         '<textarea class="roo-mb-textarea"></textarea>' +
3041                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3042                 });
3043                 msgEl = bodyEl.dom.firstChild;
3044                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3045                 textboxEl.enableDisplayMode();
3046                 textboxEl.addKeyListener([10,13], function(){
3047                     if(dlg.isVisible() && opt && opt.buttons){
3048                         if(opt.buttons.ok){
3049                             handleButton("ok");
3050                         }else if(opt.buttons.yes){
3051                             handleButton("yes");
3052                         }
3053                     }
3054                 });
3055                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3056                 textareaEl.enableDisplayMode();
3057                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3058                 progressEl.enableDisplayMode();
3059                 var pf = progressEl.dom.firstChild;
3060                 if (pf) {
3061                     pp = Roo.get(pf.firstChild);
3062                     pp.setHeight(pf.offsetHeight);
3063                 }
3064                 
3065             }
3066             return dlg;
3067         },
3068
3069         /**
3070          * Updates the message box body text
3071          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3072          * the XHTML-compliant non-breaking space character '&amp;#160;')
3073          * @return {Roo.MessageBox} This message box
3074          */
3075         updateText : function(text){
3076             if(!dlg.isVisible() && !opt.width){
3077                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3078             }
3079             msgEl.innerHTML = text || '&#160;';
3080       
3081             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3082             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3083             var w = Math.max(
3084                     Math.min(opt.width || cw , this.maxWidth), 
3085                     Math.max(opt.minWidth || this.minWidth, bwidth)
3086             );
3087             if(opt.prompt){
3088                 activeTextEl.setWidth(w);
3089             }
3090             if(dlg.isVisible()){
3091                 dlg.fixedcenter = false;
3092             }
3093             // to big, make it scroll. = But as usual stupid IE does not support
3094             // !important..
3095             
3096             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3097                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3098                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3099             } else {
3100                 bodyEl.dom.style.height = '';
3101                 bodyEl.dom.style.overflowY = '';
3102             }
3103             if (cw > w) {
3104                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3105             } else {
3106                 bodyEl.dom.style.overflowX = '';
3107             }
3108             
3109             dlg.setContentSize(w, bodyEl.getHeight());
3110             if(dlg.isVisible()){
3111                 dlg.fixedcenter = true;
3112             }
3113             return this;
3114         },
3115
3116         /**
3117          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3118          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3119          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3120          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3121          * @return {Roo.MessageBox} This message box
3122          */
3123         updateProgress : function(value, text){
3124             if(text){
3125                 this.updateText(text);
3126             }
3127             if (pp) { // weird bug on my firefox - for some reason this is not defined
3128                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3129             }
3130             return this;
3131         },        
3132
3133         /**
3134          * Returns true if the message box is currently displayed
3135          * @return {Boolean} True if the message box is visible, else false
3136          */
3137         isVisible : function(){
3138             return dlg && dlg.isVisible();  
3139         },
3140
3141         /**
3142          * Hides the message box if it is displayed
3143          */
3144         hide : function(){
3145             if(this.isVisible()){
3146                 dlg.hide();
3147             }  
3148         },
3149
3150         /**
3151          * Displays a new message box, or reinitializes an existing message box, based on the config options
3152          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3153          * The following config object properties are supported:
3154          * <pre>
3155 Property    Type             Description
3156 ----------  ---------------  ------------------------------------------------------------------------------------
3157 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3158                                    closes (defaults to undefined)
3159 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3160                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3161 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3162                                    progress and wait dialogs will ignore this property and always hide the
3163                                    close button as they can only be closed programmatically.
3164 cls               String           A custom CSS class to apply to the message box element
3165 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3166                                    displayed (defaults to 75)
3167 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3168                                    function will be btn (the name of the button that was clicked, if applicable,
3169                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3170                                    Progress and wait dialogs will ignore this option since they do not respond to
3171                                    user actions and can only be closed programmatically, so any required function
3172                                    should be called by the same code after it closes the dialog.
3173 icon              String           A CSS class that provides a background image to be used as an icon for
3174                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3175 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3176 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3177 modal             Boolean          False to allow user interaction with the page while the message box is
3178                                    displayed (defaults to true)
3179 msg               String           A string that will replace the existing message box body text (defaults
3180                                    to the XHTML-compliant non-breaking space character '&#160;')
3181 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3182 progress          Boolean          True to display a progress bar (defaults to false)
3183 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3184 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3185 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3186 title             String           The title text
3187 value             String           The string value to set into the active textbox element if displayed
3188 wait              Boolean          True to display a progress bar (defaults to false)
3189 width             Number           The width of the dialog in pixels
3190 </pre>
3191          *
3192          * Example usage:
3193          * <pre><code>
3194 Roo.Msg.show({
3195    title: 'Address',
3196    msg: 'Please enter your address:',
3197    width: 300,
3198    buttons: Roo.MessageBox.OKCANCEL,
3199    multiline: true,
3200    fn: saveAddress,
3201    animEl: 'addAddressBtn'
3202 });
3203 </code></pre>
3204          * @param {Object} config Configuration options
3205          * @return {Roo.MessageBox} This message box
3206          */
3207         show : function(options)
3208         {
3209             
3210             // this causes nightmares if you show one dialog after another
3211             // especially on callbacks..
3212              
3213             if(this.isVisible()){
3214                 
3215                 this.hide();
3216                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3217                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3218                 Roo.log("New Dialog Message:" +  options.msg )
3219                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3220                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3221                 
3222             }
3223             var d = this.getDialog();
3224             opt = options;
3225             d.setTitle(opt.title || "&#160;");
3226             d.closeEl.setDisplayed(opt.closable !== false);
3227             activeTextEl = textboxEl;
3228             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3229             if(opt.prompt){
3230                 if(opt.multiline){
3231                     textboxEl.hide();
3232                     textareaEl.show();
3233                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3234                         opt.multiline : this.defaultTextHeight);
3235                     activeTextEl = textareaEl;
3236                 }else{
3237                     textboxEl.show();
3238                     textareaEl.hide();
3239                 }
3240             }else{
3241                 textboxEl.hide();
3242                 textareaEl.hide();
3243             }
3244             progressEl.setDisplayed(opt.progress === true);
3245             this.updateProgress(0);
3246             activeTextEl.dom.value = opt.value || "";
3247             if(opt.prompt){
3248                 dlg.setDefaultButton(activeTextEl);
3249             }else{
3250                 var bs = opt.buttons;
3251                 var db = null;
3252                 if(bs && bs.ok){
3253                     db = buttons["ok"];
3254                 }else if(bs && bs.yes){
3255                     db = buttons["yes"];
3256                 }
3257                 dlg.setDefaultButton(db);
3258             }
3259             bwidth = updateButtons(opt.buttons);
3260             this.updateText(opt.msg);
3261             if(opt.cls){
3262                 d.el.addClass(opt.cls);
3263             }
3264             d.proxyDrag = opt.proxyDrag === true;
3265             d.modal = opt.modal !== false;
3266             d.mask = opt.modal !== false ? mask : false;
3267             if(!d.isVisible()){
3268                 // force it to the end of the z-index stack so it gets a cursor in FF
3269                 document.body.appendChild(dlg.el.dom);
3270                 d.animateTarget = null;
3271                 d.show(options.animEl);
3272             }
3273             return this;
3274         },
3275
3276         /**
3277          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3278          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3279          * and closing the message box when the process is complete.
3280          * @param {String} title The title bar text
3281          * @param {String} msg The message box body text
3282          * @return {Roo.MessageBox} This message box
3283          */
3284         progress : function(title, msg){
3285             this.show({
3286                 title : title,
3287                 msg : msg,
3288                 buttons: false,
3289                 progress:true,
3290                 closable:false,
3291                 minWidth: this.minProgressWidth,
3292                 modal : true
3293             });
3294             return this;
3295         },
3296
3297         /**
3298          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3299          * If a callback function is passed it will be called after the user clicks the button, and the
3300          * id of the button that was clicked will be passed as the only parameter to the callback
3301          * (could also be the top-right close button).
3302          * @param {String} title The title bar text
3303          * @param {String} msg The message box body text
3304          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3305          * @param {Object} scope (optional) The scope of the callback function
3306          * @return {Roo.MessageBox} This message box
3307          */
3308         alert : function(title, msg, fn, scope){
3309             this.show({
3310                 title : title,
3311                 msg : msg,
3312                 buttons: this.OK,
3313                 fn: fn,
3314                 scope : scope,
3315                 modal : true
3316             });
3317             return this;
3318         },
3319
3320         /**
3321          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3322          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3323          * You are responsible for closing the message box when the process is complete.
3324          * @param {String} msg The message box body text
3325          * @param {String} title (optional) The title bar text
3326          * @return {Roo.MessageBox} This message box
3327          */
3328         wait : function(msg, title){
3329             this.show({
3330                 title : title,
3331                 msg : msg,
3332                 buttons: false,
3333                 closable:false,
3334                 progress:true,
3335                 modal:true,
3336                 width:300,
3337                 wait:true
3338             });
3339             waitTimer = Roo.TaskMgr.start({
3340                 run: function(i){
3341                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3342                 },
3343                 interval: 1000
3344             });
3345             return this;
3346         },
3347
3348         /**
3349          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3350          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3351          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3352          * @param {String} title The title bar text
3353          * @param {String} msg The message box body text
3354          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3355          * @param {Object} scope (optional) The scope of the callback function
3356          * @return {Roo.MessageBox} This message box
3357          */
3358         confirm : function(title, msg, fn, scope){
3359             this.show({
3360                 title : title,
3361                 msg : msg,
3362                 buttons: this.YESNO,
3363                 fn: fn,
3364                 scope : scope,
3365                 modal : true
3366             });
3367             return this;
3368         },
3369
3370         /**
3371          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3372          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3373          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3374          * (could also be the top-right close button) and the text that was entered will be passed as the two
3375          * parameters to the callback.
3376          * @param {String} title The title bar text
3377          * @param {String} msg The message box body text
3378          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3379          * @param {Object} scope (optional) The scope of the callback function
3380          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3381          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3382          * @return {Roo.MessageBox} This message box
3383          */
3384         prompt : function(title, msg, fn, scope, multiline){
3385             this.show({
3386                 title : title,
3387                 msg : msg,
3388                 buttons: this.OKCANCEL,
3389                 fn: fn,
3390                 minWidth:250,
3391                 scope : scope,
3392                 prompt:true,
3393                 multiline: multiline,
3394                 modal : true
3395             });
3396             return this;
3397         },
3398
3399         /**
3400          * Button config that displays a single OK button
3401          * @type Object
3402          */
3403         OK : {ok:true},
3404         /**
3405          * Button config that displays Yes and No buttons
3406          * @type Object
3407          */
3408         YESNO : {yes:true, no:true},
3409         /**
3410          * Button config that displays OK and Cancel buttons
3411          * @type Object
3412          */
3413         OKCANCEL : {ok:true, cancel:true},
3414         /**
3415          * Button config that displays Yes, No and Cancel buttons
3416          * @type Object
3417          */
3418         YESNOCANCEL : {yes:true, no:true, cancel:true},
3419
3420         /**
3421          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3422          * @type Number
3423          */
3424         defaultTextHeight : 75,
3425         /**
3426          * The maximum width in pixels of the message box (defaults to 600)
3427          * @type Number
3428          */
3429         maxWidth : 600,
3430         /**
3431          * The minimum width in pixels of the message box (defaults to 100)
3432          * @type Number
3433          */
3434         minWidth : 100,
3435         /**
3436          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3437          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3438          * @type Number
3439          */
3440         minProgressWidth : 250,
3441         /**
3442          * An object containing the default button text strings that can be overriden for localized language support.
3443          * Supported properties are: ok, cancel, yes and no.
3444          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3445          * @type Object
3446          */
3447         buttonText : {
3448             ok : "OK",
3449             cancel : "Cancel",
3450             yes : "Yes",
3451             no : "No"
3452         }
3453     };
3454 }();
3455
3456 /**
3457  * Shorthand for {@link Roo.MessageBox}
3458  */
3459 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3460 Roo.Msg = Roo.Msg || Roo.MessageBox;
3461 /*
3462  * - LGPL
3463  *
3464  * navbar
3465  * 
3466  */
3467
3468 /**
3469  * @class Roo.bootstrap.Navbar
3470  * @extends Roo.bootstrap.Component
3471  * Bootstrap Navbar class
3472
3473  * @constructor
3474  * Create a new Navbar
3475  * @param {Object} config The config object
3476  */
3477
3478
3479 Roo.bootstrap.Navbar = function(config){
3480     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3481     this.addEvents({
3482         // raw events
3483         /**
3484          * @event beforetoggle
3485          * Fire before toggle the menu
3486          * @param {Roo.EventObject} e
3487          */
3488         "beforetoggle" : true
3489     });
3490 };
3491
3492 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3493     
3494     
3495    
3496     // private
3497     navItems : false,
3498     loadMask : false,
3499     
3500     
3501     getAutoCreate : function(){
3502         
3503         
3504         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3505         
3506     },
3507     
3508     initEvents :function ()
3509     {
3510         //Roo.log(this.el.select('.navbar-toggle',true));
3511         this.el.select('.navbar-toggle',true).on('click', function() {
3512             if(this.fireEvent('beforetoggle', this) !== false){
3513                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3514             }
3515             
3516         }, this);
3517         
3518         var mark = {
3519             tag: "div",
3520             cls:"x-dlg-mask"
3521         };
3522         
3523         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3524         
3525         var size = this.el.getSize();
3526         this.maskEl.setSize(size.width, size.height);
3527         this.maskEl.enableDisplayMode("block");
3528         this.maskEl.hide();
3529         
3530         if(this.loadMask){
3531             this.maskEl.show();
3532         }
3533     },
3534     
3535     
3536     getChildContainer : function()
3537     {
3538         if (this.el.select('.collapse').getCount()) {
3539             return this.el.select('.collapse',true).first();
3540         }
3541         
3542         return this.el;
3543     },
3544     
3545     mask : function()
3546     {
3547         this.maskEl.show();
3548     },
3549     
3550     unmask : function()
3551     {
3552         this.maskEl.hide();
3553     } 
3554     
3555     
3556     
3557     
3558 });
3559
3560
3561
3562  
3563
3564  /*
3565  * - LGPL
3566  *
3567  * navbar
3568  * 
3569  */
3570
3571 /**
3572  * @class Roo.bootstrap.NavSimplebar
3573  * @extends Roo.bootstrap.Navbar
3574  * Bootstrap Sidebar class
3575  *
3576  * @cfg {Boolean} inverse is inverted color
3577  * 
3578  * @cfg {String} type (nav | pills | tabs)
3579  * @cfg {Boolean} arrangement stacked | justified
3580  * @cfg {String} align (left | right) alignment
3581  * 
3582  * @cfg {Boolean} main (true|false) main nav bar? default false
3583  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3584  * 
3585  * @cfg {String} tag (header|footer|nav|div) default is nav 
3586
3587  * 
3588  * 
3589  * 
3590  * @constructor
3591  * Create a new Sidebar
3592  * @param {Object} config The config object
3593  */
3594
3595
3596 Roo.bootstrap.NavSimplebar = function(config){
3597     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3598 };
3599
3600 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3601     
3602     inverse: false,
3603     
3604     type: false,
3605     arrangement: '',
3606     align : false,
3607     
3608     
3609     
3610     main : false,
3611     
3612     
3613     tag : false,
3614     
3615     
3616     getAutoCreate : function(){
3617         
3618         
3619         var cfg = {
3620             tag : this.tag || 'div',
3621             cls : 'navbar'
3622         };
3623           
3624         
3625         cfg.cn = [
3626             {
3627                 cls: 'nav',
3628                 tag : 'ul'
3629             }
3630         ];
3631         
3632          
3633         this.type = this.type || 'nav';
3634         if (['tabs','pills'].indexOf(this.type)!==-1) {
3635             cfg.cn[0].cls += ' nav-' + this.type
3636         
3637         
3638         } else {
3639             if (this.type!=='nav') {
3640                 Roo.log('nav type must be nav/tabs/pills')
3641             }
3642             cfg.cn[0].cls += ' navbar-nav'
3643         }
3644         
3645         
3646         
3647         
3648         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3649             cfg.cn[0].cls += ' nav-' + this.arrangement;
3650         }
3651         
3652         
3653         if (this.align === 'right') {
3654             cfg.cn[0].cls += ' navbar-right';
3655         }
3656         
3657         if (this.inverse) {
3658             cfg.cls += ' navbar-inverse';
3659             
3660         }
3661         
3662         
3663         return cfg;
3664     
3665         
3666     }
3667     
3668     
3669     
3670 });
3671
3672
3673
3674  
3675
3676  
3677        /*
3678  * - LGPL
3679  *
3680  * navbar
3681  * 
3682  */
3683
3684 /**
3685  * @class Roo.bootstrap.NavHeaderbar
3686  * @extends Roo.bootstrap.NavSimplebar
3687  * Bootstrap Sidebar class
3688  *
3689  * @cfg {String} brand what is brand
3690  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3691  * @cfg {String} brand_href href of the brand
3692  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3693  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3694  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3695  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3696  * 
3697  * @constructor
3698  * Create a new Sidebar
3699  * @param {Object} config The config object
3700  */
3701
3702
3703 Roo.bootstrap.NavHeaderbar = function(config){
3704     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3705       
3706 };
3707
3708 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3709     
3710     position: '',
3711     brand: '',
3712     brand_href: false,
3713     srButton : true,
3714     autohide : false,
3715     desktopCenter : false,
3716    
3717     
3718     getAutoCreate : function(){
3719         
3720         var   cfg = {
3721             tag: this.nav || 'nav',
3722             cls: 'navbar',
3723             role: 'navigation',
3724             cn: []
3725         };
3726         
3727         var cn = cfg.cn;
3728         if (this.desktopCenter) {
3729             cn.push({cls : 'container', cn : []});
3730             cn = cn[0].cn;
3731         }
3732         
3733         if(this.srButton){
3734             cn.push({
3735                 tag: 'div',
3736                 cls: 'navbar-header',
3737                 cn: [
3738                     {
3739                         tag: 'button',
3740                         type: 'button',
3741                         cls: 'navbar-toggle',
3742                         'data-toggle': 'collapse',
3743                         cn: [
3744                             {
3745                                 tag: 'span',
3746                                 cls: 'sr-only',
3747                                 html: 'Toggle navigation'
3748                             },
3749                             {
3750                                 tag: 'span',
3751                                 cls: 'icon-bar'
3752                             },
3753                             {
3754                                 tag: 'span',
3755                                 cls: 'icon-bar'
3756                             },
3757                             {
3758                                 tag: 'span',
3759                                 cls: 'icon-bar'
3760                             }
3761                         ]
3762                     }
3763                 ]
3764             });
3765         }
3766         
3767         cn.push({
3768             tag: 'div',
3769             cls: 'collapse navbar-collapse',
3770             cn : []
3771         });
3772         
3773         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3774         
3775         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3776             cfg.cls += ' navbar-' + this.position;
3777             
3778             // tag can override this..
3779             
3780             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3781         }
3782         
3783         if (this.brand !== '') {
3784             cn[0].cn.push({
3785                 tag: 'a',
3786                 href: this.brand_href ? this.brand_href : '#',
3787                 cls: 'navbar-brand',
3788                 cn: [
3789                 this.brand
3790                 ]
3791             });
3792         }
3793         
3794         if(this.main){
3795             cfg.cls += ' main-nav';
3796         }
3797         
3798         
3799         return cfg;
3800
3801         
3802     },
3803     getHeaderChildContainer : function()
3804     {
3805         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3806             return this.el.select('.navbar-header',true).first();
3807         }
3808         
3809         return this.getChildContainer();
3810     },
3811     
3812     
3813     initEvents : function()
3814     {
3815         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3816         
3817         if (this.autohide) {
3818             
3819             var prevScroll = 0;
3820             var ft = this.el;
3821             
3822             Roo.get(document).on('scroll',function(e) {
3823                 var ns = Roo.get(document).getScroll().top;
3824                 var os = prevScroll;
3825                 prevScroll = ns;
3826                 
3827                 if(ns > os){
3828                     ft.removeClass('slideDown');
3829                     ft.addClass('slideUp');
3830                     return;
3831                 }
3832                 ft.removeClass('slideUp');
3833                 ft.addClass('slideDown');
3834                  
3835               
3836           },this);
3837         }
3838     }    
3839     
3840 });
3841
3842
3843
3844  
3845
3846  /*
3847  * - LGPL
3848  *
3849  * navbar
3850  * 
3851  */
3852
3853 /**
3854  * @class Roo.bootstrap.NavSidebar
3855  * @extends Roo.bootstrap.Navbar
3856  * Bootstrap Sidebar class
3857  * 
3858  * @constructor
3859  * Create a new Sidebar
3860  * @param {Object} config The config object
3861  */
3862
3863
3864 Roo.bootstrap.NavSidebar = function(config){
3865     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3866 };
3867
3868 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3869     
3870     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3871     
3872     getAutoCreate : function(){
3873         
3874         
3875         return  {
3876             tag: 'div',
3877             cls: 'sidebar sidebar-nav'
3878         };
3879     
3880         
3881     }
3882     
3883     
3884     
3885 });
3886
3887
3888
3889  
3890
3891  /*
3892  * - LGPL
3893  *
3894  * nav group
3895  * 
3896  */
3897
3898 /**
3899  * @class Roo.bootstrap.NavGroup
3900  * @extends Roo.bootstrap.Component
3901  * Bootstrap NavGroup class
3902  * @cfg {String} align (left|right)
3903  * @cfg {Boolean} inverse
3904  * @cfg {String} type (nav|pills|tab) default nav
3905  * @cfg {String} navId - reference Id for navbar.
3906
3907  * 
3908  * @constructor
3909  * Create a new nav group
3910  * @param {Object} config The config object
3911  */
3912
3913 Roo.bootstrap.NavGroup = function(config){
3914     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3915     this.navItems = [];
3916    
3917     Roo.bootstrap.NavGroup.register(this);
3918      this.addEvents({
3919         /**
3920              * @event changed
3921              * Fires when the active item changes
3922              * @param {Roo.bootstrap.NavGroup} this
3923              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3924              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3925          */
3926         'changed': true
3927      });
3928     
3929 };
3930
3931 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3932     
3933     align: '',
3934     inverse: false,
3935     form: false,
3936     type: 'nav',
3937     navId : '',
3938     // private
3939     
3940     navItems : false, 
3941     
3942     getAutoCreate : function()
3943     {
3944         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3945         
3946         cfg = {
3947             tag : 'ul',
3948             cls: 'nav' 
3949         };
3950         
3951         if (['tabs','pills'].indexOf(this.type)!==-1) {
3952             cfg.cls += ' nav-' + this.type
3953         } else {
3954             if (this.type!=='nav') {
3955                 Roo.log('nav type must be nav/tabs/pills')
3956             }
3957             cfg.cls += ' navbar-nav'
3958         }
3959         
3960         if (this.parent().sidebar) {
3961             cfg = {
3962                 tag: 'ul',
3963                 cls: 'dashboard-menu sidebar-menu'
3964             };
3965             
3966             return cfg;
3967         }
3968         
3969         if (this.form === true) {
3970             cfg = {
3971                 tag: 'form',
3972                 cls: 'navbar-form'
3973             };
3974             
3975             if (this.align === 'right') {
3976                 cfg.cls += ' navbar-right';
3977             } else {
3978                 cfg.cls += ' navbar-left';
3979             }
3980         }
3981         
3982         if (this.align === 'right') {
3983             cfg.cls += ' navbar-right';
3984         }
3985         
3986         if (this.inverse) {
3987             cfg.cls += ' navbar-inverse';
3988             
3989         }
3990         
3991         
3992         return cfg;
3993     },
3994     /**
3995     * sets the active Navigation item
3996     * @param {Roo.bootstrap.NavItem} the new current navitem
3997     */
3998     setActiveItem : function(item)
3999     {
4000         var prev = false;
4001         Roo.each(this.navItems, function(v){
4002             if (v == item) {
4003                 return ;
4004             }
4005             if (v.isActive()) {
4006                 v.setActive(false, true);
4007                 prev = v;
4008                 
4009             }
4010             
4011         });
4012
4013         item.setActive(true, true);
4014         this.fireEvent('changed', this, item, prev);
4015         
4016         
4017     },
4018     /**
4019     * gets the active Navigation item
4020     * @return {Roo.bootstrap.NavItem} the current navitem
4021     */
4022     getActive : function()
4023     {
4024         
4025         var prev = false;
4026         Roo.each(this.navItems, function(v){
4027             
4028             if (v.isActive()) {
4029                 prev = v;
4030                 
4031             }
4032             
4033         });
4034         return prev;
4035     },
4036     
4037     indexOfNav : function()
4038     {
4039         
4040         var prev = false;
4041         Roo.each(this.navItems, function(v,i){
4042             
4043             if (v.isActive()) {
4044                 prev = i;
4045                 
4046             }
4047             
4048         });
4049         return prev;
4050     },
4051     /**
4052     * adds a Navigation item
4053     * @param {Roo.bootstrap.NavItem} the navitem to add
4054     */
4055     addItem : function(cfg)
4056     {
4057         var cn = new Roo.bootstrap.NavItem(cfg);
4058         this.register(cn);
4059         cn.parentId = this.id;
4060         cn.onRender(this.el, null);
4061         return cn;
4062     },
4063     /**
4064     * register a Navigation item
4065     * @param {Roo.bootstrap.NavItem} the navitem to add
4066     */
4067     register : function(item)
4068     {
4069         this.navItems.push( item);
4070         item.navId = this.navId;
4071     
4072     },
4073     
4074     /**
4075     * clear all the Navigation item
4076     */
4077    
4078     clearAll : function()
4079     {
4080         this.navItems = [];
4081         this.el.dom.innerHTML = '';
4082     },
4083     
4084     getNavItem: function(tabId)
4085     {
4086         var ret = false;
4087         Roo.each(this.navItems, function(e) {
4088             if (e.tabId == tabId) {
4089                ret =  e;
4090                return false;
4091             }
4092             return true;
4093             
4094         });
4095         return ret;
4096     },
4097     
4098     setActiveNext : function()
4099     {
4100         var i = this.indexOfNav(this.getActive());
4101         if (i > this.navItems.length) {
4102             return;
4103         }
4104         this.setActiveItem(this.navItems[i+1]);
4105     },
4106     setActivePrev : function()
4107     {
4108         var i = this.indexOfNav(this.getActive());
4109         if (i  < 1) {
4110             return;
4111         }
4112         this.setActiveItem(this.navItems[i-1]);
4113     },
4114     clearWasActive : function(except) {
4115         Roo.each(this.navItems, function(e) {
4116             if (e.tabId != except.tabId && e.was_active) {
4117                e.was_active = false;
4118                return false;
4119             }
4120             return true;
4121             
4122         });
4123     },
4124     getWasActive : function ()
4125     {
4126         var r = false;
4127         Roo.each(this.navItems, function(e) {
4128             if (e.was_active) {
4129                r = e;
4130                return false;
4131             }
4132             return true;
4133             
4134         });
4135         return r;
4136     }
4137     
4138     
4139 });
4140
4141  
4142 Roo.apply(Roo.bootstrap.NavGroup, {
4143     
4144     groups: {},
4145      /**
4146     * register a Navigation Group
4147     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4148     */
4149     register : function(navgrp)
4150     {
4151         this.groups[navgrp.navId] = navgrp;
4152         
4153     },
4154     /**
4155     * fetch a Navigation Group based on the navigation ID
4156     * @param {string} the navgroup to add
4157     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4158     */
4159     get: function(navId) {
4160         if (typeof(this.groups[navId]) == 'undefined') {
4161             return false;
4162             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4163         }
4164         return this.groups[navId] ;
4165     }
4166     
4167     
4168     
4169 });
4170
4171  /*
4172  * - LGPL
4173  *
4174  * row
4175  * 
4176  */
4177
4178 /**
4179  * @class Roo.bootstrap.NavItem
4180  * @extends Roo.bootstrap.Component
4181  * Bootstrap Navbar.NavItem class
4182  * @cfg {String} href  link to
4183  * @cfg {String} html content of button
4184  * @cfg {String} badge text inside badge
4185  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4186  * @cfg {String} glyphicon name of glyphicon
4187  * @cfg {String} icon name of font awesome icon
4188  * @cfg {Boolean} active Is item active
4189  * @cfg {Boolean} disabled Is item disabled
4190  
4191  * @cfg {Boolean} preventDefault (true | false) default false
4192  * @cfg {String} tabId the tab that this item activates.
4193  * @cfg {String} tagtype (a|span) render as a href or span?
4194  * @cfg {Boolean} animateRef (true|false) link to element default false  
4195   
4196  * @constructor
4197  * Create a new Navbar Item
4198  * @param {Object} config The config object
4199  */
4200 Roo.bootstrap.NavItem = function(config){
4201     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4202     this.addEvents({
4203         // raw events
4204         /**
4205          * @event click
4206          * The raw click event for the entire grid.
4207          * @param {Roo.EventObject} e
4208          */
4209         "click" : true,
4210          /**
4211             * @event changed
4212             * Fires when the active item active state changes
4213             * @param {Roo.bootstrap.NavItem} this
4214             * @param {boolean} state the new state
4215              
4216          */
4217         'changed': true,
4218         /**
4219             * @event scrollto
4220             * Fires when scroll to element
4221             * @param {Roo.bootstrap.NavItem} this
4222             * @param {Object} options
4223             * @param {Roo.EventObject} e
4224              
4225          */
4226         'scrollto': true
4227     });
4228    
4229 };
4230
4231 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4232     
4233     href: false,
4234     html: '',
4235     badge: '',
4236     icon: false,
4237     glyphicon: false,
4238     active: false,
4239     preventDefault : false,
4240     tabId : false,
4241     tagtype : 'a',
4242     disabled : false,
4243     animateRef : false,
4244     was_active : false,
4245     
4246     getAutoCreate : function(){
4247          
4248         var cfg = {
4249             tag: 'li',
4250             cls: 'nav-item'
4251             
4252         };
4253         
4254         if (this.active) {
4255             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4256         }
4257         if (this.disabled) {
4258             cfg.cls += ' disabled';
4259         }
4260         
4261         if (this.href || this.html || this.glyphicon || this.icon) {
4262             cfg.cn = [
4263                 {
4264                     tag: this.tagtype,
4265                     href : this.href || "#",
4266                     html: this.html || ''
4267                 }
4268             ];
4269             
4270             if (this.icon) {
4271                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4272             }
4273
4274             if(this.glyphicon) {
4275                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4276             }
4277             
4278             if (this.menu) {
4279                 
4280                 cfg.cn[0].html += " <span class='caret'></span>";
4281              
4282             }
4283             
4284             if (this.badge !== '') {
4285                  
4286                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4287             }
4288         }
4289         
4290         
4291         
4292         return cfg;
4293     },
4294     initEvents: function() 
4295     {
4296         if (typeof (this.menu) != 'undefined') {
4297             this.menu.parentType = this.xtype;
4298             this.menu.triggerEl = this.el;
4299             this.menu = this.addxtype(Roo.apply({}, this.menu));
4300         }
4301         
4302         this.el.select('a',true).on('click', this.onClick, this);
4303         
4304         if(this.tagtype == 'span'){
4305             this.el.select('span',true).on('click', this.onClick, this);
4306         }
4307        
4308         // at this point parent should be available..
4309         this.parent().register(this);
4310     },
4311     
4312     onClick : function(e)
4313     {
4314         if (e.getTarget('.dropdown-menu-item')) {
4315             // did you click on a menu itemm.... - then don't trigger onclick..
4316             return;
4317         }
4318         
4319         if(
4320                 this.preventDefault || 
4321                 this.href == '#' 
4322         ){
4323             Roo.log("NavItem - prevent Default?");
4324             e.preventDefault();
4325         }
4326         
4327         if (this.disabled) {
4328             return;
4329         }
4330         
4331         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4332         if (tg && tg.transition) {
4333             Roo.log("waiting for the transitionend");
4334             return;
4335         }
4336         
4337         
4338         
4339         //Roo.log("fire event clicked");
4340         if(this.fireEvent('click', this, e) === false){
4341             return;
4342         };
4343         
4344         if(this.tagtype == 'span'){
4345             return;
4346         }
4347         
4348         //Roo.log(this.href);
4349         var ael = this.el.select('a',true).first();
4350         //Roo.log(ael);
4351         
4352         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4353             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4354             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4355                 return; // ignore... - it's a 'hash' to another page.
4356             }
4357             Roo.log("NavItem - prevent Default?");
4358             e.preventDefault();
4359             this.scrollToElement(e);
4360         }
4361         
4362         
4363         var p =  this.parent();
4364    
4365         if (['tabs','pills'].indexOf(p.type)!==-1) {
4366             if (typeof(p.setActiveItem) !== 'undefined') {
4367                 p.setActiveItem(this);
4368             }
4369         }
4370         
4371         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4372         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4373             // remove the collapsed menu expand...
4374             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4375         }
4376     },
4377     
4378     isActive: function () {
4379         return this.active
4380     },
4381     setActive : function(state, fire, is_was_active)
4382     {
4383         if (this.active && !state && this.navId) {
4384             this.was_active = true;
4385             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4386             if (nv) {
4387                 nv.clearWasActive(this);
4388             }
4389             
4390         }
4391         this.active = state;
4392         
4393         if (!state ) {
4394             this.el.removeClass('active');
4395         } else if (!this.el.hasClass('active')) {
4396             this.el.addClass('active');
4397         }
4398         if (fire) {
4399             this.fireEvent('changed', this, state);
4400         }
4401         
4402         // show a panel if it's registered and related..
4403         
4404         if (!this.navId || !this.tabId || !state || is_was_active) {
4405             return;
4406         }
4407         
4408         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4409         if (!tg) {
4410             return;
4411         }
4412         var pan = tg.getPanelByName(this.tabId);
4413         if (!pan) {
4414             return;
4415         }
4416         // if we can not flip to new panel - go back to old nav highlight..
4417         if (false == tg.showPanel(pan)) {
4418             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4419             if (nv) {
4420                 var onav = nv.getWasActive();
4421                 if (onav) {
4422                     onav.setActive(true, false, true);
4423                 }
4424             }
4425             
4426         }
4427         
4428         
4429         
4430     },
4431      // this should not be here...
4432     setDisabled : function(state)
4433     {
4434         this.disabled = state;
4435         if (!state ) {
4436             this.el.removeClass('disabled');
4437         } else if (!this.el.hasClass('disabled')) {
4438             this.el.addClass('disabled');
4439         }
4440         
4441     },
4442     
4443     /**
4444      * Fetch the element to display the tooltip on.
4445      * @return {Roo.Element} defaults to this.el
4446      */
4447     tooltipEl : function()
4448     {
4449         return this.el.select('' + this.tagtype + '', true).first();
4450     },
4451     
4452     scrollToElement : function(e)
4453     {
4454         var c = document.body;
4455         
4456         /*
4457          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4458          */
4459         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4460             c = document.documentElement;
4461         }
4462         
4463         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4464         
4465         if(!target){
4466             return;
4467         }
4468
4469         var o = target.calcOffsetsTo(c);
4470         
4471         var options = {
4472             target : target,
4473             value : o[1]
4474         };
4475         
4476         this.fireEvent('scrollto', this, options, e);
4477         
4478         Roo.get(c).scrollTo('top', options.value, true);
4479         
4480         return;
4481     }
4482 });
4483  
4484
4485  /*
4486  * - LGPL
4487  *
4488  * sidebar item
4489  *
4490  *  li
4491  *    <span> icon </span>
4492  *    <span> text </span>
4493  *    <span>badge </span>
4494  */
4495
4496 /**
4497  * @class Roo.bootstrap.NavSidebarItem
4498  * @extends Roo.bootstrap.NavItem
4499  * Bootstrap Navbar.NavSidebarItem class
4500  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4501  * {bool} open is the menu open
4502  * @constructor
4503  * Create a new Navbar Button
4504  * @param {Object} config The config object
4505  */
4506 Roo.bootstrap.NavSidebarItem = function(config){
4507     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4508     this.addEvents({
4509         // raw events
4510         /**
4511          * @event click
4512          * The raw click event for the entire grid.
4513          * @param {Roo.EventObject} e
4514          */
4515         "click" : true,
4516          /**
4517             * @event changed
4518             * Fires when the active item active state changes
4519             * @param {Roo.bootstrap.NavSidebarItem} this
4520             * @param {boolean} state the new state
4521              
4522          */
4523         'changed': true
4524     });
4525    
4526 };
4527
4528 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4529     
4530     badgeWeight : 'default',
4531     
4532     open: false,
4533     
4534     getAutoCreate : function(){
4535         
4536         
4537         var a = {
4538                 tag: 'a',
4539                 href : this.href || '#',
4540                 cls: '',
4541                 html : '',
4542                 cn : []
4543         };
4544         var cfg = {
4545             tag: 'li',
4546             cls: '',
4547             cn: [ a ]
4548         };
4549         var span = {
4550             tag: 'span',
4551             html : this.html || ''
4552         };
4553         
4554         
4555         if (this.active) {
4556             cfg.cls += ' active';
4557         }
4558         
4559         if (this.disabled) {
4560             cfg.cls += ' disabled';
4561         }
4562         if (this.open) {
4563             cfg.cls += ' open x-open';
4564         }
4565         // left icon..
4566         if (this.glyphicon || this.icon) {
4567             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4568             a.cn.push({ tag : 'i', cls : c }) ;
4569         }
4570         // html..
4571         a.cn.push(span);
4572         // then badge..
4573         if (this.badge !== '') {
4574             
4575             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4576         }
4577         // fi
4578         if (this.menu) {
4579             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4580             a.cls += 'dropdown-toggle treeview' ;
4581         }
4582         
4583         return cfg;
4584          
4585            
4586     },
4587     
4588     initEvents : function()
4589     { 
4590         if (typeof (this.menu) != 'undefined') {
4591             this.menu.parentType = this.xtype;
4592             this.menu.triggerEl = this.el;
4593             this.menu = this.addxtype(Roo.apply({}, this.menu));
4594         }
4595         
4596         this.el.on('click', this.onClick, this);
4597        
4598     
4599         if(this.badge !== ''){
4600  
4601             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4602         }
4603         
4604     },
4605     
4606     onClick : function(e)
4607     {
4608         if(this.disabled){
4609             e.preventDefault();
4610             return;
4611         }
4612         
4613         if(this.preventDefault){
4614             e.preventDefault();
4615         }
4616         
4617         this.fireEvent('click', this);
4618     },
4619     
4620     disable : function()
4621     {
4622         this.setDisabled(true);
4623     },
4624     
4625     enable : function()
4626     {
4627         this.setDisabled(false);
4628     },
4629     
4630     setDisabled : function(state)
4631     {
4632         if(this.disabled == state){
4633             return;
4634         }
4635         
4636         this.disabled = state;
4637         
4638         if (state) {
4639             this.el.addClass('disabled');
4640             return;
4641         }
4642         
4643         this.el.removeClass('disabled');
4644         
4645         return;
4646     },
4647     
4648     setActive : function(state)
4649     {
4650         if(this.active == state){
4651             return;
4652         }
4653         
4654         this.active = state;
4655         
4656         if (state) {
4657             this.el.addClass('active');
4658             return;
4659         }
4660         
4661         this.el.removeClass('active');
4662         
4663         return;
4664     },
4665     
4666     isActive: function () 
4667     {
4668         return this.active;
4669     },
4670     
4671     setBadge : function(str)
4672     {
4673         if(!this.badgeEl){
4674             return;
4675         }
4676         
4677         this.badgeEl.dom.innerHTML = str;
4678     }
4679     
4680    
4681      
4682  
4683 });
4684  
4685
4686  /*
4687  * - LGPL
4688  *
4689  * row
4690  * 
4691  */
4692
4693 /**
4694  * @class Roo.bootstrap.Row
4695  * @extends Roo.bootstrap.Component
4696  * Bootstrap Row class (contains columns...)
4697  * 
4698  * @constructor
4699  * Create a new Row
4700  * @param {Object} config The config object
4701  */
4702
4703 Roo.bootstrap.Row = function(config){
4704     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4705 };
4706
4707 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4708     
4709     getAutoCreate : function(){
4710        return {
4711             cls: 'row clearfix'
4712        };
4713     }
4714     
4715     
4716 });
4717
4718  
4719
4720  /*
4721  * - LGPL
4722  *
4723  * element
4724  * 
4725  */
4726
4727 /**
4728  * @class Roo.bootstrap.Element
4729  * @extends Roo.bootstrap.Component
4730  * Bootstrap Element class
4731  * @cfg {String} html contents of the element
4732  * @cfg {String} tag tag of the element
4733  * @cfg {String} cls class of the element
4734  * @cfg {Boolean} preventDefault (true|false) default false
4735  * @cfg {Boolean} clickable (true|false) default false
4736  * 
4737  * @constructor
4738  * Create a new Element
4739  * @param {Object} config The config object
4740  */
4741
4742 Roo.bootstrap.Element = function(config){
4743     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4744     
4745     this.addEvents({
4746         // raw events
4747         /**
4748          * @event click
4749          * When a element is chick
4750          * @param {Roo.bootstrap.Element} this
4751          * @param {Roo.EventObject} e
4752          */
4753         "click" : true
4754     });
4755 };
4756
4757 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4758     
4759     tag: 'div',
4760     cls: '',
4761     html: '',
4762     preventDefault: false, 
4763     clickable: false,
4764     
4765     getAutoCreate : function(){
4766         
4767         var cfg = {
4768             tag: this.tag,
4769             cls: this.cls,
4770             html: this.html
4771         };
4772         
4773         return cfg;
4774     },
4775     
4776     initEvents: function() 
4777     {
4778         Roo.bootstrap.Element.superclass.initEvents.call(this);
4779         
4780         if(this.clickable){
4781             this.el.on('click', this.onClick, this);
4782         }
4783         
4784     },
4785     
4786     onClick : function(e)
4787     {
4788         if(this.preventDefault){
4789             e.preventDefault();
4790         }
4791         
4792         this.fireEvent('click', this, e);
4793     },
4794     
4795     getValue : function()
4796     {
4797         return this.el.dom.innerHTML;
4798     },
4799     
4800     setValue : function(value)
4801     {
4802         this.el.dom.innerHTML = value;
4803     }
4804    
4805 });
4806
4807  
4808
4809  /*
4810  * - LGPL
4811  *
4812  * pagination
4813  * 
4814  */
4815
4816 /**
4817  * @class Roo.bootstrap.Pagination
4818  * @extends Roo.bootstrap.Component
4819  * Bootstrap Pagination class
4820  * @cfg {String} size xs | sm | md | lg
4821  * @cfg {Boolean} inverse false | true
4822  * 
4823  * @constructor
4824  * Create a new Pagination
4825  * @param {Object} config The config object
4826  */
4827
4828 Roo.bootstrap.Pagination = function(config){
4829     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4830 };
4831
4832 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4833     
4834     cls: false,
4835     size: false,
4836     inverse: false,
4837     
4838     getAutoCreate : function(){
4839         var cfg = {
4840             tag: 'ul',
4841                 cls: 'pagination'
4842         };
4843         if (this.inverse) {
4844             cfg.cls += ' inverse';
4845         }
4846         if (this.html) {
4847             cfg.html=this.html;
4848         }
4849         if (this.cls) {
4850             cfg.cls += " " + this.cls;
4851         }
4852         return cfg;
4853     }
4854    
4855 });
4856
4857  
4858
4859  /*
4860  * - LGPL
4861  *
4862  * Pagination item
4863  * 
4864  */
4865
4866
4867 /**
4868  * @class Roo.bootstrap.PaginationItem
4869  * @extends Roo.bootstrap.Component
4870  * Bootstrap PaginationItem class
4871  * @cfg {String} html text
4872  * @cfg {String} href the link
4873  * @cfg {Boolean} preventDefault (true | false) default true
4874  * @cfg {Boolean} active (true | false) default false
4875  * @cfg {Boolean} disabled default false
4876  * 
4877  * 
4878  * @constructor
4879  * Create a new PaginationItem
4880  * @param {Object} config The config object
4881  */
4882
4883
4884 Roo.bootstrap.PaginationItem = function(config){
4885     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4886     this.addEvents({
4887         // raw events
4888         /**
4889          * @event click
4890          * The raw click event for the entire grid.
4891          * @param {Roo.EventObject} e
4892          */
4893         "click" : true
4894     });
4895 };
4896
4897 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4898     
4899     href : false,
4900     html : false,
4901     preventDefault: true,
4902     active : false,
4903     cls : false,
4904     disabled: false,
4905     
4906     getAutoCreate : function(){
4907         var cfg= {
4908             tag: 'li',
4909             cn: [
4910                 {
4911                     tag : 'a',
4912                     href : this.href ? this.href : '#',
4913                     html : this.html ? this.html : ''
4914                 }
4915             ]
4916         };
4917         
4918         if(this.cls){
4919             cfg.cls = this.cls;
4920         }
4921         
4922         if(this.disabled){
4923             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4924         }
4925         
4926         if(this.active){
4927             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4928         }
4929         
4930         return cfg;
4931     },
4932     
4933     initEvents: function() {
4934         
4935         this.el.on('click', this.onClick, this);
4936         
4937     },
4938     onClick : function(e)
4939     {
4940         Roo.log('PaginationItem on click ');
4941         if(this.preventDefault){
4942             e.preventDefault();
4943         }
4944         
4945         if(this.disabled){
4946             return;
4947         }
4948         
4949         this.fireEvent('click', this, e);
4950     }
4951    
4952 });
4953
4954  
4955
4956  /*
4957  * - LGPL
4958  *
4959  * slider
4960  * 
4961  */
4962
4963
4964 /**
4965  * @class Roo.bootstrap.Slider
4966  * @extends Roo.bootstrap.Component
4967  * Bootstrap Slider class
4968  *    
4969  * @constructor
4970  * Create a new Slider
4971  * @param {Object} config The config object
4972  */
4973
4974 Roo.bootstrap.Slider = function(config){
4975     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4976 };
4977
4978 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4979     
4980     getAutoCreate : function(){
4981         
4982         var cfg = {
4983             tag: 'div',
4984             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4985             cn: [
4986                 {
4987                     tag: 'a',
4988                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4989                 }
4990             ]
4991         };
4992         
4993         return cfg;
4994     }
4995    
4996 });
4997
4998  /*
4999  * Based on:
5000  * Ext JS Library 1.1.1
5001  * Copyright(c) 2006-2007, Ext JS, LLC.
5002  *
5003  * Originally Released Under LGPL - original licence link has changed is not relivant.
5004  *
5005  * Fork - LGPL
5006  * <script type="text/javascript">
5007  */
5008  
5009
5010 /**
5011  * @class Roo.grid.ColumnModel
5012  * @extends Roo.util.Observable
5013  * This is the default implementation of a ColumnModel used by the Grid. It defines
5014  * the columns in the grid.
5015  * <br>Usage:<br>
5016  <pre><code>
5017  var colModel = new Roo.grid.ColumnModel([
5018         {header: "Ticker", width: 60, sortable: true, locked: true},
5019         {header: "Company Name", width: 150, sortable: true},
5020         {header: "Market Cap.", width: 100, sortable: true},
5021         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5022         {header: "Employees", width: 100, sortable: true, resizable: false}
5023  ]);
5024  </code></pre>
5025  * <p>
5026  
5027  * The config options listed for this class are options which may appear in each
5028  * individual column definition.
5029  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5030  * @constructor
5031  * @param {Object} config An Array of column config objects. See this class's
5032  * config objects for details.
5033 */
5034 Roo.grid.ColumnModel = function(config){
5035         /**
5036      * The config passed into the constructor
5037      */
5038     this.config = config;
5039     this.lookup = {};
5040
5041     // if no id, create one
5042     // if the column does not have a dataIndex mapping,
5043     // map it to the order it is in the config
5044     for(var i = 0, len = config.length; i < len; i++){
5045         var c = config[i];
5046         if(typeof c.dataIndex == "undefined"){
5047             c.dataIndex = i;
5048         }
5049         if(typeof c.renderer == "string"){
5050             c.renderer = Roo.util.Format[c.renderer];
5051         }
5052         if(typeof c.id == "undefined"){
5053             c.id = Roo.id();
5054         }
5055         if(c.editor && c.editor.xtype){
5056             c.editor  = Roo.factory(c.editor, Roo.grid);
5057         }
5058         if(c.editor && c.editor.isFormField){
5059             c.editor = new Roo.grid.GridEditor(c.editor);
5060         }
5061         this.lookup[c.id] = c;
5062     }
5063
5064     /**
5065      * The width of columns which have no width specified (defaults to 100)
5066      * @type Number
5067      */
5068     this.defaultWidth = 100;
5069
5070     /**
5071      * Default sortable of columns which have no sortable specified (defaults to false)
5072      * @type Boolean
5073      */
5074     this.defaultSortable = false;
5075
5076     this.addEvents({
5077         /**
5078              * @event widthchange
5079              * Fires when the width of a column changes.
5080              * @param {ColumnModel} this
5081              * @param {Number} columnIndex The column index
5082              * @param {Number} newWidth The new width
5083              */
5084             "widthchange": true,
5085         /**
5086              * @event headerchange
5087              * Fires when the text of a header changes.
5088              * @param {ColumnModel} this
5089              * @param {Number} columnIndex The column index
5090              * @param {Number} newText The new header text
5091              */
5092             "headerchange": true,
5093         /**
5094              * @event hiddenchange
5095              * Fires when a column is hidden or "unhidden".
5096              * @param {ColumnModel} this
5097              * @param {Number} columnIndex The column index
5098              * @param {Boolean} hidden true if hidden, false otherwise
5099              */
5100             "hiddenchange": true,
5101             /**
5102          * @event columnmoved
5103          * Fires when a column is moved.
5104          * @param {ColumnModel} this
5105          * @param {Number} oldIndex
5106          * @param {Number} newIndex
5107          */
5108         "columnmoved" : true,
5109         /**
5110          * @event columlockchange
5111          * Fires when a column's locked state is changed
5112          * @param {ColumnModel} this
5113          * @param {Number} colIndex
5114          * @param {Boolean} locked true if locked
5115          */
5116         "columnlockchange" : true
5117     });
5118     Roo.grid.ColumnModel.superclass.constructor.call(this);
5119 };
5120 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5121     /**
5122      * @cfg {String} header The header text to display in the Grid view.
5123      */
5124     /**
5125      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5126      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5127      * specified, the column's index is used as an index into the Record's data Array.
5128      */
5129     /**
5130      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5131      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5132      */
5133     /**
5134      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5135      * Defaults to the value of the {@link #defaultSortable} property.
5136      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5137      */
5138     /**
5139      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5140      */
5141     /**
5142      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5143      */
5144     /**
5145      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5146      */
5147     /**
5148      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5149      */
5150     /**
5151      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5152      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5153      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5154      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5155      */
5156        /**
5157      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5158      */
5159     /**
5160      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5161      */
5162     /**
5163      * @cfg {String} cursor (Optional)
5164      */
5165     /**
5166      * @cfg {String} tooltip (Optional)
5167      */
5168     /**
5169      * @cfg {Number} xs (Optional)
5170      */
5171     /**
5172      * @cfg {Number} sm (Optional)
5173      */
5174     /**
5175      * @cfg {Number} md (Optional)
5176      */
5177     /**
5178      * @cfg {Number} lg (Optional)
5179      */
5180     /**
5181      * Returns the id of the column at the specified index.
5182      * @param {Number} index The column index
5183      * @return {String} the id
5184      */
5185     getColumnId : function(index){
5186         return this.config[index].id;
5187     },
5188
5189     /**
5190      * Returns the column for a specified id.
5191      * @param {String} id The column id
5192      * @return {Object} the column
5193      */
5194     getColumnById : function(id){
5195         return this.lookup[id];
5196     },
5197
5198     
5199     /**
5200      * Returns the column for a specified dataIndex.
5201      * @param {String} dataIndex The column dataIndex
5202      * @return {Object|Boolean} the column or false if not found
5203      */
5204     getColumnByDataIndex: function(dataIndex){
5205         var index = this.findColumnIndex(dataIndex);
5206         return index > -1 ? this.config[index] : false;
5207     },
5208     
5209     /**
5210      * Returns the index for a specified column id.
5211      * @param {String} id The column id
5212      * @return {Number} the index, or -1 if not found
5213      */
5214     getIndexById : function(id){
5215         for(var i = 0, len = this.config.length; i < len; i++){
5216             if(this.config[i].id == id){
5217                 return i;
5218             }
5219         }
5220         return -1;
5221     },
5222     
5223     /**
5224      * Returns the index for a specified column dataIndex.
5225      * @param {String} dataIndex The column dataIndex
5226      * @return {Number} the index, or -1 if not found
5227      */
5228     
5229     findColumnIndex : function(dataIndex){
5230         for(var i = 0, len = this.config.length; i < len; i++){
5231             if(this.config[i].dataIndex == dataIndex){
5232                 return i;
5233             }
5234         }
5235         return -1;
5236     },
5237     
5238     
5239     moveColumn : function(oldIndex, newIndex){
5240         var c = this.config[oldIndex];
5241         this.config.splice(oldIndex, 1);
5242         this.config.splice(newIndex, 0, c);
5243         this.dataMap = null;
5244         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5245     },
5246
5247     isLocked : function(colIndex){
5248         return this.config[colIndex].locked === true;
5249     },
5250
5251     setLocked : function(colIndex, value, suppressEvent){
5252         if(this.isLocked(colIndex) == value){
5253             return;
5254         }
5255         this.config[colIndex].locked = value;
5256         if(!suppressEvent){
5257             this.fireEvent("columnlockchange", this, colIndex, value);
5258         }
5259     },
5260
5261     getTotalLockedWidth : function(){
5262         var totalWidth = 0;
5263         for(var i = 0; i < this.config.length; i++){
5264             if(this.isLocked(i) && !this.isHidden(i)){
5265                 this.totalWidth += this.getColumnWidth(i);
5266             }
5267         }
5268         return totalWidth;
5269     },
5270
5271     getLockedCount : function(){
5272         for(var i = 0, len = this.config.length; i < len; i++){
5273             if(!this.isLocked(i)){
5274                 return i;
5275             }
5276         }
5277         
5278         return this.config.length;
5279     },
5280
5281     /**
5282      * Returns the number of columns.
5283      * @return {Number}
5284      */
5285     getColumnCount : function(visibleOnly){
5286         if(visibleOnly === true){
5287             var c = 0;
5288             for(var i = 0, len = this.config.length; i < len; i++){
5289                 if(!this.isHidden(i)){
5290                     c++;
5291                 }
5292             }
5293             return c;
5294         }
5295         return this.config.length;
5296     },
5297
5298     /**
5299      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5300      * @param {Function} fn
5301      * @param {Object} scope (optional)
5302      * @return {Array} result
5303      */
5304     getColumnsBy : function(fn, scope){
5305         var r = [];
5306         for(var i = 0, len = this.config.length; i < len; i++){
5307             var c = this.config[i];
5308             if(fn.call(scope||this, c, i) === true){
5309                 r[r.length] = c;
5310             }
5311         }
5312         return r;
5313     },
5314
5315     /**
5316      * Returns true if the specified column is sortable.
5317      * @param {Number} col The column index
5318      * @return {Boolean}
5319      */
5320     isSortable : function(col){
5321         if(typeof this.config[col].sortable == "undefined"){
5322             return this.defaultSortable;
5323         }
5324         return this.config[col].sortable;
5325     },
5326
5327     /**
5328      * Returns the rendering (formatting) function defined for the column.
5329      * @param {Number} col The column index.
5330      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5331      */
5332     getRenderer : function(col){
5333         if(!this.config[col].renderer){
5334             return Roo.grid.ColumnModel.defaultRenderer;
5335         }
5336         return this.config[col].renderer;
5337     },
5338
5339     /**
5340      * Sets the rendering (formatting) function for a column.
5341      * @param {Number} col The column index
5342      * @param {Function} fn The function to use to process the cell's raw data
5343      * to return HTML markup for the grid view. The render function is called with
5344      * the following parameters:<ul>
5345      * <li>Data value.</li>
5346      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5347      * <li>css A CSS style string to apply to the table cell.</li>
5348      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5349      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5350      * <li>Row index</li>
5351      * <li>Column index</li>
5352      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5353      */
5354     setRenderer : function(col, fn){
5355         this.config[col].renderer = fn;
5356     },
5357
5358     /**
5359      * Returns the width for the specified column.
5360      * @param {Number} col The column index
5361      * @return {Number}
5362      */
5363     getColumnWidth : function(col){
5364         return this.config[col].width * 1 || this.defaultWidth;
5365     },
5366
5367     /**
5368      * Sets the width for a column.
5369      * @param {Number} col The column index
5370      * @param {Number} width The new width
5371      */
5372     setColumnWidth : function(col, width, suppressEvent){
5373         this.config[col].width = width;
5374         this.totalWidth = null;
5375         if(!suppressEvent){
5376              this.fireEvent("widthchange", this, col, width);
5377         }
5378     },
5379
5380     /**
5381      * Returns the total width of all columns.
5382      * @param {Boolean} includeHidden True to include hidden column widths
5383      * @return {Number}
5384      */
5385     getTotalWidth : function(includeHidden){
5386         if(!this.totalWidth){
5387             this.totalWidth = 0;
5388             for(var i = 0, len = this.config.length; i < len; i++){
5389                 if(includeHidden || !this.isHidden(i)){
5390                     this.totalWidth += this.getColumnWidth(i);
5391                 }
5392             }
5393         }
5394         return this.totalWidth;
5395     },
5396
5397     /**
5398      * Returns the header for the specified column.
5399      * @param {Number} col The column index
5400      * @return {String}
5401      */
5402     getColumnHeader : function(col){
5403         return this.config[col].header;
5404     },
5405
5406     /**
5407      * Sets the header for a column.
5408      * @param {Number} col The column index
5409      * @param {String} header The new header
5410      */
5411     setColumnHeader : function(col, header){
5412         this.config[col].header = header;
5413         this.fireEvent("headerchange", this, col, header);
5414     },
5415
5416     /**
5417      * Returns the tooltip for the specified column.
5418      * @param {Number} col The column index
5419      * @return {String}
5420      */
5421     getColumnTooltip : function(col){
5422             return this.config[col].tooltip;
5423     },
5424     /**
5425      * Sets the tooltip for a column.
5426      * @param {Number} col The column index
5427      * @param {String} tooltip The new tooltip
5428      */
5429     setColumnTooltip : function(col, tooltip){
5430             this.config[col].tooltip = tooltip;
5431     },
5432
5433     /**
5434      * Returns the dataIndex for the specified column.
5435      * @param {Number} col The column index
5436      * @return {Number}
5437      */
5438     getDataIndex : function(col){
5439         return this.config[col].dataIndex;
5440     },
5441
5442     /**
5443      * Sets the dataIndex for a column.
5444      * @param {Number} col The column index
5445      * @param {Number} dataIndex The new dataIndex
5446      */
5447     setDataIndex : function(col, dataIndex){
5448         this.config[col].dataIndex = dataIndex;
5449     },
5450
5451     
5452     
5453     /**
5454      * Returns true if the cell is editable.
5455      * @param {Number} colIndex The column index
5456      * @param {Number} rowIndex The row index - this is nto actually used..?
5457      * @return {Boolean}
5458      */
5459     isCellEditable : function(colIndex, rowIndex){
5460         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5461     },
5462
5463     /**
5464      * Returns the editor defined for the cell/column.
5465      * return false or null to disable editing.
5466      * @param {Number} colIndex The column index
5467      * @param {Number} rowIndex The row index
5468      * @return {Object}
5469      */
5470     getCellEditor : function(colIndex, rowIndex){
5471         return this.config[colIndex].editor;
5472     },
5473
5474     /**
5475      * Sets if a column is editable.
5476      * @param {Number} col The column index
5477      * @param {Boolean} editable True if the column is editable
5478      */
5479     setEditable : function(col, editable){
5480         this.config[col].editable = editable;
5481     },
5482
5483
5484     /**
5485      * Returns true if the column is hidden.
5486      * @param {Number} colIndex The column index
5487      * @return {Boolean}
5488      */
5489     isHidden : function(colIndex){
5490         return this.config[colIndex].hidden;
5491     },
5492
5493
5494     /**
5495      * Returns true if the column width cannot be changed
5496      */
5497     isFixed : function(colIndex){
5498         return this.config[colIndex].fixed;
5499     },
5500
5501     /**
5502      * Returns true if the column can be resized
5503      * @return {Boolean}
5504      */
5505     isResizable : function(colIndex){
5506         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5507     },
5508     /**
5509      * Sets if a column is hidden.
5510      * @param {Number} colIndex The column index
5511      * @param {Boolean} hidden True if the column is hidden
5512      */
5513     setHidden : function(colIndex, hidden){
5514         this.config[colIndex].hidden = hidden;
5515         this.totalWidth = null;
5516         this.fireEvent("hiddenchange", this, colIndex, hidden);
5517     },
5518
5519     /**
5520      * Sets the editor for a column.
5521      * @param {Number} col The column index
5522      * @param {Object} editor The editor object
5523      */
5524     setEditor : function(col, editor){
5525         this.config[col].editor = editor;
5526     }
5527 });
5528
5529 Roo.grid.ColumnModel.defaultRenderer = function(value){
5530         if(typeof value == "string" && value.length < 1){
5531             return "&#160;";
5532         }
5533         return value;
5534 };
5535
5536 // Alias for backwards compatibility
5537 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5538 /*
5539  * Based on:
5540  * Ext JS Library 1.1.1
5541  * Copyright(c) 2006-2007, Ext JS, LLC.
5542  *
5543  * Originally Released Under LGPL - original licence link has changed is not relivant.
5544  *
5545  * Fork - LGPL
5546  * <script type="text/javascript">
5547  */
5548  
5549 /**
5550  * @class Roo.LoadMask
5551  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5552  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5553  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5554  * element's UpdateManager load indicator and will be destroyed after the initial load.
5555  * @constructor
5556  * Create a new LoadMask
5557  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5558  * @param {Object} config The config object
5559  */
5560 Roo.LoadMask = function(el, config){
5561     this.el = Roo.get(el);
5562     Roo.apply(this, config);
5563     if(this.store){
5564         this.store.on('beforeload', this.onBeforeLoad, this);
5565         this.store.on('load', this.onLoad, this);
5566         this.store.on('loadexception', this.onLoadException, this);
5567         this.removeMask = false;
5568     }else{
5569         var um = this.el.getUpdateManager();
5570         um.showLoadIndicator = false; // disable the default indicator
5571         um.on('beforeupdate', this.onBeforeLoad, this);
5572         um.on('update', this.onLoad, this);
5573         um.on('failure', this.onLoad, this);
5574         this.removeMask = true;
5575     }
5576 };
5577
5578 Roo.LoadMask.prototype = {
5579     /**
5580      * @cfg {Boolean} removeMask
5581      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5582      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5583      */
5584     /**
5585      * @cfg {String} msg
5586      * The text to display in a centered loading message box (defaults to 'Loading...')
5587      */
5588     msg : 'Loading...',
5589     /**
5590      * @cfg {String} msgCls
5591      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5592      */
5593     msgCls : 'x-mask-loading',
5594
5595     /**
5596      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5597      * @type Boolean
5598      */
5599     disabled: false,
5600
5601     /**
5602      * Disables the mask to prevent it from being displayed
5603      */
5604     disable : function(){
5605        this.disabled = true;
5606     },
5607
5608     /**
5609      * Enables the mask so that it can be displayed
5610      */
5611     enable : function(){
5612         this.disabled = false;
5613     },
5614     
5615     onLoadException : function()
5616     {
5617         Roo.log(arguments);
5618         
5619         if (typeof(arguments[3]) != 'undefined') {
5620             Roo.MessageBox.alert("Error loading",arguments[3]);
5621         } 
5622         /*
5623         try {
5624             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5625                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5626             }   
5627         } catch(e) {
5628             
5629         }
5630         */
5631     
5632         
5633         
5634         this.el.unmask(this.removeMask);
5635     },
5636     // private
5637     onLoad : function()
5638     {
5639         this.el.unmask(this.removeMask);
5640     },
5641
5642     // private
5643     onBeforeLoad : function(){
5644         if(!this.disabled){
5645             this.el.mask(this.msg, this.msgCls);
5646         }
5647     },
5648
5649     // private
5650     destroy : function(){
5651         if(this.store){
5652             this.store.un('beforeload', this.onBeforeLoad, this);
5653             this.store.un('load', this.onLoad, this);
5654             this.store.un('loadexception', this.onLoadException, this);
5655         }else{
5656             var um = this.el.getUpdateManager();
5657             um.un('beforeupdate', this.onBeforeLoad, this);
5658             um.un('update', this.onLoad, this);
5659             um.un('failure', this.onLoad, this);
5660         }
5661     }
5662 };/*
5663  * - LGPL
5664  *
5665  * table
5666  * 
5667  */
5668
5669 /**
5670  * @class Roo.bootstrap.Table
5671  * @extends Roo.bootstrap.Component
5672  * Bootstrap Table class
5673  * @cfg {String} cls table class
5674  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5675  * @cfg {String} bgcolor Specifies the background color for a table
5676  * @cfg {Number} border Specifies whether the table cells should have borders or not
5677  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5678  * @cfg {Number} cellspacing Specifies the space between cells
5679  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5680  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5681  * @cfg {String} sortable Specifies that the table should be sortable
5682  * @cfg {String} summary Specifies a summary of the content of a table
5683  * @cfg {Number} width Specifies the width of a table
5684  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5685  * 
5686  * @cfg {boolean} striped Should the rows be alternative striped
5687  * @cfg {boolean} bordered Add borders to the table
5688  * @cfg {boolean} hover Add hover highlighting
5689  * @cfg {boolean} condensed Format condensed
5690  * @cfg {boolean} responsive Format condensed
5691  * @cfg {Boolean} loadMask (true|false) default false
5692  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5693  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5694  * @cfg {Boolean} rowSelection (true|false) default false
5695  * @cfg {Boolean} cellSelection (true|false) default false
5696  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5697  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5698  
5699  * 
5700  * @constructor
5701  * Create a new Table
5702  * @param {Object} config The config object
5703  */
5704
5705 Roo.bootstrap.Table = function(config){
5706     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5707     
5708   
5709     
5710     // BC...
5711     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5712     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5713     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5714     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5715     
5716     
5717     if (this.sm) {
5718         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5719         this.sm = this.selModel;
5720         this.sm.xmodule = this.xmodule || false;
5721     }
5722     if (this.cm && typeof(this.cm.config) == 'undefined') {
5723         this.colModel = new Roo.grid.ColumnModel(this.cm);
5724         this.cm = this.colModel;
5725         this.cm.xmodule = this.xmodule || false;
5726     }
5727     if (this.store) {
5728         this.store= Roo.factory(this.store, Roo.data);
5729         this.ds = this.store;
5730         this.ds.xmodule = this.xmodule || false;
5731          
5732     }
5733     if (this.footer && this.store) {
5734         this.footer.dataSource = this.ds;
5735         this.footer = Roo.factory(this.footer);
5736     }
5737     
5738     /** @private */
5739     this.addEvents({
5740         /**
5741          * @event cellclick
5742          * Fires when a cell is clicked
5743          * @param {Roo.bootstrap.Table} this
5744          * @param {Roo.Element} el
5745          * @param {Number} rowIndex
5746          * @param {Number} columnIndex
5747          * @param {Roo.EventObject} e
5748          */
5749         "cellclick" : true,
5750         /**
5751          * @event celldblclick
5752          * Fires when a cell is double clicked
5753          * @param {Roo.bootstrap.Table} this
5754          * @param {Roo.Element} el
5755          * @param {Number} rowIndex
5756          * @param {Number} columnIndex
5757          * @param {Roo.EventObject} e
5758          */
5759         "celldblclick" : true,
5760         /**
5761          * @event rowclick
5762          * Fires when a row is clicked
5763          * @param {Roo.bootstrap.Table} this
5764          * @param {Roo.Element} el
5765          * @param {Number} rowIndex
5766          * @param {Roo.EventObject} e
5767          */
5768         "rowclick" : true,
5769         /**
5770          * @event rowdblclick
5771          * Fires when a row is double clicked
5772          * @param {Roo.bootstrap.Table} this
5773          * @param {Roo.Element} el
5774          * @param {Number} rowIndex
5775          * @param {Roo.EventObject} e
5776          */
5777         "rowdblclick" : true,
5778         /**
5779          * @event mouseover
5780          * Fires when a mouseover occur
5781          * @param {Roo.bootstrap.Table} this
5782          * @param {Roo.Element} el
5783          * @param {Number} rowIndex
5784          * @param {Number} columnIndex
5785          * @param {Roo.EventObject} e
5786          */
5787         "mouseover" : true,
5788         /**
5789          * @event mouseout
5790          * Fires when a mouseout occur
5791          * @param {Roo.bootstrap.Table} this
5792          * @param {Roo.Element} el
5793          * @param {Number} rowIndex
5794          * @param {Number} columnIndex
5795          * @param {Roo.EventObject} e
5796          */
5797         "mouseout" : true,
5798         /**
5799          * @event rowclass
5800          * Fires when a row is rendered, so you can change add a style to it.
5801          * @param {Roo.bootstrap.Table} this
5802          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5803          */
5804         'rowclass' : true,
5805           /**
5806          * @event rowsrendered
5807          * Fires when all the  rows have been rendered
5808          * @param {Roo.bootstrap.Table} this
5809          */
5810         'rowsrendered' : true,
5811         /**
5812          * @event contextmenu
5813          * The raw contextmenu event for the entire grid.
5814          * @param {Roo.EventObject} e
5815          */
5816         "contextmenu" : true,
5817         /**
5818          * @event rowcontextmenu
5819          * Fires when a row is right clicked
5820          * @param {Roo.bootstrap.Table} this
5821          * @param {Number} rowIndex
5822          * @param {Roo.EventObject} e
5823          */
5824         "rowcontextmenu" : true,
5825         /**
5826          * @event cellcontextmenu
5827          * Fires when a cell is right clicked
5828          * @param {Roo.bootstrap.Table} this
5829          * @param {Number} rowIndex
5830          * @param {Number} cellIndex
5831          * @param {Roo.EventObject} e
5832          */
5833          "cellcontextmenu" : true,
5834          /**
5835          * @event headercontextmenu
5836          * Fires when a header is right clicked
5837          * @param {Roo.bootstrap.Table} this
5838          * @param {Number} columnIndex
5839          * @param {Roo.EventObject} e
5840          */
5841         "headercontextmenu" : true
5842     });
5843 };
5844
5845 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5846     
5847     cls: false,
5848     align: false,
5849     bgcolor: false,
5850     border: false,
5851     cellpadding: false,
5852     cellspacing: false,
5853     frame: false,
5854     rules: false,
5855     sortable: false,
5856     summary: false,
5857     width: false,
5858     striped : false,
5859     scrollBody : false,
5860     bordered: false,
5861     hover:  false,
5862     condensed : false,
5863     responsive : false,
5864     sm : false,
5865     cm : false,
5866     store : false,
5867     loadMask : false,
5868     footerShow : true,
5869     headerShow : true,
5870   
5871     rowSelection : false,
5872     cellSelection : false,
5873     layout : false,
5874     
5875     // Roo.Element - the tbody
5876     mainBody: false,
5877     // Roo.Element - thead element
5878     mainHead: false,
5879     
5880     container: false, // used by gridpanel...
5881     
5882     getAutoCreate : function()
5883     {
5884         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5885         
5886         cfg = {
5887             tag: 'table',
5888             cls : 'table',
5889             cn : []
5890         };
5891         if (this.scrollBody) {
5892             cfg.cls += ' table-body-fixed';
5893         }    
5894         if (this.striped) {
5895             cfg.cls += ' table-striped';
5896         }
5897         
5898         if (this.hover) {
5899             cfg.cls += ' table-hover';
5900         }
5901         if (this.bordered) {
5902             cfg.cls += ' table-bordered';
5903         }
5904         if (this.condensed) {
5905             cfg.cls += ' table-condensed';
5906         }
5907         if (this.responsive) {
5908             cfg.cls += ' table-responsive';
5909         }
5910         
5911         if (this.cls) {
5912             cfg.cls+=  ' ' +this.cls;
5913         }
5914         
5915         // this lot should be simplifed...
5916         
5917         if (this.align) {
5918             cfg.align=this.align;
5919         }
5920         if (this.bgcolor) {
5921             cfg.bgcolor=this.bgcolor;
5922         }
5923         if (this.border) {
5924             cfg.border=this.border;
5925         }
5926         if (this.cellpadding) {
5927             cfg.cellpadding=this.cellpadding;
5928         }
5929         if (this.cellspacing) {
5930             cfg.cellspacing=this.cellspacing;
5931         }
5932         if (this.frame) {
5933             cfg.frame=this.frame;
5934         }
5935         if (this.rules) {
5936             cfg.rules=this.rules;
5937         }
5938         if (this.sortable) {
5939             cfg.sortable=this.sortable;
5940         }
5941         if (this.summary) {
5942             cfg.summary=this.summary;
5943         }
5944         if (this.width) {
5945             cfg.width=this.width;
5946         }
5947         if (this.layout) {
5948             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5949         }
5950         
5951         if(this.store || this.cm){
5952             if(this.headerShow){
5953                 cfg.cn.push(this.renderHeader());
5954             }
5955             
5956             cfg.cn.push(this.renderBody());
5957             
5958             if(this.footerShow){
5959                 cfg.cn.push(this.renderFooter());
5960             }
5961             // where does this come from?
5962             //cfg.cls+=  ' TableGrid';
5963         }
5964         
5965         return { cn : [ cfg ] };
5966     },
5967     
5968     initEvents : function()
5969     {   
5970         if(!this.store || !this.cm){
5971             return;
5972         }
5973         
5974         //Roo.log('initEvents with ds!!!!');
5975         
5976         this.mainBody = this.el.select('tbody', true).first();
5977         this.mainHead = this.el.select('thead', true).first();
5978         
5979         
5980         
5981         var _this = this;
5982         
5983         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5984             e.on('click', _this.sort, _this);
5985         });
5986         
5987         this.el.on("click", this.onClick, this);
5988         this.el.on("dblclick", this.onDblClick, this);
5989         
5990         // why is this done????? = it breaks dialogs??
5991         //this.parent().el.setStyle('position', 'relative');
5992         
5993         
5994         if (this.footer) {
5995             this.footer.parentId = this.id;
5996             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5997         }
5998         
5999         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6000         
6001         this.store.on('load', this.onLoad, this);
6002         this.store.on('beforeload', this.onBeforeLoad, this);
6003         this.store.on('update', this.onUpdate, this);
6004         this.store.on('add', this.onAdd, this);
6005         this.store.on("clear", this.clear, this);
6006         
6007         this.el.on("contextmenu", this.onContextMenu, this);
6008         
6009         this.mainBody.on('scroll', this.onBodyScroll, this);
6010         
6011         
6012     },
6013     
6014     onContextMenu : function(e, t)
6015     {
6016         this.processEvent("contextmenu", e);
6017     },
6018     
6019     processEvent : function(name, e)
6020     {
6021         if (name != 'touchstart' ) {
6022             this.fireEvent(name, e);    
6023         }
6024         
6025         var t = e.getTarget();
6026         
6027         var cell = Roo.get(t);
6028         
6029         if(!cell){
6030             return;
6031         }
6032         
6033         if(cell.findParent('tfoot', false, true)){
6034             return;
6035         }
6036         
6037         if(cell.findParent('thead', false, true)){
6038             
6039             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6040                 cell = Roo.get(t).findParent('th', false, true);
6041                 if (!cell) {
6042                     Roo.log("failed to find th in thead?");
6043                     Roo.log(e.getTarget());
6044                     return;
6045                 }
6046             }
6047             
6048             var cellIndex = cell.dom.cellIndex;
6049             
6050             var ename = name == 'touchstart' ? 'click' : name;
6051             this.fireEvent("header" + ename, this, cellIndex, e);
6052             
6053             return;
6054         }
6055         
6056         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6057             cell = Roo.get(t).findParent('td', false, true);
6058             if (!cell) {
6059                 Roo.log("failed to find th in tbody?");
6060                 Roo.log(e.getTarget());
6061                 return;
6062             }
6063         }
6064         
6065         var row = cell.findParent('tr', false, true);
6066         var cellIndex = cell.dom.cellIndex;
6067         var rowIndex = row.dom.rowIndex - 1;
6068         
6069         if(row !== false){
6070             
6071             this.fireEvent("row" + name, this, rowIndex, e);
6072             
6073             if(cell !== false){
6074             
6075                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6076             }
6077         }
6078         
6079     },
6080     
6081     onMouseover : function(e, el)
6082     {
6083         var cell = Roo.get(el);
6084         
6085         if(!cell){
6086             return;
6087         }
6088         
6089         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6090             cell = cell.findParent('td', false, true);
6091         }
6092         
6093         var row = cell.findParent('tr', false, true);
6094         var cellIndex = cell.dom.cellIndex;
6095         var rowIndex = row.dom.rowIndex - 1; // start from 0
6096         
6097         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6098         
6099     },
6100     
6101     onMouseout : function(e, el)
6102     {
6103         var cell = Roo.get(el);
6104         
6105         if(!cell){
6106             return;
6107         }
6108         
6109         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6110             cell = cell.findParent('td', false, true);
6111         }
6112         
6113         var row = cell.findParent('tr', false, true);
6114         var cellIndex = cell.dom.cellIndex;
6115         var rowIndex = row.dom.rowIndex - 1; // start from 0
6116         
6117         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6118         
6119     },
6120     
6121     onClick : function(e, el)
6122     {
6123         var cell = Roo.get(el);
6124         
6125         if(!cell || (!this.cellSelection && !this.rowSelection)){
6126             return;
6127         }
6128         
6129         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6130             cell = cell.findParent('td', false, true);
6131         }
6132         
6133         if(!cell || typeof(cell) == 'undefined'){
6134             return;
6135         }
6136         
6137         var row = cell.findParent('tr', false, true);
6138         
6139         if(!row || typeof(row) == 'undefined'){
6140             return;
6141         }
6142         
6143         var cellIndex = cell.dom.cellIndex;
6144         var rowIndex = this.getRowIndex(row);
6145         
6146         // why??? - should these not be based on SelectionModel?
6147         if(this.cellSelection){
6148             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6149         }
6150         
6151         if(this.rowSelection){
6152             this.fireEvent('rowclick', this, row, rowIndex, e);
6153         }
6154         
6155         
6156     },
6157     
6158     onDblClick : function(e,el)
6159     {
6160         var cell = Roo.get(el);
6161         
6162         if(!cell || (!this.CellSelection && !this.RowSelection)){
6163             return;
6164         }
6165         
6166         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6167             cell = cell.findParent('td', false, true);
6168         }
6169         
6170         if(!cell || typeof(cell) == 'undefined'){
6171             return;
6172         }
6173         
6174         var row = cell.findParent('tr', false, true);
6175         
6176         if(!row || typeof(row) == 'undefined'){
6177             return;
6178         }
6179         
6180         var cellIndex = cell.dom.cellIndex;
6181         var rowIndex = this.getRowIndex(row);
6182         
6183         if(this.CellSelection){
6184             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6185         }
6186         
6187         if(this.RowSelection){
6188             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6189         }
6190     },
6191     
6192     sort : function(e,el)
6193     {
6194         var col = Roo.get(el);
6195         
6196         if(!col.hasClass('sortable')){
6197             return;
6198         }
6199         
6200         var sort = col.attr('sort');
6201         var dir = 'ASC';
6202         
6203         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6204             dir = 'DESC';
6205         }
6206         
6207         this.store.sortInfo = {field : sort, direction : dir};
6208         
6209         if (this.footer) {
6210             Roo.log("calling footer first");
6211             this.footer.onClick('first');
6212         } else {
6213         
6214             this.store.load({ params : { start : 0 } });
6215         }
6216     },
6217     
6218     renderHeader : function()
6219     {
6220         var header = {
6221             tag: 'thead',
6222             cn : []
6223         };
6224         
6225         var cm = this.cm;
6226         this.totalWidth = 0;
6227         
6228         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6229             
6230             var config = cm.config[i];
6231             
6232             var c = {
6233                 tag: 'th',
6234                 style : '',
6235                 html: cm.getColumnHeader(i)
6236             };
6237             
6238             var hh = '';
6239             
6240             if(typeof(config.sortable) != 'undefined' && config.sortable){
6241                 c.cls = 'sortable';
6242                 c.html = '<i class="glyphicon"></i>' + c.html;
6243             }
6244             
6245             if(typeof(config.lgHeader) != 'undefined'){
6246                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6247             }
6248             
6249             if(typeof(config.mdHeader) != 'undefined'){
6250                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6251             }
6252             
6253             if(typeof(config.smHeader) != 'undefined'){
6254                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6255             }
6256             
6257             if(typeof(config.xsHeader) != 'undefined'){
6258                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6259             }
6260             
6261             if(hh.length){
6262                 c.html = hh;
6263             }
6264             
6265             if(typeof(config.tooltip) != 'undefined'){
6266                 c.tooltip = config.tooltip;
6267             }
6268             
6269             if(typeof(config.colspan) != 'undefined'){
6270                 c.colspan = config.colspan;
6271             }
6272             
6273             if(typeof(config.hidden) != 'undefined' && config.hidden){
6274                 c.style += ' display:none;';
6275             }
6276             
6277             if(typeof(config.dataIndex) != 'undefined'){
6278                 c.sort = config.dataIndex;
6279             }
6280             
6281            
6282             
6283             if(typeof(config.align) != 'undefined' && config.align.length){
6284                 c.style += ' text-align:' + config.align + ';';
6285             }
6286             
6287             if(typeof(config.width) != 'undefined'){
6288                 c.style += ' width:' + config.width + 'px;';
6289                 this.totalWidth += config.width;
6290             } else {
6291                 this.totalWidth += 100; // assume minimum of 100 per column?
6292             }
6293             
6294             if(typeof(config.cls) != 'undefined'){
6295                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6296             }
6297             
6298             ['xs','sm','md','lg'].map(function(size){
6299                 
6300                 if(typeof(config[size]) == 'undefined'){
6301                     return;
6302                 }
6303                 
6304                 if (!config[size]) { // 0 = hidden
6305                     c.cls += ' hidden-' + size;
6306                     return;
6307                 }
6308                 
6309                 c.cls += ' col-' + size + '-' + config[size];
6310
6311             });
6312             
6313             header.cn.push(c)
6314         }
6315         
6316         return header;
6317     },
6318     
6319     renderBody : function()
6320     {
6321         var body = {
6322             tag: 'tbody',
6323             cn : [
6324                 {
6325                     tag: 'tr',
6326                     cn : [
6327                         {
6328                             tag : 'td',
6329                             colspan :  this.cm.getColumnCount()
6330                         }
6331                     ]
6332                 }
6333             ]
6334         };
6335         
6336         return body;
6337     },
6338     
6339     renderFooter : function()
6340     {
6341         var footer = {
6342             tag: 'tfoot',
6343             cn : [
6344                 {
6345                     tag: 'tr',
6346                     cn : [
6347                         {
6348                             tag : 'td',
6349                             colspan :  this.cm.getColumnCount()
6350                         }
6351                     ]
6352                 }
6353             ]
6354         };
6355         
6356         return footer;
6357     },
6358     
6359     
6360     
6361     onLoad : function()
6362     {
6363 //        Roo.log('ds onload');
6364         this.clear();
6365         
6366         var _this = this;
6367         var cm = this.cm;
6368         var ds = this.store;
6369         
6370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6371             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6372             if (_this.store.sortInfo) {
6373                     
6374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6375                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6376                 }
6377                 
6378                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6379                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6380                 }
6381             }
6382         });
6383         
6384         var tbody =  this.mainBody;
6385               
6386         if(ds.getCount() > 0){
6387             ds.data.each(function(d,rowIndex){
6388                 var row =  this.renderRow(cm, ds, rowIndex);
6389                 
6390                 tbody.createChild(row);
6391                 
6392                 var _this = this;
6393                 
6394                 if(row.cellObjects.length){
6395                     Roo.each(row.cellObjects, function(r){
6396                         _this.renderCellObject(r);
6397                     })
6398                 }
6399                 
6400             }, this);
6401         }
6402         
6403         Roo.each(this.el.select('tbody td', true).elements, function(e){
6404             e.on('mouseover', _this.onMouseover, _this);
6405         });
6406         
6407         Roo.each(this.el.select('tbody td', true).elements, function(e){
6408             e.on('mouseout', _this.onMouseout, _this);
6409         });
6410         this.fireEvent('rowsrendered', this);
6411         //if(this.loadMask){
6412         //    this.maskEl.hide();
6413         //}
6414         
6415         this.autoSize();
6416     },
6417     
6418     
6419     onUpdate : function(ds,record)
6420     {
6421         this.refreshRow(record);
6422     },
6423     
6424     onRemove : function(ds, record, index, isUpdate){
6425         if(isUpdate !== true){
6426             this.fireEvent("beforerowremoved", this, index, record);
6427         }
6428         var bt = this.mainBody.dom;
6429         
6430         var rows = this.el.select('tbody > tr', true).elements;
6431         
6432         if(typeof(rows[index]) != 'undefined'){
6433             bt.removeChild(rows[index].dom);
6434         }
6435         
6436 //        if(bt.rows[index]){
6437 //            bt.removeChild(bt.rows[index]);
6438 //        }
6439         
6440         if(isUpdate !== true){
6441             //this.stripeRows(index);
6442             //this.syncRowHeights(index, index);
6443             //this.layout();
6444             this.fireEvent("rowremoved", this, index, record);
6445         }
6446     },
6447     
6448     onAdd : function(ds, records, rowIndex)
6449     {
6450         //Roo.log('on Add called');
6451         // - note this does not handle multiple adding very well..
6452         var bt = this.mainBody.dom;
6453         for (var i =0 ; i < records.length;i++) {
6454             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6455             //Roo.log(records[i]);
6456             //Roo.log(this.store.getAt(rowIndex+i));
6457             this.insertRow(this.store, rowIndex + i, false);
6458             return;
6459         }
6460         
6461     },
6462     
6463     
6464     refreshRow : function(record){
6465         var ds = this.store, index;
6466         if(typeof record == 'number'){
6467             index = record;
6468             record = ds.getAt(index);
6469         }else{
6470             index = ds.indexOf(record);
6471         }
6472         this.insertRow(ds, index, true);
6473         this.onRemove(ds, record, index+1, true);
6474         //this.syncRowHeights(index, index);
6475         //this.layout();
6476         this.fireEvent("rowupdated", this, index, record);
6477     },
6478     
6479     insertRow : function(dm, rowIndex, isUpdate){
6480         
6481         if(!isUpdate){
6482             this.fireEvent("beforerowsinserted", this, rowIndex);
6483         }
6484             //var s = this.getScrollState();
6485         var row = this.renderRow(this.cm, this.store, rowIndex);
6486         // insert before rowIndex..
6487         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6488         
6489         var _this = this;
6490                 
6491         if(row.cellObjects.length){
6492             Roo.each(row.cellObjects, function(r){
6493                 _this.renderCellObject(r);
6494             })
6495         }
6496             
6497         if(!isUpdate){
6498             this.fireEvent("rowsinserted", this, rowIndex);
6499             //this.syncRowHeights(firstRow, lastRow);
6500             //this.stripeRows(firstRow);
6501             //this.layout();
6502         }
6503         
6504     },
6505     
6506     
6507     getRowDom : function(rowIndex)
6508     {
6509         var rows = this.el.select('tbody > tr', true).elements;
6510         
6511         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6512         
6513     },
6514     // returns the object tree for a tr..
6515   
6516     
6517     renderRow : function(cm, ds, rowIndex) 
6518     {
6519         
6520         var d = ds.getAt(rowIndex);
6521         
6522         var row = {
6523             tag : 'tr',
6524             cn : []
6525         };
6526             
6527         var cellObjects = [];
6528         
6529         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6530             var config = cm.config[i];
6531             
6532             var renderer = cm.getRenderer(i);
6533             var value = '';
6534             var id = false;
6535             
6536             if(typeof(renderer) !== 'undefined'){
6537                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6538             }
6539             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6540             // and are rendered into the cells after the row is rendered - using the id for the element.
6541             
6542             if(typeof(value) === 'object'){
6543                 id = Roo.id();
6544                 cellObjects.push({
6545                     container : id,
6546                     cfg : value 
6547                 })
6548             }
6549             
6550             var rowcfg = {
6551                 record: d,
6552                 rowIndex : rowIndex,
6553                 colIndex : i,
6554                 rowClass : ''
6555             };
6556
6557             this.fireEvent('rowclass', this, rowcfg);
6558             
6559             var td = {
6560                 tag: 'td',
6561                 cls : rowcfg.rowClass,
6562                 style: '',
6563                 html: (typeof(value) === 'object') ? '' : value
6564             };
6565             
6566             if (id) {
6567                 td.id = id;
6568             }
6569             
6570             if(typeof(config.colspan) != 'undefined'){
6571                 td.colspan = config.colspan;
6572             }
6573             
6574             if(typeof(config.hidden) != 'undefined' && config.hidden){
6575                 td.style += ' display:none;';
6576             }
6577             
6578             if(typeof(config.align) != 'undefined' && config.align.length){
6579                 td.style += ' text-align:' + config.align + ';';
6580             }
6581             
6582             if(typeof(config.width) != 'undefined'){
6583                 td.style += ' width:' +  config.width + 'px;';
6584             }
6585             
6586             if(typeof(config.cursor) != 'undefined'){
6587                 td.style += ' cursor:' +  config.cursor + ';';
6588             }
6589             
6590             if(typeof(config.cls) != 'undefined'){
6591                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6592             }
6593             
6594             ['xs','sm','md','lg'].map(function(size){
6595                 
6596                 if(typeof(config[size]) == 'undefined'){
6597                     return;
6598                 }
6599                 
6600                 if (!config[size]) { // 0 = hidden
6601                     td.cls += ' hidden-' + size;
6602                     return;
6603                 }
6604                 
6605                 td.cls += ' col-' + size + '-' + config[size];
6606
6607             });
6608              
6609             row.cn.push(td);
6610            
6611         }
6612         
6613         row.cellObjects = cellObjects;
6614         
6615         return row;
6616           
6617     },
6618     
6619     
6620     
6621     onBeforeLoad : function()
6622     {
6623         //Roo.log('ds onBeforeLoad');
6624         
6625         //this.clear();
6626         
6627         //if(this.loadMask){
6628         //    this.maskEl.show();
6629         //}
6630     },
6631      /**
6632      * Remove all rows
6633      */
6634     clear : function()
6635     {
6636         this.el.select('tbody', true).first().dom.innerHTML = '';
6637     },
6638     /**
6639      * Show or hide a row.
6640      * @param {Number} rowIndex to show or hide
6641      * @param {Boolean} state hide
6642      */
6643     setRowVisibility : function(rowIndex, state)
6644     {
6645         var bt = this.mainBody.dom;
6646         
6647         var rows = this.el.select('tbody > tr', true).elements;
6648         
6649         if(typeof(rows[rowIndex]) == 'undefined'){
6650             return;
6651         }
6652         rows[rowIndex].dom.style.display = state ? '' : 'none';
6653     },
6654     
6655     
6656     getSelectionModel : function(){
6657         if(!this.selModel){
6658             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6659         }
6660         return this.selModel;
6661     },
6662     /*
6663      * Render the Roo.bootstrap object from renderder
6664      */
6665     renderCellObject : function(r)
6666     {
6667         var _this = this;
6668         
6669         var t = r.cfg.render(r.container);
6670         
6671         if(r.cfg.cn){
6672             Roo.each(r.cfg.cn, function(c){
6673                 var child = {
6674                     container: t.getChildContainer(),
6675                     cfg: c
6676                 };
6677                 _this.renderCellObject(child);
6678             })
6679         }
6680     },
6681     
6682     getRowIndex : function(row)
6683     {
6684         var rowIndex = -1;
6685         
6686         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6687             if(el != row){
6688                 return;
6689             }
6690             
6691             rowIndex = index;
6692         });
6693         
6694         return rowIndex;
6695     },
6696      /**
6697      * Returns the grid's underlying element = used by panel.Grid
6698      * @return {Element} The element
6699      */
6700     getGridEl : function(){
6701         return this.el;
6702     },
6703      /**
6704      * Forces a resize - used by panel.Grid
6705      * @return {Element} The element
6706      */
6707     autoSize : function()
6708     {
6709         //var ctr = Roo.get(this.container.dom.parentElement);
6710         var ctr = Roo.get(this.el.dom);
6711         
6712         var thd = this.getGridEl().select('thead',true).first();
6713         var tbd = this.getGridEl().select('tbody', true).first();
6714         
6715         
6716         var cw = ctr.getWidth();
6717         
6718         if (tbd) {
6719             
6720             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6721             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6722             cw -= barsize;
6723         }
6724         cw = Math.max(cw, this.totalWidth);
6725         this.getGridEl().select('tr',true).setWidth(cw);
6726         // resize 'expandable coloumn?
6727         
6728         return; // we doe not have a view in this design..
6729         
6730     },
6731     onBodyScroll: function()
6732     {
6733         
6734         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6735         this.mainHead.setStyle({
6736                     'position' : 'relative',
6737                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6738         });
6739         
6740         
6741     }
6742 });
6743
6744  
6745
6746  /*
6747  * - LGPL
6748  *
6749  * table cell
6750  * 
6751  */
6752
6753 /**
6754  * @class Roo.bootstrap.TableCell
6755  * @extends Roo.bootstrap.Component
6756  * Bootstrap TableCell class
6757  * @cfg {String} html cell contain text
6758  * @cfg {String} cls cell class
6759  * @cfg {String} tag cell tag (td|th) default td
6760  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6761  * @cfg {String} align Aligns the content in a cell
6762  * @cfg {String} axis Categorizes cells
6763  * @cfg {String} bgcolor Specifies the background color of a cell
6764  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6765  * @cfg {Number} colspan Specifies the number of columns a cell should span
6766  * @cfg {String} headers Specifies one or more header cells a cell is related to
6767  * @cfg {Number} height Sets the height of a cell
6768  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6769  * @cfg {Number} rowspan Sets the number of rows a cell should span
6770  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6771  * @cfg {String} valign Vertical aligns the content in a cell
6772  * @cfg {Number} width Specifies the width of a cell
6773  * 
6774  * @constructor
6775  * Create a new TableCell
6776  * @param {Object} config The config object
6777  */
6778
6779 Roo.bootstrap.TableCell = function(config){
6780     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6781 };
6782
6783 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6784     
6785     html: false,
6786     cls: false,
6787     tag: false,
6788     abbr: false,
6789     align: false,
6790     axis: false,
6791     bgcolor: false,
6792     charoff: false,
6793     colspan: false,
6794     headers: false,
6795     height: false,
6796     nowrap: false,
6797     rowspan: false,
6798     scope: false,
6799     valign: false,
6800     width: false,
6801     
6802     
6803     getAutoCreate : function(){
6804         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6805         
6806         cfg = {
6807             tag: 'td'
6808         };
6809         
6810         if(this.tag){
6811             cfg.tag = this.tag;
6812         }
6813         
6814         if (this.html) {
6815             cfg.html=this.html
6816         }
6817         if (this.cls) {
6818             cfg.cls=this.cls
6819         }
6820         if (this.abbr) {
6821             cfg.abbr=this.abbr
6822         }
6823         if (this.align) {
6824             cfg.align=this.align
6825         }
6826         if (this.axis) {
6827             cfg.axis=this.axis
6828         }
6829         if (this.bgcolor) {
6830             cfg.bgcolor=this.bgcolor
6831         }
6832         if (this.charoff) {
6833             cfg.charoff=this.charoff
6834         }
6835         if (this.colspan) {
6836             cfg.colspan=this.colspan
6837         }
6838         if (this.headers) {
6839             cfg.headers=this.headers
6840         }
6841         if (this.height) {
6842             cfg.height=this.height
6843         }
6844         if (this.nowrap) {
6845             cfg.nowrap=this.nowrap
6846         }
6847         if (this.rowspan) {
6848             cfg.rowspan=this.rowspan
6849         }
6850         if (this.scope) {
6851             cfg.scope=this.scope
6852         }
6853         if (this.valign) {
6854             cfg.valign=this.valign
6855         }
6856         if (this.width) {
6857             cfg.width=this.width
6858         }
6859         
6860         
6861         return cfg;
6862     }
6863    
6864 });
6865
6866  
6867
6868  /*
6869  * - LGPL
6870  *
6871  * table row
6872  * 
6873  */
6874
6875 /**
6876  * @class Roo.bootstrap.TableRow
6877  * @extends Roo.bootstrap.Component
6878  * Bootstrap TableRow class
6879  * @cfg {String} cls row class
6880  * @cfg {String} align Aligns the content in a table row
6881  * @cfg {String} bgcolor Specifies a background color for a table row
6882  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6883  * @cfg {String} valign Vertical aligns the content in a table row
6884  * 
6885  * @constructor
6886  * Create a new TableRow
6887  * @param {Object} config The config object
6888  */
6889
6890 Roo.bootstrap.TableRow = function(config){
6891     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6892 };
6893
6894 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6895     
6896     cls: false,
6897     align: false,
6898     bgcolor: false,
6899     charoff: false,
6900     valign: false,
6901     
6902     getAutoCreate : function(){
6903         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6904         
6905         cfg = {
6906             tag: 'tr'
6907         };
6908             
6909         if(this.cls){
6910             cfg.cls = this.cls;
6911         }
6912         if(this.align){
6913             cfg.align = this.align;
6914         }
6915         if(this.bgcolor){
6916             cfg.bgcolor = this.bgcolor;
6917         }
6918         if(this.charoff){
6919             cfg.charoff = this.charoff;
6920         }
6921         if(this.valign){
6922             cfg.valign = this.valign;
6923         }
6924         
6925         return cfg;
6926     }
6927    
6928 });
6929
6930  
6931
6932  /*
6933  * - LGPL
6934  *
6935  * table body
6936  * 
6937  */
6938
6939 /**
6940  * @class Roo.bootstrap.TableBody
6941  * @extends Roo.bootstrap.Component
6942  * Bootstrap TableBody class
6943  * @cfg {String} cls element class
6944  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6945  * @cfg {String} align Aligns the content inside the element
6946  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6947  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6948  * 
6949  * @constructor
6950  * Create a new TableBody
6951  * @param {Object} config The config object
6952  */
6953
6954 Roo.bootstrap.TableBody = function(config){
6955     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6956 };
6957
6958 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6959     
6960     cls: false,
6961     tag: false,
6962     align: false,
6963     charoff: false,
6964     valign: false,
6965     
6966     getAutoCreate : function(){
6967         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6968         
6969         cfg = {
6970             tag: 'tbody'
6971         };
6972             
6973         if (this.cls) {
6974             cfg.cls=this.cls
6975         }
6976         if(this.tag){
6977             cfg.tag = this.tag;
6978         }
6979         
6980         if(this.align){
6981             cfg.align = this.align;
6982         }
6983         if(this.charoff){
6984             cfg.charoff = this.charoff;
6985         }
6986         if(this.valign){
6987             cfg.valign = this.valign;
6988         }
6989         
6990         return cfg;
6991     }
6992     
6993     
6994 //    initEvents : function()
6995 //    {
6996 //        
6997 //        if(!this.store){
6998 //            return;
6999 //        }
7000 //        
7001 //        this.store = Roo.factory(this.store, Roo.data);
7002 //        this.store.on('load', this.onLoad, this);
7003 //        
7004 //        this.store.load();
7005 //        
7006 //    },
7007 //    
7008 //    onLoad: function () 
7009 //    {   
7010 //        this.fireEvent('load', this);
7011 //    }
7012 //    
7013 //   
7014 });
7015
7016  
7017
7018  /*
7019  * Based on:
7020  * Ext JS Library 1.1.1
7021  * Copyright(c) 2006-2007, Ext JS, LLC.
7022  *
7023  * Originally Released Under LGPL - original licence link has changed is not relivant.
7024  *
7025  * Fork - LGPL
7026  * <script type="text/javascript">
7027  */
7028
7029 // as we use this in bootstrap.
7030 Roo.namespace('Roo.form');
7031  /**
7032  * @class Roo.form.Action
7033  * Internal Class used to handle form actions
7034  * @constructor
7035  * @param {Roo.form.BasicForm} el The form element or its id
7036  * @param {Object} config Configuration options
7037  */
7038
7039  
7040  
7041 // define the action interface
7042 Roo.form.Action = function(form, options){
7043     this.form = form;
7044     this.options = options || {};
7045 };
7046 /**
7047  * Client Validation Failed
7048  * @const 
7049  */
7050 Roo.form.Action.CLIENT_INVALID = 'client';
7051 /**
7052  * Server Validation Failed
7053  * @const 
7054  */
7055 Roo.form.Action.SERVER_INVALID = 'server';
7056  /**
7057  * Connect to Server Failed
7058  * @const 
7059  */
7060 Roo.form.Action.CONNECT_FAILURE = 'connect';
7061 /**
7062  * Reading Data from Server Failed
7063  * @const 
7064  */
7065 Roo.form.Action.LOAD_FAILURE = 'load';
7066
7067 Roo.form.Action.prototype = {
7068     type : 'default',
7069     failureType : undefined,
7070     response : undefined,
7071     result : undefined,
7072
7073     // interface method
7074     run : function(options){
7075
7076     },
7077
7078     // interface method
7079     success : function(response){
7080
7081     },
7082
7083     // interface method
7084     handleResponse : function(response){
7085
7086     },
7087
7088     // default connection failure
7089     failure : function(response){
7090         
7091         this.response = response;
7092         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7093         this.form.afterAction(this, false);
7094     },
7095
7096     processResponse : function(response){
7097         this.response = response;
7098         if(!response.responseText){
7099             return true;
7100         }
7101         this.result = this.handleResponse(response);
7102         return this.result;
7103     },
7104
7105     // utility functions used internally
7106     getUrl : function(appendParams){
7107         var url = this.options.url || this.form.url || this.form.el.dom.action;
7108         if(appendParams){
7109             var p = this.getParams();
7110             if(p){
7111                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7112             }
7113         }
7114         return url;
7115     },
7116
7117     getMethod : function(){
7118         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7119     },
7120
7121     getParams : function(){
7122         var bp = this.form.baseParams;
7123         var p = this.options.params;
7124         if(p){
7125             if(typeof p == "object"){
7126                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7127             }else if(typeof p == 'string' && bp){
7128                 p += '&' + Roo.urlEncode(bp);
7129             }
7130         }else if(bp){
7131             p = Roo.urlEncode(bp);
7132         }
7133         return p;
7134     },
7135
7136     createCallback : function(){
7137         return {
7138             success: this.success,
7139             failure: this.failure,
7140             scope: this,
7141             timeout: (this.form.timeout*1000),
7142             upload: this.form.fileUpload ? this.success : undefined
7143         };
7144     }
7145 };
7146
7147 Roo.form.Action.Submit = function(form, options){
7148     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7149 };
7150
7151 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7152     type : 'submit',
7153
7154     haveProgress : false,
7155     uploadComplete : false,
7156     
7157     // uploadProgress indicator.
7158     uploadProgress : function()
7159     {
7160         if (!this.form.progressUrl) {
7161             return;
7162         }
7163         
7164         if (!this.haveProgress) {
7165             Roo.MessageBox.progress("Uploading", "Uploading");
7166         }
7167         if (this.uploadComplete) {
7168            Roo.MessageBox.hide();
7169            return;
7170         }
7171         
7172         this.haveProgress = true;
7173    
7174         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7175         
7176         var c = new Roo.data.Connection();
7177         c.request({
7178             url : this.form.progressUrl,
7179             params: {
7180                 id : uid
7181             },
7182             method: 'GET',
7183             success : function(req){
7184                //console.log(data);
7185                 var rdata = false;
7186                 var edata;
7187                 try  {
7188                    rdata = Roo.decode(req.responseText)
7189                 } catch (e) {
7190                     Roo.log("Invalid data from server..");
7191                     Roo.log(edata);
7192                     return;
7193                 }
7194                 if (!rdata || !rdata.success) {
7195                     Roo.log(rdata);
7196                     Roo.MessageBox.alert(Roo.encode(rdata));
7197                     return;
7198                 }
7199                 var data = rdata.data;
7200                 
7201                 if (this.uploadComplete) {
7202                    Roo.MessageBox.hide();
7203                    return;
7204                 }
7205                    
7206                 if (data){
7207                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7208                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7209                     );
7210                 }
7211                 this.uploadProgress.defer(2000,this);
7212             },
7213        
7214             failure: function(data) {
7215                 Roo.log('progress url failed ');
7216                 Roo.log(data);
7217             },
7218             scope : this
7219         });
7220            
7221     },
7222     
7223     
7224     run : function()
7225     {
7226         // run get Values on the form, so it syncs any secondary forms.
7227         this.form.getValues();
7228         
7229         var o = this.options;
7230         var method = this.getMethod();
7231         var isPost = method == 'POST';
7232         if(o.clientValidation === false || this.form.isValid()){
7233             
7234             if (this.form.progressUrl) {
7235                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7236                     (new Date() * 1) + '' + Math.random());
7237                     
7238             } 
7239             
7240             
7241             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7242                 form:this.form.el.dom,
7243                 url:this.getUrl(!isPost),
7244                 method: method,
7245                 params:isPost ? this.getParams() : null,
7246                 isUpload: this.form.fileUpload
7247             }));
7248             
7249             this.uploadProgress();
7250
7251         }else if (o.clientValidation !== false){ // client validation failed
7252             this.failureType = Roo.form.Action.CLIENT_INVALID;
7253             this.form.afterAction(this, false);
7254         }
7255     },
7256
7257     success : function(response)
7258     {
7259         this.uploadComplete= true;
7260         if (this.haveProgress) {
7261             Roo.MessageBox.hide();
7262         }
7263         
7264         
7265         var result = this.processResponse(response);
7266         if(result === true || result.success){
7267             this.form.afterAction(this, true);
7268             return;
7269         }
7270         if(result.errors){
7271             this.form.markInvalid(result.errors);
7272             this.failureType = Roo.form.Action.SERVER_INVALID;
7273         }
7274         this.form.afterAction(this, false);
7275     },
7276     failure : function(response)
7277     {
7278         this.uploadComplete= true;
7279         if (this.haveProgress) {
7280             Roo.MessageBox.hide();
7281         }
7282         
7283         this.response = response;
7284         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7285         this.form.afterAction(this, false);
7286     },
7287     
7288     handleResponse : function(response){
7289         if(this.form.errorReader){
7290             var rs = this.form.errorReader.read(response);
7291             var errors = [];
7292             if(rs.records){
7293                 for(var i = 0, len = rs.records.length; i < len; i++) {
7294                     var r = rs.records[i];
7295                     errors[i] = r.data;
7296                 }
7297             }
7298             if(errors.length < 1){
7299                 errors = null;
7300             }
7301             return {
7302                 success : rs.success,
7303                 errors : errors
7304             };
7305         }
7306         var ret = false;
7307         try {
7308             ret = Roo.decode(response.responseText);
7309         } catch (e) {
7310             ret = {
7311                 success: false,
7312                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7313                 errors : []
7314             };
7315         }
7316         return ret;
7317         
7318     }
7319 });
7320
7321
7322 Roo.form.Action.Load = function(form, options){
7323     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7324     this.reader = this.form.reader;
7325 };
7326
7327 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7328     type : 'load',
7329
7330     run : function(){
7331         
7332         Roo.Ajax.request(Roo.apply(
7333                 this.createCallback(), {
7334                     method:this.getMethod(),
7335                     url:this.getUrl(false),
7336                     params:this.getParams()
7337         }));
7338     },
7339
7340     success : function(response){
7341         
7342         var result = this.processResponse(response);
7343         if(result === true || !result.success || !result.data){
7344             this.failureType = Roo.form.Action.LOAD_FAILURE;
7345             this.form.afterAction(this, false);
7346             return;
7347         }
7348         this.form.clearInvalid();
7349         this.form.setValues(result.data);
7350         this.form.afterAction(this, true);
7351     },
7352
7353     handleResponse : function(response){
7354         if(this.form.reader){
7355             var rs = this.form.reader.read(response);
7356             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7357             return {
7358                 success : rs.success,
7359                 data : data
7360             };
7361         }
7362         return Roo.decode(response.responseText);
7363     }
7364 });
7365
7366 Roo.form.Action.ACTION_TYPES = {
7367     'load' : Roo.form.Action.Load,
7368     'submit' : Roo.form.Action.Submit
7369 };/*
7370  * - LGPL
7371  *
7372  * form
7373  * 
7374  */
7375
7376 /**
7377  * @class Roo.bootstrap.Form
7378  * @extends Roo.bootstrap.Component
7379  * Bootstrap Form class
7380  * @cfg {String} method  GET | POST (default POST)
7381  * @cfg {String} labelAlign top | left (default top)
7382  * @cfg {String} align left  | right - for navbars
7383  * @cfg {Boolean} loadMask load mask when submit (default true)
7384
7385  * 
7386  * @constructor
7387  * Create a new Form
7388  * @param {Object} config The config object
7389  */
7390
7391
7392 Roo.bootstrap.Form = function(config){
7393     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7394     this.addEvents({
7395         /**
7396          * @event clientvalidation
7397          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7398          * @param {Form} this
7399          * @param {Boolean} valid true if the form has passed client-side validation
7400          */
7401         clientvalidation: true,
7402         /**
7403          * @event beforeaction
7404          * Fires before any action is performed. Return false to cancel the action.
7405          * @param {Form} this
7406          * @param {Action} action The action to be performed
7407          */
7408         beforeaction: true,
7409         /**
7410          * @event actionfailed
7411          * Fires when an action fails.
7412          * @param {Form} this
7413          * @param {Action} action The action that failed
7414          */
7415         actionfailed : true,
7416         /**
7417          * @event actioncomplete
7418          * Fires when an action is completed.
7419          * @param {Form} this
7420          * @param {Action} action The action that completed
7421          */
7422         actioncomplete : true
7423     });
7424     
7425 };
7426
7427 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7428       
7429      /**
7430      * @cfg {String} method
7431      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7432      */
7433     method : 'POST',
7434     /**
7435      * @cfg {String} url
7436      * The URL to use for form actions if one isn't supplied in the action options.
7437      */
7438     /**
7439      * @cfg {Boolean} fileUpload
7440      * Set to true if this form is a file upload.
7441      */
7442      
7443     /**
7444      * @cfg {Object} baseParams
7445      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7446      */
7447       
7448     /**
7449      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7450      */
7451     timeout: 30,
7452     /**
7453      * @cfg {Sting} align (left|right) for navbar forms
7454      */
7455     align : 'left',
7456
7457     // private
7458     activeAction : null,
7459  
7460     /**
7461      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7462      * element by passing it or its id or mask the form itself by passing in true.
7463      * @type Mixed
7464      */
7465     waitMsgTarget : false,
7466     
7467     loadMask : true,
7468     
7469     getAutoCreate : function(){
7470         
7471         var cfg = {
7472             tag: 'form',
7473             method : this.method || 'POST',
7474             id : this.id || Roo.id(),
7475             cls : ''
7476         };
7477         if (this.parent().xtype.match(/^Nav/)) {
7478             cfg.cls = 'navbar-form navbar-' + this.align;
7479             
7480         }
7481         
7482         if (this.labelAlign == 'left' ) {
7483             cfg.cls += ' form-horizontal';
7484         }
7485         
7486         
7487         return cfg;
7488     },
7489     initEvents : function()
7490     {
7491         this.el.on('submit', this.onSubmit, this);
7492         // this was added as random key presses on the form where triggering form submit.
7493         this.el.on('keypress', function(e) {
7494             if (e.getCharCode() != 13) {
7495                 return true;
7496             }
7497             // we might need to allow it for textareas.. and some other items.
7498             // check e.getTarget().
7499             
7500             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7501                 return true;
7502             }
7503         
7504             Roo.log("keypress blocked");
7505             
7506             e.preventDefault();
7507             return false;
7508         });
7509         
7510     },
7511     // private
7512     onSubmit : function(e){
7513         e.stopEvent();
7514     },
7515     
7516      /**
7517      * Returns true if client-side validation on the form is successful.
7518      * @return Boolean
7519      */
7520     isValid : function(){
7521         var items = this.getItems();
7522         var valid = true;
7523         items.each(function(f){
7524            if(!f.validate()){
7525                valid = false;
7526                
7527            }
7528         });
7529         return valid;
7530     },
7531     /**
7532      * Returns true if any fields in this form have changed since their original load.
7533      * @return Boolean
7534      */
7535     isDirty : function(){
7536         var dirty = false;
7537         var items = this.getItems();
7538         items.each(function(f){
7539            if(f.isDirty()){
7540                dirty = true;
7541                return false;
7542            }
7543            return true;
7544         });
7545         return dirty;
7546     },
7547      /**
7548      * Performs a predefined action (submit or load) or custom actions you define on this form.
7549      * @param {String} actionName The name of the action type
7550      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7551      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7552      * accept other config options):
7553      * <pre>
7554 Property          Type             Description
7555 ----------------  ---------------  ----------------------------------------------------------------------------------
7556 url               String           The url for the action (defaults to the form's url)
7557 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7558 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7559 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7560                                    validate the form on the client (defaults to false)
7561      * </pre>
7562      * @return {BasicForm} this
7563      */
7564     doAction : function(action, options){
7565         if(typeof action == 'string'){
7566             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7567         }
7568         if(this.fireEvent('beforeaction', this, action) !== false){
7569             this.beforeAction(action);
7570             action.run.defer(100, action);
7571         }
7572         return this;
7573     },
7574     
7575     // private
7576     beforeAction : function(action){
7577         var o = action.options;
7578         
7579         if(this.loadMask){
7580             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7581         }
7582         // not really supported yet.. ??
7583         
7584         //if(this.waitMsgTarget === true){
7585         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7586         //}else if(this.waitMsgTarget){
7587         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7588         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7589         //}else {
7590         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7591        // }
7592          
7593     },
7594
7595     // private
7596     afterAction : function(action, success){
7597         this.activeAction = null;
7598         var o = action.options;
7599         
7600         //if(this.waitMsgTarget === true){
7601             this.el.unmask();
7602         //}else if(this.waitMsgTarget){
7603         //    this.waitMsgTarget.unmask();
7604         //}else{
7605         //    Roo.MessageBox.updateProgress(1);
7606         //    Roo.MessageBox.hide();
7607        // }
7608         // 
7609         if(success){
7610             if(o.reset){
7611                 this.reset();
7612             }
7613             Roo.callback(o.success, o.scope, [this, action]);
7614             this.fireEvent('actioncomplete', this, action);
7615             
7616         }else{
7617             
7618             // failure condition..
7619             // we have a scenario where updates need confirming.
7620             // eg. if a locking scenario exists..
7621             // we look for { errors : { needs_confirm : true }} in the response.
7622             if (
7623                 (typeof(action.result) != 'undefined')  &&
7624                 (typeof(action.result.errors) != 'undefined')  &&
7625                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7626            ){
7627                 var _t = this;
7628                 Roo.log("not supported yet");
7629                  /*
7630                 
7631                 Roo.MessageBox.confirm(
7632                     "Change requires confirmation",
7633                     action.result.errorMsg,
7634                     function(r) {
7635                         if (r != 'yes') {
7636                             return;
7637                         }
7638                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7639                     }
7640                     
7641                 );
7642                 */
7643                 
7644                 
7645                 return;
7646             }
7647             
7648             Roo.callback(o.failure, o.scope, [this, action]);
7649             // show an error message if no failed handler is set..
7650             if (!this.hasListener('actionfailed')) {
7651                 Roo.log("need to add dialog support");
7652                 /*
7653                 Roo.MessageBox.alert("Error",
7654                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7655                         action.result.errorMsg :
7656                         "Saving Failed, please check your entries or try again"
7657                 );
7658                 */
7659             }
7660             
7661             this.fireEvent('actionfailed', this, action);
7662         }
7663         
7664     },
7665     /**
7666      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7667      * @param {String} id The value to search for
7668      * @return Field
7669      */
7670     findField : function(id){
7671         var items = this.getItems();
7672         var field = items.get(id);
7673         if(!field){
7674              items.each(function(f){
7675                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7676                     field = f;
7677                     return false;
7678                 }
7679                 return true;
7680             });
7681         }
7682         return field || null;
7683     },
7684      /**
7685      * Mark fields in this form invalid in bulk.
7686      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7687      * @return {BasicForm} this
7688      */
7689     markInvalid : function(errors){
7690         if(errors instanceof Array){
7691             for(var i = 0, len = errors.length; i < len; i++){
7692                 var fieldError = errors[i];
7693                 var f = this.findField(fieldError.id);
7694                 if(f){
7695                     f.markInvalid(fieldError.msg);
7696                 }
7697             }
7698         }else{
7699             var field, id;
7700             for(id in errors){
7701                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7702                     field.markInvalid(errors[id]);
7703                 }
7704             }
7705         }
7706         //Roo.each(this.childForms || [], function (f) {
7707         //    f.markInvalid(errors);
7708         //});
7709         
7710         return this;
7711     },
7712
7713     /**
7714      * Set values for fields in this form in bulk.
7715      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7716      * @return {BasicForm} this
7717      */
7718     setValues : function(values){
7719         if(values instanceof Array){ // array of objects
7720             for(var i = 0, len = values.length; i < len; i++){
7721                 var v = values[i];
7722                 var f = this.findField(v.id);
7723                 if(f){
7724                     f.setValue(v.value);
7725                     if(this.trackResetOnLoad){
7726                         f.originalValue = f.getValue();
7727                     }
7728                 }
7729             }
7730         }else{ // object hash
7731             var field, id;
7732             for(id in values){
7733                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7734                     
7735                     if (field.setFromData && 
7736                         field.valueField && 
7737                         field.displayField &&
7738                         // combos' with local stores can 
7739                         // be queried via setValue()
7740                         // to set their value..
7741                         (field.store && !field.store.isLocal)
7742                         ) {
7743                         // it's a combo
7744                         var sd = { };
7745                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7746                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7747                         field.setFromData(sd);
7748                         
7749                     } else {
7750                         field.setValue(values[id]);
7751                     }
7752                     
7753                     
7754                     if(this.trackResetOnLoad){
7755                         field.originalValue = field.getValue();
7756                     }
7757                 }
7758             }
7759         }
7760          
7761         //Roo.each(this.childForms || [], function (f) {
7762         //    f.setValues(values);
7763         //});
7764                 
7765         return this;
7766     },
7767
7768     /**
7769      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7770      * they are returned as an array.
7771      * @param {Boolean} asString
7772      * @return {Object}
7773      */
7774     getValues : function(asString){
7775         //if (this.childForms) {
7776             // copy values from the child forms
7777         //    Roo.each(this.childForms, function (f) {
7778         //        this.setValues(f.getValues());
7779         //    }, this);
7780         //}
7781         
7782         
7783         
7784         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7785         if(asString === true){
7786             return fs;
7787         }
7788         return Roo.urlDecode(fs);
7789     },
7790     
7791     /**
7792      * Returns the fields in this form as an object with key/value pairs. 
7793      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7794      * @return {Object}
7795      */
7796     getFieldValues : function(with_hidden)
7797     {
7798         var items = this.getItems();
7799         var ret = {};
7800         items.each(function(f){
7801             if (!f.getName()) {
7802                 return;
7803             }
7804             var v = f.getValue();
7805             if (f.inputType =='radio') {
7806                 if (typeof(ret[f.getName()]) == 'undefined') {
7807                     ret[f.getName()] = ''; // empty..
7808                 }
7809                 
7810                 if (!f.el.dom.checked) {
7811                     return;
7812                     
7813                 }
7814                 v = f.el.dom.value;
7815                 
7816             }
7817             
7818             // not sure if this supported any more..
7819             if ((typeof(v) == 'object') && f.getRawValue) {
7820                 v = f.getRawValue() ; // dates..
7821             }
7822             // combo boxes where name != hiddenName...
7823             if (f.name != f.getName()) {
7824                 ret[f.name] = f.getRawValue();
7825             }
7826             ret[f.getName()] = v;
7827         });
7828         
7829         return ret;
7830     },
7831
7832     /**
7833      * Clears all invalid messages in this form.
7834      * @return {BasicForm} this
7835      */
7836     clearInvalid : function(){
7837         var items = this.getItems();
7838         
7839         items.each(function(f){
7840            f.clearInvalid();
7841         });
7842         
7843         
7844         
7845         return this;
7846     },
7847
7848     /**
7849      * Resets this form.
7850      * @return {BasicForm} this
7851      */
7852     reset : function(){
7853         var items = this.getItems();
7854         items.each(function(f){
7855             f.reset();
7856         });
7857         
7858         Roo.each(this.childForms || [], function (f) {
7859             f.reset();
7860         });
7861        
7862         
7863         return this;
7864     },
7865     getItems : function()
7866     {
7867         var r=new Roo.util.MixedCollection(false, function(o){
7868             return o.id || (o.id = Roo.id());
7869         });
7870         var iter = function(el) {
7871             if (el.inputEl) {
7872                 r.add(el);
7873             }
7874             if (!el.items) {
7875                 return;
7876             }
7877             Roo.each(el.items,function(e) {
7878                 iter(e);
7879             });
7880             
7881             
7882         };
7883         
7884         iter(this);
7885         return r;
7886         
7887         
7888         
7889         
7890     }
7891     
7892 });
7893
7894  
7895 /*
7896  * Based on:
7897  * Ext JS Library 1.1.1
7898  * Copyright(c) 2006-2007, Ext JS, LLC.
7899  *
7900  * Originally Released Under LGPL - original licence link has changed is not relivant.
7901  *
7902  * Fork - LGPL
7903  * <script type="text/javascript">
7904  */
7905 /**
7906  * @class Roo.form.VTypes
7907  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7908  * @singleton
7909  */
7910 Roo.form.VTypes = function(){
7911     // closure these in so they are only created once.
7912     var alpha = /^[a-zA-Z_]+$/;
7913     var alphanum = /^[a-zA-Z0-9_]+$/;
7914     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7915     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7916
7917     // All these messages and functions are configurable
7918     return {
7919         /**
7920          * The function used to validate email addresses
7921          * @param {String} value The email address
7922          */
7923         'email' : function(v){
7924             return email.test(v);
7925         },
7926         /**
7927          * The error text to display when the email validation function returns false
7928          * @type String
7929          */
7930         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7931         /**
7932          * The keystroke filter mask to be applied on email input
7933          * @type RegExp
7934          */
7935         'emailMask' : /[a-z0-9_\.\-@]/i,
7936
7937         /**
7938          * The function used to validate URLs
7939          * @param {String} value The URL
7940          */
7941         'url' : function(v){
7942             return url.test(v);
7943         },
7944         /**
7945          * The error text to display when the url validation function returns false
7946          * @type String
7947          */
7948         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7949         
7950         /**
7951          * The function used to validate alpha values
7952          * @param {String} value The value
7953          */
7954         'alpha' : function(v){
7955             return alpha.test(v);
7956         },
7957         /**
7958          * The error text to display when the alpha validation function returns false
7959          * @type String
7960          */
7961         'alphaText' : 'This field should only contain letters and _',
7962         /**
7963          * The keystroke filter mask to be applied on alpha input
7964          * @type RegExp
7965          */
7966         'alphaMask' : /[a-z_]/i,
7967
7968         /**
7969          * The function used to validate alphanumeric values
7970          * @param {String} value The value
7971          */
7972         'alphanum' : function(v){
7973             return alphanum.test(v);
7974         },
7975         /**
7976          * The error text to display when the alphanumeric validation function returns false
7977          * @type String
7978          */
7979         'alphanumText' : 'This field should only contain letters, numbers and _',
7980         /**
7981          * The keystroke filter mask to be applied on alphanumeric input
7982          * @type RegExp
7983          */
7984         'alphanumMask' : /[a-z0-9_]/i
7985     };
7986 }();/*
7987  * - LGPL
7988  *
7989  * Input
7990  * 
7991  */
7992
7993 /**
7994  * @class Roo.bootstrap.Input
7995  * @extends Roo.bootstrap.Component
7996  * Bootstrap Input class
7997  * @cfg {Boolean} disabled is it disabled
7998  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7999  * @cfg {String} name name of the input
8000  * @cfg {string} fieldLabel - the label associated
8001  * @cfg {string} placeholder - placeholder to put in text.
8002  * @cfg {string}  before - input group add on before
8003  * @cfg {string} after - input group add on after
8004  * @cfg {string} size - (lg|sm) or leave empty..
8005  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8006  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8007  * @cfg {Number} md colspan out of 12 for computer-sized screens
8008  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8009  * @cfg {string} value default value of the input
8010  * @cfg {Number} labelWidth set the width of label (0-12)
8011  * @cfg {String} labelAlign (top|left)
8012  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8013  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8014
8015  * @cfg {String} align (left|center|right) Default left
8016  * @cfg {Boolean} forceFeedback (true|false) Default false
8017  * 
8018  * 
8019  * 
8020  * 
8021  * @constructor
8022  * Create a new Input
8023  * @param {Object} config The config object
8024  */
8025
8026 Roo.bootstrap.Input = function(config){
8027     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8028    
8029         this.addEvents({
8030             /**
8031              * @event focus
8032              * Fires when this field receives input focus.
8033              * @param {Roo.form.Field} this
8034              */
8035             focus : true,
8036             /**
8037              * @event blur
8038              * Fires when this field loses input focus.
8039              * @param {Roo.form.Field} this
8040              */
8041             blur : true,
8042             /**
8043              * @event specialkey
8044              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8045              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8046              * @param {Roo.form.Field} this
8047              * @param {Roo.EventObject} e The event object
8048              */
8049             specialkey : true,
8050             /**
8051              * @event change
8052              * Fires just before the field blurs if the field value has changed.
8053              * @param {Roo.form.Field} this
8054              * @param {Mixed} newValue The new value
8055              * @param {Mixed} oldValue The original value
8056              */
8057             change : true,
8058             /**
8059              * @event invalid
8060              * Fires after the field has been marked as invalid.
8061              * @param {Roo.form.Field} this
8062              * @param {String} msg The validation message
8063              */
8064             invalid : true,
8065             /**
8066              * @event valid
8067              * Fires after the field has been validated with no errors.
8068              * @param {Roo.form.Field} this
8069              */
8070             valid : true,
8071              /**
8072              * @event keyup
8073              * Fires after the key up
8074              * @param {Roo.form.Field} this
8075              * @param {Roo.EventObject}  e The event Object
8076              */
8077             keyup : true
8078         });
8079 };
8080
8081 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8082      /**
8083      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8084       automatic validation (defaults to "keyup").
8085      */
8086     validationEvent : "keyup",
8087      /**
8088      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8089      */
8090     validateOnBlur : true,
8091     /**
8092      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8093      */
8094     validationDelay : 250,
8095      /**
8096      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8097      */
8098     focusClass : "x-form-focus",  // not needed???
8099     
8100        
8101     /**
8102      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8103      */
8104     invalidClass : "has-warning",
8105     
8106     /**
8107      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8108      */
8109     validClass : "has-success",
8110     
8111     /**
8112      * @cfg {Boolean} hasFeedback (true|false) default true
8113      */
8114     hasFeedback : true,
8115     
8116     /**
8117      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8118      */
8119     invalidFeedbackClass : "glyphicon-warning-sign",
8120     
8121     /**
8122      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8123      */
8124     validFeedbackClass : "glyphicon-ok",
8125     
8126     /**
8127      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8128      */
8129     selectOnFocus : false,
8130     
8131      /**
8132      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8133      */
8134     maskRe : null,
8135        /**
8136      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8137      */
8138     vtype : null,
8139     
8140       /**
8141      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8142      */
8143     disableKeyFilter : false,
8144     
8145        /**
8146      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8147      */
8148     disabled : false,
8149      /**
8150      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8151      */
8152     allowBlank : true,
8153     /**
8154      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8155      */
8156     blankText : "This field is required",
8157     
8158      /**
8159      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8160      */
8161     minLength : 0,
8162     /**
8163      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8164      */
8165     maxLength : Number.MAX_VALUE,
8166     /**
8167      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8168      */
8169     minLengthText : "The minimum length for this field is {0}",
8170     /**
8171      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8172      */
8173     maxLengthText : "The maximum length for this field is {0}",
8174   
8175     
8176     /**
8177      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8178      * If available, this function will be called only after the basic validators all return true, and will be passed the
8179      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8180      */
8181     validator : null,
8182     /**
8183      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8184      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8185      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8186      */
8187     regex : null,
8188     /**
8189      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8190      */
8191     regexText : "",
8192     
8193     autocomplete: false,
8194     
8195     
8196     fieldLabel : '',
8197     inputType : 'text',
8198     
8199     name : false,
8200     placeholder: false,
8201     before : false,
8202     after : false,
8203     size : false,
8204     hasFocus : false,
8205     preventMark: false,
8206     isFormField : true,
8207     value : '',
8208     labelWidth : 2,
8209     labelAlign : false,
8210     readOnly : false,
8211     align : false,
8212     formatedValue : false,
8213     forceFeedback : false,
8214     
8215     parentLabelAlign : function()
8216     {
8217         var parent = this;
8218         while (parent.parent()) {
8219             parent = parent.parent();
8220             if (typeof(parent.labelAlign) !='undefined') {
8221                 return parent.labelAlign;
8222             }
8223         }
8224         return 'left';
8225         
8226     },
8227     
8228     getAutoCreate : function(){
8229         
8230         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8231         
8232         var id = Roo.id();
8233         
8234         var cfg = {};
8235         
8236        
8237         
8238         if(this.inputType != 'hidden'){
8239             cfg.cls = 'form-group' //input-group
8240         }
8241         
8242         var input =  {
8243             tag: 'input',
8244             id : id,
8245             type : this.inputType,
8246             value : this.value,
8247             cls : 'form-control',
8248             placeholder : this.placeholder || '',
8249             autocomplete : this.autocomplete || 'new-password'
8250         };
8251         
8252         
8253         if(this.align){
8254             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8255         }
8256         
8257         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8258             input.maxLength = this.maxLength;
8259         }
8260         
8261         if (this.disabled) {
8262             input.disabled=true;
8263         }
8264         
8265         if (this.readOnly) {
8266             input.readonly=true;
8267         }
8268         
8269         if (this.name) {
8270             input.name = this.name;
8271         }
8272         if (this.size) {
8273             input.cls += ' input-' + this.size;
8274         }
8275         var settings=this;
8276         ['xs','sm','md','lg'].map(function(size){
8277             if (settings[size]) {
8278                 cfg.cls += ' col-' + size + '-' + settings[size];
8279             }
8280         });
8281         
8282         var inputblock = input;
8283         
8284         var feedback = {
8285             tag: 'span',
8286             cls: 'glyphicon form-control-feedback'
8287         };
8288             
8289         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8290             
8291             inputblock = {
8292                 cls : 'has-feedback',
8293                 cn :  [
8294                     input,
8295                     feedback
8296                 ] 
8297             };  
8298         }
8299         
8300         if (this.before || this.after) {
8301             
8302             inputblock = {
8303                 cls : 'input-group',
8304                 cn :  [] 
8305             };
8306             
8307             if (this.before && typeof(this.before) == 'string') {
8308                 
8309                 inputblock.cn.push({
8310                     tag :'span',
8311                     cls : 'roo-input-before input-group-addon',
8312                     html : this.before
8313                 });
8314             }
8315             if (this.before && typeof(this.before) == 'object') {
8316                 this.before = Roo.factory(this.before);
8317                 
8318                 inputblock.cn.push({
8319                     tag :'span',
8320                     cls : 'roo-input-before input-group-' +
8321                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8322                 });
8323             }
8324             
8325             inputblock.cn.push(input);
8326             
8327             if (this.after && typeof(this.after) == 'string') {
8328                 inputblock.cn.push({
8329                     tag :'span',
8330                     cls : 'roo-input-after input-group-addon',
8331                     html : this.after
8332                 });
8333             }
8334             if (this.after && typeof(this.after) == 'object') {
8335                 this.after = Roo.factory(this.after);
8336                 
8337                 inputblock.cn.push({
8338                     tag :'span',
8339                     cls : 'roo-input-after input-group-' +
8340                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8341                 });
8342             }
8343             
8344             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8345                 inputblock.cls += ' has-feedback';
8346                 inputblock.cn.push(feedback);
8347             }
8348         };
8349         
8350         if (align ==='left' && this.fieldLabel.length) {
8351                 
8352                 cfg.cn = [
8353                     
8354                     {
8355                         tag: 'label',
8356                         'for' :  id,
8357                         cls : 'control-label col-sm-' + this.labelWidth,
8358                         html : this.fieldLabel
8359                         
8360                     },
8361                     {
8362                         cls : "col-sm-" + (12 - this.labelWidth), 
8363                         cn: [
8364                             inputblock
8365                         ]
8366                     }
8367                     
8368                 ];
8369         } else if ( this.fieldLabel.length) {
8370                 
8371                  cfg.cn = [
8372                    
8373                     {
8374                         tag: 'label',
8375                         //cls : 'input-group-addon',
8376                         html : this.fieldLabel
8377                         
8378                     },
8379                     
8380                     inputblock
8381                     
8382                 ];
8383
8384         } else {
8385             
8386                 cfg.cn = [
8387                     
8388                         inputblock
8389                     
8390                 ];
8391                 
8392                 
8393         };
8394         
8395         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8396            cfg.cls += ' navbar-form';
8397         }
8398         if (this.parentType === 'NavGroup') {
8399            cfg.cls += ' navbar-form';
8400            cfg.tag = 'li';
8401         }
8402         return cfg;
8403         
8404     },
8405     /**
8406      * return the real input element.
8407      */
8408     inputEl: function ()
8409     {
8410         return this.el.select('input.form-control',true).first();
8411     },
8412     
8413     tooltipEl : function()
8414     {
8415         return this.inputEl();
8416     },
8417     
8418     setDisabled : function(v)
8419     {
8420         var i  = this.inputEl().dom;
8421         if (!v) {
8422             i.removeAttribute('disabled');
8423             return;
8424             
8425         }
8426         i.setAttribute('disabled','true');
8427     },
8428     initEvents : function()
8429     {
8430           
8431         this.inputEl().on("keydown" , this.fireKey,  this);
8432         this.inputEl().on("focus", this.onFocus,  this);
8433         this.inputEl().on("blur", this.onBlur,  this);
8434         
8435         this.inputEl().relayEvent('keyup', this);
8436  
8437         // reference to original value for reset
8438         this.originalValue = this.getValue();
8439         //Roo.form.TextField.superclass.initEvents.call(this);
8440         if(this.validationEvent == 'keyup'){
8441             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8442             this.inputEl().on('keyup', this.filterValidation, this);
8443         }
8444         else if(this.validationEvent !== false){
8445             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8446         }
8447         
8448         if(this.selectOnFocus){
8449             this.on("focus", this.preFocus, this);
8450             
8451         }
8452         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8453             this.inputEl().on("keypress", this.filterKeys, this);
8454         }
8455        /* if(this.grow){
8456             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8457             this.el.on("click", this.autoSize,  this);
8458         }
8459         */
8460         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8461             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8462         }
8463         
8464         if (typeof(this.before) == 'object') {
8465             this.before.render(this.el.select('.roo-input-before',true).first());
8466         }
8467         if (typeof(this.after) == 'object') {
8468             this.after.render(this.el.select('.roo-input-after',true).first());
8469         }
8470         
8471         
8472     },
8473     filterValidation : function(e){
8474         if(!e.isNavKeyPress()){
8475             this.validationTask.delay(this.validationDelay);
8476         }
8477     },
8478      /**
8479      * Validates the field value
8480      * @return {Boolean} True if the value is valid, else false
8481      */
8482     validate : function(){
8483         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8484         if(this.disabled || this.validateValue(this.getRawValue())){
8485             this.markValid();
8486             return true;
8487         }
8488         
8489         this.markInvalid();
8490         return false;
8491     },
8492     
8493     
8494     /**
8495      * Validates a value according to the field's validation rules and marks the field as invalid
8496      * if the validation fails
8497      * @param {Mixed} value The value to validate
8498      * @return {Boolean} True if the value is valid, else false
8499      */
8500     validateValue : function(value){
8501         if(value.length < 1)  { // if it's blank
8502             if(this.allowBlank){
8503                 return true;
8504             }
8505             return false;
8506         }
8507         
8508         if(value.length < this.minLength){
8509             return false;
8510         }
8511         if(value.length > this.maxLength){
8512             return false;
8513         }
8514         if(this.vtype){
8515             var vt = Roo.form.VTypes;
8516             if(!vt[this.vtype](value, this)){
8517                 return false;
8518             }
8519         }
8520         if(typeof this.validator == "function"){
8521             var msg = this.validator(value);
8522             if(msg !== true){
8523                 return false;
8524             }
8525         }
8526         
8527         if(this.regex && !this.regex.test(value)){
8528             return false;
8529         }
8530         
8531         return true;
8532     },
8533
8534     
8535     
8536      // private
8537     fireKey : function(e){
8538         //Roo.log('field ' + e.getKey());
8539         if(e.isNavKeyPress()){
8540             this.fireEvent("specialkey", this, e);
8541         }
8542     },
8543     focus : function (selectText){
8544         if(this.rendered){
8545             this.inputEl().focus();
8546             if(selectText === true){
8547                 this.inputEl().dom.select();
8548             }
8549         }
8550         return this;
8551     } ,
8552     
8553     onFocus : function(){
8554         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8555            // this.el.addClass(this.focusClass);
8556         }
8557         if(!this.hasFocus){
8558             this.hasFocus = true;
8559             this.startValue = this.getValue();
8560             this.fireEvent("focus", this);
8561         }
8562     },
8563     
8564     beforeBlur : Roo.emptyFn,
8565
8566     
8567     // private
8568     onBlur : function(){
8569         this.beforeBlur();
8570         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8571             //this.el.removeClass(this.focusClass);
8572         }
8573         this.hasFocus = false;
8574         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8575             this.validate();
8576         }
8577         var v = this.getValue();
8578         if(String(v) !== String(this.startValue)){
8579             this.fireEvent('change', this, v, this.startValue);
8580         }
8581         this.fireEvent("blur", this);
8582     },
8583     
8584     /**
8585      * Resets the current field value to the originally loaded value and clears any validation messages
8586      */
8587     reset : function(){
8588         this.setValue(this.originalValue);
8589         this.validate();
8590     },
8591      /**
8592      * Returns the name of the field
8593      * @return {Mixed} name The name field
8594      */
8595     getName: function(){
8596         return this.name;
8597     },
8598      /**
8599      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8600      * @return {Mixed} value The field value
8601      */
8602     getValue : function(){
8603         
8604         var v = this.inputEl().getValue();
8605         
8606         return v;
8607     },
8608     /**
8609      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8610      * @return {Mixed} value The field value
8611      */
8612     getRawValue : function(){
8613         var v = this.inputEl().getValue();
8614         
8615         return v;
8616     },
8617     
8618     /**
8619      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8620      * @param {Mixed} value The value to set
8621      */
8622     setRawValue : function(v){
8623         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8624     },
8625     
8626     selectText : function(start, end){
8627         var v = this.getRawValue();
8628         if(v.length > 0){
8629             start = start === undefined ? 0 : start;
8630             end = end === undefined ? v.length : end;
8631             var d = this.inputEl().dom;
8632             if(d.setSelectionRange){
8633                 d.setSelectionRange(start, end);
8634             }else if(d.createTextRange){
8635                 var range = d.createTextRange();
8636                 range.moveStart("character", start);
8637                 range.moveEnd("character", v.length-end);
8638                 range.select();
8639             }
8640         }
8641     },
8642     
8643     /**
8644      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8645      * @param {Mixed} value The value to set
8646      */
8647     setValue : function(v){
8648         this.value = v;
8649         if(this.rendered){
8650             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8651             this.validate();
8652         }
8653     },
8654     
8655     /*
8656     processValue : function(value){
8657         if(this.stripCharsRe){
8658             var newValue = value.replace(this.stripCharsRe, '');
8659             if(newValue !== value){
8660                 this.setRawValue(newValue);
8661                 return newValue;
8662             }
8663         }
8664         return value;
8665     },
8666   */
8667     preFocus : function(){
8668         
8669         if(this.selectOnFocus){
8670             this.inputEl().dom.select();
8671         }
8672     },
8673     filterKeys : function(e){
8674         var k = e.getKey();
8675         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8676             return;
8677         }
8678         var c = e.getCharCode(), cc = String.fromCharCode(c);
8679         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8680             return;
8681         }
8682         if(!this.maskRe.test(cc)){
8683             e.stopEvent();
8684         }
8685     },
8686      /**
8687      * Clear any invalid styles/messages for this field
8688      */
8689     clearInvalid : function(){
8690         
8691         if(!this.el || this.preventMark){ // not rendered
8692             return;
8693         }
8694         
8695         var label = this.el.select('label', true).first();
8696         var icon = this.el.select('i.fa-star', true).first();
8697         
8698         if(label && icon){
8699             icon.remove();
8700         }
8701         
8702         this.el.removeClass(this.invalidClass);
8703         
8704         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8705             
8706             var feedback = this.el.select('.form-control-feedback', true).first();
8707             
8708             if(feedback){
8709                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8710             }
8711             
8712         }
8713         
8714         this.fireEvent('valid', this);
8715     },
8716     
8717      /**
8718      * Mark this field as valid
8719      */
8720     markValid : function()
8721     {
8722         if(!this.el  || this.preventMark){ // not rendered
8723             return;
8724         }
8725         
8726         this.el.removeClass([this.invalidClass, this.validClass]);
8727         
8728         var feedback = this.el.select('.form-control-feedback', true).first();
8729             
8730         if(feedback){
8731             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8732         }
8733
8734         if(this.disabled || this.allowBlank){
8735             return;
8736         }
8737         
8738         var formGroup = this.el.findParent('.form-group', false, true);
8739         
8740         if(formGroup){
8741             
8742             var label = formGroup.select('label', true).first();
8743             var icon = formGroup.select('i.fa-star', true).first();
8744             
8745             if(label && icon){
8746                 icon.remove();
8747             }
8748         }
8749         
8750         this.el.addClass(this.validClass);
8751         
8752         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8753             
8754             var feedback = this.el.select('.form-control-feedback', true).first();
8755             
8756             if(feedback){
8757                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8758                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8759             }
8760             
8761         }
8762         
8763         this.fireEvent('valid', this);
8764     },
8765     
8766      /**
8767      * Mark this field as invalid
8768      * @param {String} msg The validation message
8769      */
8770     markInvalid : function(msg)
8771     {
8772         if(!this.el  || this.preventMark){ // not rendered
8773             return;
8774         }
8775         
8776         this.el.removeClass([this.invalidClass, this.validClass]);
8777         
8778         var feedback = this.el.select('.form-control-feedback', true).first();
8779             
8780         if(feedback){
8781             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8782         }
8783
8784         if(this.disabled || this.allowBlank){
8785             return;
8786         }
8787         
8788         var formGroup = this.el.findParent('.form-group', false, true);
8789         
8790         if(formGroup){
8791             var label = formGroup.select('label', true).first();
8792             var icon = formGroup.select('i.fa-star', true).first();
8793
8794             if(!this.getValue().length && label && !icon){
8795                 this.el.findParent('.form-group', false, true).createChild({
8796                     tag : 'i',
8797                     cls : 'text-danger fa fa-lg fa-star',
8798                     tooltip : 'This field is required',
8799                     style : 'margin-right:5px;'
8800                 }, label, true);
8801             }
8802         }
8803         
8804         
8805         this.el.addClass(this.invalidClass);
8806         
8807         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8808             
8809             var feedback = this.el.select('.form-control-feedback', true).first();
8810             
8811             if(feedback){
8812                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8813                 
8814                 if(this.getValue().length || this.forceFeedback){
8815                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8816                 }
8817                 
8818             }
8819             
8820         }
8821         
8822         this.fireEvent('invalid', this, msg);
8823     },
8824     // private
8825     SafariOnKeyDown : function(event)
8826     {
8827         // this is a workaround for a password hang bug on chrome/ webkit.
8828         
8829         var isSelectAll = false;
8830         
8831         if(this.inputEl().dom.selectionEnd > 0){
8832             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8833         }
8834         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8835             event.preventDefault();
8836             this.setValue('');
8837             return;
8838         }
8839         
8840         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8841             
8842             event.preventDefault();
8843             // this is very hacky as keydown always get's upper case.
8844             //
8845             var cc = String.fromCharCode(event.getCharCode());
8846             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8847             
8848         }
8849     },
8850     adjustWidth : function(tag, w){
8851         tag = tag.toLowerCase();
8852         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8853             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8854                 if(tag == 'input'){
8855                     return w + 2;
8856                 }
8857                 if(tag == 'textarea'){
8858                     return w-2;
8859                 }
8860             }else if(Roo.isOpera){
8861                 if(tag == 'input'){
8862                     return w + 2;
8863                 }
8864                 if(tag == 'textarea'){
8865                     return w-2;
8866                 }
8867             }
8868         }
8869         return w;
8870     }
8871     
8872 });
8873
8874  
8875 /*
8876  * - LGPL
8877  *
8878  * Input
8879  * 
8880  */
8881
8882 /**
8883  * @class Roo.bootstrap.TextArea
8884  * @extends Roo.bootstrap.Input
8885  * Bootstrap TextArea class
8886  * @cfg {Number} cols Specifies the visible width of a text area
8887  * @cfg {Number} rows Specifies the visible number of lines in a text area
8888  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8889  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8890  * @cfg {string} html text
8891  * 
8892  * @constructor
8893  * Create a new TextArea
8894  * @param {Object} config The config object
8895  */
8896
8897 Roo.bootstrap.TextArea = function(config){
8898     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8899    
8900 };
8901
8902 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8903      
8904     cols : false,
8905     rows : 5,
8906     readOnly : false,
8907     warp : 'soft',
8908     resize : false,
8909     value: false,
8910     html: false,
8911     
8912     getAutoCreate : function(){
8913         
8914         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8915         
8916         var id = Roo.id();
8917         
8918         var cfg = {};
8919         
8920         var input =  {
8921             tag: 'textarea',
8922             id : id,
8923             warp : this.warp,
8924             rows : this.rows,
8925             value : this.value || '',
8926             html: this.html || '',
8927             cls : 'form-control',
8928             placeholder : this.placeholder || '' 
8929             
8930         };
8931         
8932         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8933             input.maxLength = this.maxLength;
8934         }
8935         
8936         if(this.resize){
8937             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8938         }
8939         
8940         if(this.cols){
8941             input.cols = this.cols;
8942         }
8943         
8944         if (this.readOnly) {
8945             input.readonly = true;
8946         }
8947         
8948         if (this.name) {
8949             input.name = this.name;
8950         }
8951         
8952         if (this.size) {
8953             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8954         }
8955         
8956         var settings=this;
8957         ['xs','sm','md','lg'].map(function(size){
8958             if (settings[size]) {
8959                 cfg.cls += ' col-' + size + '-' + settings[size];
8960             }
8961         });
8962         
8963         var inputblock = input;
8964         
8965         if(this.hasFeedback && !this.allowBlank){
8966             
8967             var feedback = {
8968                 tag: 'span',
8969                 cls: 'glyphicon form-control-feedback'
8970             };
8971
8972             inputblock = {
8973                 cls : 'has-feedback',
8974                 cn :  [
8975                     input,
8976                     feedback
8977                 ] 
8978             };  
8979         }
8980         
8981         
8982         if (this.before || this.after) {
8983             
8984             inputblock = {
8985                 cls : 'input-group',
8986                 cn :  [] 
8987             };
8988             if (this.before) {
8989                 inputblock.cn.push({
8990                     tag :'span',
8991                     cls : 'input-group-addon',
8992                     html : this.before
8993                 });
8994             }
8995             
8996             inputblock.cn.push(input);
8997             
8998             if(this.hasFeedback && !this.allowBlank){
8999                 inputblock.cls += ' has-feedback';
9000                 inputblock.cn.push(feedback);
9001             }
9002             
9003             if (this.after) {
9004                 inputblock.cn.push({
9005                     tag :'span',
9006                     cls : 'input-group-addon',
9007                     html : this.after
9008                 });
9009             }
9010             
9011         }
9012         
9013         if (align ==='left' && this.fieldLabel.length) {
9014 //                Roo.log("left and has label");
9015                 cfg.cn = [
9016                     
9017                     {
9018                         tag: 'label',
9019                         'for' :  id,
9020                         cls : 'control-label col-sm-' + this.labelWidth,
9021                         html : this.fieldLabel
9022                         
9023                     },
9024                     {
9025                         cls : "col-sm-" + (12 - this.labelWidth), 
9026                         cn: [
9027                             inputblock
9028                         ]
9029                     }
9030                     
9031                 ];
9032         } else if ( this.fieldLabel.length) {
9033 //                Roo.log(" label");
9034                  cfg.cn = [
9035                    
9036                     {
9037                         tag: 'label',
9038                         //cls : 'input-group-addon',
9039                         html : this.fieldLabel
9040                         
9041                     },
9042                     
9043                     inputblock
9044                     
9045                 ];
9046
9047         } else {
9048             
9049 //                   Roo.log(" no label && no align");
9050                 cfg.cn = [
9051                     
9052                         inputblock
9053                     
9054                 ];
9055                 
9056                 
9057         }
9058         
9059         if (this.disabled) {
9060             input.disabled=true;
9061         }
9062         
9063         return cfg;
9064         
9065     },
9066     /**
9067      * return the real textarea element.
9068      */
9069     inputEl: function ()
9070     {
9071         return this.el.select('textarea.form-control',true).first();
9072     },
9073     
9074     /**
9075      * Clear any invalid styles/messages for this field
9076      */
9077     clearInvalid : function()
9078     {
9079         
9080         if(!this.el || this.preventMark){ // not rendered
9081             return;
9082         }
9083         
9084         var label = this.el.select('label', true).first();
9085         var icon = this.el.select('i.fa-star', true).first();
9086         
9087         if(label && icon){
9088             icon.remove();
9089         }
9090         
9091         this.el.removeClass(this.invalidClass);
9092         
9093         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9094             
9095             var feedback = this.el.select('.form-control-feedback', true).first();
9096             
9097             if(feedback){
9098                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9099             }
9100             
9101         }
9102         
9103         this.fireEvent('valid', this);
9104     },
9105     
9106      /**
9107      * Mark this field as valid
9108      */
9109     markValid : function()
9110     {
9111         if(!this.el  || this.preventMark){ // not rendered
9112             return;
9113         }
9114         
9115         this.el.removeClass([this.invalidClass, this.validClass]);
9116         
9117         var feedback = this.el.select('.form-control-feedback', true).first();
9118             
9119         if(feedback){
9120             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9121         }
9122
9123         if(this.disabled || this.allowBlank){
9124             return;
9125         }
9126         
9127         var label = this.el.select('label', true).first();
9128         var icon = this.el.select('i.fa-star', true).first();
9129         
9130         if(label && icon){
9131             icon.remove();
9132         }
9133         
9134         this.el.addClass(this.validClass);
9135         
9136         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9137             
9138             var feedback = this.el.select('.form-control-feedback', true).first();
9139             
9140             if(feedback){
9141                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9142                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9143             }
9144             
9145         }
9146         
9147         this.fireEvent('valid', this);
9148     },
9149     
9150      /**
9151      * Mark this field as invalid
9152      * @param {String} msg The validation message
9153      */
9154     markInvalid : function(msg)
9155     {
9156         if(!this.el  || this.preventMark){ // not rendered
9157             return;
9158         }
9159         
9160         this.el.removeClass([this.invalidClass, this.validClass]);
9161         
9162         var feedback = this.el.select('.form-control-feedback', true).first();
9163             
9164         if(feedback){
9165             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9166         }
9167
9168         if(this.disabled || this.allowBlank){
9169             return;
9170         }
9171         
9172         var label = this.el.select('label', true).first();
9173         var icon = this.el.select('i.fa-star', true).first();
9174         
9175         if(!this.getValue().length && label && !icon){
9176             this.el.createChild({
9177                 tag : 'i',
9178                 cls : 'text-danger fa fa-lg fa-star',
9179                 tooltip : 'This field is required',
9180                 style : 'margin-right:5px;'
9181             }, label, true);
9182         }
9183
9184         this.el.addClass(this.invalidClass);
9185         
9186         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9187             
9188             var feedback = this.el.select('.form-control-feedback', true).first();
9189             
9190             if(feedback){
9191                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9192                 
9193                 if(this.getValue().length || this.forceFeedback){
9194                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9195                 }
9196                 
9197             }
9198             
9199         }
9200         
9201         this.fireEvent('invalid', this, msg);
9202     }
9203 });
9204
9205  
9206 /*
9207  * - LGPL
9208  *
9209  * trigger field - base class for combo..
9210  * 
9211  */
9212  
9213 /**
9214  * @class Roo.bootstrap.TriggerField
9215  * @extends Roo.bootstrap.Input
9216  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9217  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9218  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9219  * for which you can provide a custom implementation.  For example:
9220  * <pre><code>
9221 var trigger = new Roo.bootstrap.TriggerField();
9222 trigger.onTriggerClick = myTriggerFn;
9223 trigger.applyTo('my-field');
9224 </code></pre>
9225  *
9226  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9227  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9228  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9229  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9230  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9231
9232  * @constructor
9233  * Create a new TriggerField.
9234  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9235  * to the base TextField)
9236  */
9237 Roo.bootstrap.TriggerField = function(config){
9238     this.mimicing = false;
9239     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9240 };
9241
9242 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9243     /**
9244      * @cfg {String} triggerClass A CSS class to apply to the trigger
9245      */
9246      /**
9247      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9248      */
9249     hideTrigger:false,
9250
9251     /**
9252      * @cfg {Boolean} removable (true|false) special filter default false
9253      */
9254     removable : false,
9255     
9256     /** @cfg {Boolean} grow @hide */
9257     /** @cfg {Number} growMin @hide */
9258     /** @cfg {Number} growMax @hide */
9259
9260     /**
9261      * @hide 
9262      * @method
9263      */
9264     autoSize: Roo.emptyFn,
9265     // private
9266     monitorTab : true,
9267     // private
9268     deferHeight : true,
9269
9270     
9271     actionMode : 'wrap',
9272     
9273     caret : false,
9274     
9275     
9276     getAutoCreate : function(){
9277        
9278         var align = this.labelAlign || this.parentLabelAlign();
9279         
9280         var id = Roo.id();
9281         
9282         var cfg = {
9283             cls: 'form-group' //input-group
9284         };
9285         
9286         
9287         var input =  {
9288             tag: 'input',
9289             id : id,
9290             type : this.inputType,
9291             cls : 'form-control',
9292             autocomplete: 'new-password',
9293             placeholder : this.placeholder || '' 
9294             
9295         };
9296         if (this.name) {
9297             input.name = this.name;
9298         }
9299         if (this.size) {
9300             input.cls += ' input-' + this.size;
9301         }
9302         
9303         if (this.disabled) {
9304             input.disabled=true;
9305         }
9306         
9307         var inputblock = input;
9308         
9309         if(this.hasFeedback && !this.allowBlank){
9310             
9311             var feedback = {
9312                 tag: 'span',
9313                 cls: 'glyphicon form-control-feedback'
9314             };
9315             
9316             if(this.removable && !this.editable && !this.tickable){
9317                 inputblock = {
9318                     cls : 'has-feedback',
9319                     cn :  [
9320                         inputblock,
9321                         {
9322                             tag: 'button',
9323                             html : 'x',
9324                             cls : 'roo-combo-removable-btn close'
9325                         },
9326                         feedback
9327                     ] 
9328                 };
9329             } else {
9330                 inputblock = {
9331                     cls : 'has-feedback',
9332                     cn :  [
9333                         inputblock,
9334                         feedback
9335                     ] 
9336                 };
9337             }
9338
9339         } else {
9340             if(this.removable && !this.editable && !this.tickable){
9341                 inputblock = {
9342                     cls : 'roo-removable',
9343                     cn :  [
9344                         inputblock,
9345                         {
9346                             tag: 'button',
9347                             html : 'x',
9348                             cls : 'roo-combo-removable-btn close'
9349                         }
9350                     ] 
9351                 };
9352             }
9353         }
9354         
9355         if (this.before || this.after) {
9356             
9357             inputblock = {
9358                 cls : 'input-group',
9359                 cn :  [] 
9360             };
9361             if (this.before) {
9362                 inputblock.cn.push({
9363                     tag :'span',
9364                     cls : 'input-group-addon',
9365                     html : this.before
9366                 });
9367             }
9368             
9369             inputblock.cn.push(input);
9370             
9371             if(this.hasFeedback && !this.allowBlank){
9372                 inputblock.cls += ' has-feedback';
9373                 inputblock.cn.push(feedback);
9374             }
9375             
9376             if (this.after) {
9377                 inputblock.cn.push({
9378                     tag :'span',
9379                     cls : 'input-group-addon',
9380                     html : this.after
9381                 });
9382             }
9383             
9384         };
9385         
9386         var box = {
9387             tag: 'div',
9388             cn: [
9389                 {
9390                     tag: 'input',
9391                     type : 'hidden',
9392                     cls: 'form-hidden-field'
9393                 },
9394                 inputblock
9395             ]
9396             
9397         };
9398         
9399         if(this.multiple){
9400             box = {
9401                 tag: 'div',
9402                 cn: [
9403                     {
9404                         tag: 'input',
9405                         type : 'hidden',
9406                         cls: 'form-hidden-field'
9407                     },
9408                     {
9409                         tag: 'ul',
9410                         cls: 'roo-select2-choices',
9411                         cn:[
9412                             {
9413                                 tag: 'li',
9414                                 cls: 'roo-select2-search-field',
9415                                 cn: [
9416
9417                                     inputblock
9418                                 ]
9419                             }
9420                         ]
9421                     }
9422                 ]
9423             }
9424         };
9425         
9426         var combobox = {
9427             cls: 'roo-select2-container input-group',
9428             cn: [
9429                 box
9430 //                {
9431 //                    tag: 'ul',
9432 //                    cls: 'typeahead typeahead-long dropdown-menu',
9433 //                    style: 'display:none'
9434 //                }
9435             ]
9436         };
9437         
9438         if(!this.multiple && this.showToggleBtn){
9439             
9440             var caret = {
9441                         tag: 'span',
9442                         cls: 'caret'
9443              };
9444             if (this.caret != false) {
9445                 caret = {
9446                      tag: 'i',
9447                      cls: 'fa fa-' + this.caret
9448                 };
9449                 
9450             }
9451             
9452             combobox.cn.push({
9453                 tag :'span',
9454                 cls : 'input-group-addon btn dropdown-toggle',
9455                 cn : [
9456                     caret,
9457                     {
9458                         tag: 'span',
9459                         cls: 'combobox-clear',
9460                         cn  : [
9461                             {
9462                                 tag : 'i',
9463                                 cls: 'icon-remove'
9464                             }
9465                         ]
9466                     }
9467                 ]
9468
9469             })
9470         }
9471         
9472         if(this.multiple){
9473             combobox.cls += ' roo-select2-container-multi';
9474         }
9475         
9476         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9477             
9478 //                Roo.log("left and has label");
9479                 cfg.cn = [
9480                     
9481                     {
9482                         tag: 'label',
9483                         'for' :  id,
9484                         cls : 'control-label col-sm-' + this.labelWidth,
9485                         html : this.fieldLabel
9486                         
9487                     },
9488                     {
9489                         cls : "col-sm-" + (12 - this.labelWidth), 
9490                         cn: [
9491                             combobox
9492                         ]
9493                     }
9494                     
9495                 ];
9496         } else if ( this.fieldLabel.length) {
9497 //                Roo.log(" label");
9498                  cfg.cn = [
9499                    
9500                     {
9501                         tag: 'label',
9502                         //cls : 'input-group-addon',
9503                         html : this.fieldLabel
9504                         
9505                     },
9506                     
9507                     combobox
9508                     
9509                 ];
9510
9511         } else {
9512             
9513 //                Roo.log(" no label && no align");
9514                 cfg = combobox
9515                      
9516                 
9517         }
9518          
9519         var settings=this;
9520         ['xs','sm','md','lg'].map(function(size){
9521             if (settings[size]) {
9522                 cfg.cls += ' col-' + size + '-' + settings[size];
9523             }
9524         });
9525         
9526         return cfg;
9527         
9528     },
9529     
9530     
9531     
9532     // private
9533     onResize : function(w, h){
9534 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9535 //        if(typeof w == 'number'){
9536 //            var x = w - this.trigger.getWidth();
9537 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9538 //            this.trigger.setStyle('left', x+'px');
9539 //        }
9540     },
9541
9542     // private
9543     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9544
9545     // private
9546     getResizeEl : function(){
9547         return this.inputEl();
9548     },
9549
9550     // private
9551     getPositionEl : function(){
9552         return this.inputEl();
9553     },
9554
9555     // private
9556     alignErrorIcon : function(){
9557         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9558     },
9559
9560     // private
9561     initEvents : function(){
9562         
9563         this.createList();
9564         
9565         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9566         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9567         if(!this.multiple && this.showToggleBtn){
9568             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9569             if(this.hideTrigger){
9570                 this.trigger.setDisplayed(false);
9571             }
9572             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9573         }
9574         
9575         if(this.multiple){
9576             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9577         }
9578         
9579         if(this.removable && !this.editable && !this.tickable){
9580             var close = this.closeTriggerEl();
9581             
9582             if(close){
9583                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9584                 close.on('click', this.removeBtnClick, this, close);
9585             }
9586         }
9587         
9588         //this.trigger.addClassOnOver('x-form-trigger-over');
9589         //this.trigger.addClassOnClick('x-form-trigger-click');
9590         
9591         //if(!this.width){
9592         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9593         //}
9594     },
9595     
9596     closeTriggerEl : function()
9597     {
9598         var close = this.el.select('.roo-combo-removable-btn', true).first();
9599         return close ? close : false;
9600     },
9601     
9602     removeBtnClick : function(e, h, el)
9603     {
9604         e.preventDefault();
9605         
9606         if(this.fireEvent("remove", this) !== false){
9607             this.reset();
9608             this.fireEvent("afterremove", this)
9609         }
9610     },
9611     
9612     createList : function()
9613     {
9614         this.list = Roo.get(document.body).createChild({
9615             tag: 'ul',
9616             cls: 'typeahead typeahead-long dropdown-menu',
9617             style: 'display:none'
9618         });
9619         
9620         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9621         
9622     },
9623
9624     // private
9625     initTrigger : function(){
9626        
9627     },
9628
9629     // private
9630     onDestroy : function(){
9631         if(this.trigger){
9632             this.trigger.removeAllListeners();
9633           //  this.trigger.remove();
9634         }
9635         //if(this.wrap){
9636         //    this.wrap.remove();
9637         //}
9638         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9639     },
9640
9641     // private
9642     onFocus : function(){
9643         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9644         /*
9645         if(!this.mimicing){
9646             this.wrap.addClass('x-trigger-wrap-focus');
9647             this.mimicing = true;
9648             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9649             if(this.monitorTab){
9650                 this.el.on("keydown", this.checkTab, this);
9651             }
9652         }
9653         */
9654     },
9655
9656     // private
9657     checkTab : function(e){
9658         if(e.getKey() == e.TAB){
9659             this.triggerBlur();
9660         }
9661     },
9662
9663     // private
9664     onBlur : function(){
9665         // do nothing
9666     },
9667
9668     // private
9669     mimicBlur : function(e, t){
9670         /*
9671         if(!this.wrap.contains(t) && this.validateBlur()){
9672             this.triggerBlur();
9673         }
9674         */
9675     },
9676
9677     // private
9678     triggerBlur : function(){
9679         this.mimicing = false;
9680         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9681         if(this.monitorTab){
9682             this.el.un("keydown", this.checkTab, this);
9683         }
9684         //this.wrap.removeClass('x-trigger-wrap-focus');
9685         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9686     },
9687
9688     // private
9689     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9690     validateBlur : function(e, t){
9691         return true;
9692     },
9693
9694     // private
9695     onDisable : function(){
9696         this.inputEl().dom.disabled = true;
9697         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9698         //if(this.wrap){
9699         //    this.wrap.addClass('x-item-disabled');
9700         //}
9701     },
9702
9703     // private
9704     onEnable : function(){
9705         this.inputEl().dom.disabled = false;
9706         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9707         //if(this.wrap){
9708         //    this.el.removeClass('x-item-disabled');
9709         //}
9710     },
9711
9712     // private
9713     onShow : function(){
9714         var ae = this.getActionEl();
9715         
9716         if(ae){
9717             ae.dom.style.display = '';
9718             ae.dom.style.visibility = 'visible';
9719         }
9720     },
9721
9722     // private
9723     
9724     onHide : function(){
9725         var ae = this.getActionEl();
9726         ae.dom.style.display = 'none';
9727     },
9728
9729     /**
9730      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9731      * by an implementing function.
9732      * @method
9733      * @param {EventObject} e
9734      */
9735     onTriggerClick : Roo.emptyFn
9736 });
9737  /*
9738  * Based on:
9739  * Ext JS Library 1.1.1
9740  * Copyright(c) 2006-2007, Ext JS, LLC.
9741  *
9742  * Originally Released Under LGPL - original licence link has changed is not relivant.
9743  *
9744  * Fork - LGPL
9745  * <script type="text/javascript">
9746  */
9747
9748
9749 /**
9750  * @class Roo.data.SortTypes
9751  * @singleton
9752  * Defines the default sorting (casting?) comparison functions used when sorting data.
9753  */
9754 Roo.data.SortTypes = {
9755     /**
9756      * Default sort that does nothing
9757      * @param {Mixed} s The value being converted
9758      * @return {Mixed} The comparison value
9759      */
9760     none : function(s){
9761         return s;
9762     },
9763     
9764     /**
9765      * The regular expression used to strip tags
9766      * @type {RegExp}
9767      * @property
9768      */
9769     stripTagsRE : /<\/?[^>]+>/gi,
9770     
9771     /**
9772      * Strips all HTML tags to sort on text only
9773      * @param {Mixed} s The value being converted
9774      * @return {String} The comparison value
9775      */
9776     asText : function(s){
9777         return String(s).replace(this.stripTagsRE, "");
9778     },
9779     
9780     /**
9781      * Strips all HTML tags to sort on text only - Case insensitive
9782      * @param {Mixed} s The value being converted
9783      * @return {String} The comparison value
9784      */
9785     asUCText : function(s){
9786         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9787     },
9788     
9789     /**
9790      * Case insensitive string
9791      * @param {Mixed} s The value being converted
9792      * @return {String} The comparison value
9793      */
9794     asUCString : function(s) {
9795         return String(s).toUpperCase();
9796     },
9797     
9798     /**
9799      * Date sorting
9800      * @param {Mixed} s The value being converted
9801      * @return {Number} The comparison value
9802      */
9803     asDate : function(s) {
9804         if(!s){
9805             return 0;
9806         }
9807         if(s instanceof Date){
9808             return s.getTime();
9809         }
9810         return Date.parse(String(s));
9811     },
9812     
9813     /**
9814      * Float sorting
9815      * @param {Mixed} s The value being converted
9816      * @return {Float} The comparison value
9817      */
9818     asFloat : function(s) {
9819         var val = parseFloat(String(s).replace(/,/g, ""));
9820         if(isNaN(val)) {
9821             val = 0;
9822         }
9823         return val;
9824     },
9825     
9826     /**
9827      * Integer sorting
9828      * @param {Mixed} s The value being converted
9829      * @return {Number} The comparison value
9830      */
9831     asInt : function(s) {
9832         var val = parseInt(String(s).replace(/,/g, ""));
9833         if(isNaN(val)) {
9834             val = 0;
9835         }
9836         return val;
9837     }
9838 };/*
9839  * Based on:
9840  * Ext JS Library 1.1.1
9841  * Copyright(c) 2006-2007, Ext JS, LLC.
9842  *
9843  * Originally Released Under LGPL - original licence link has changed is not relivant.
9844  *
9845  * Fork - LGPL
9846  * <script type="text/javascript">
9847  */
9848
9849 /**
9850 * @class Roo.data.Record
9851  * Instances of this class encapsulate both record <em>definition</em> information, and record
9852  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9853  * to access Records cached in an {@link Roo.data.Store} object.<br>
9854  * <p>
9855  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9856  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9857  * objects.<br>
9858  * <p>
9859  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9860  * @constructor
9861  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9862  * {@link #create}. The parameters are the same.
9863  * @param {Array} data An associative Array of data values keyed by the field name.
9864  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9865  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9866  * not specified an integer id is generated.
9867  */
9868 Roo.data.Record = function(data, id){
9869     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9870     this.data = data;
9871 };
9872
9873 /**
9874  * Generate a constructor for a specific record layout.
9875  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9876  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9877  * Each field definition object may contain the following properties: <ul>
9878  * <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,
9879  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9880  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9881  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9882  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9883  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9884  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9885  * this may be omitted.</p></li>
9886  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9887  * <ul><li>auto (Default, implies no conversion)</li>
9888  * <li>string</li>
9889  * <li>int</li>
9890  * <li>float</li>
9891  * <li>boolean</li>
9892  * <li>date</li></ul></p></li>
9893  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9894  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9895  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9896  * by the Reader into an object that will be stored in the Record. It is passed the
9897  * following parameters:<ul>
9898  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9899  * </ul></p></li>
9900  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9901  * </ul>
9902  * <br>usage:<br><pre><code>
9903 var TopicRecord = Roo.data.Record.create(
9904     {name: 'title', mapping: 'topic_title'},
9905     {name: 'author', mapping: 'username'},
9906     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9907     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9908     {name: 'lastPoster', mapping: 'user2'},
9909     {name: 'excerpt', mapping: 'post_text'}
9910 );
9911
9912 var myNewRecord = new TopicRecord({
9913     title: 'Do my job please',
9914     author: 'noobie',
9915     totalPosts: 1,
9916     lastPost: new Date(),
9917     lastPoster: 'Animal',
9918     excerpt: 'No way dude!'
9919 });
9920 myStore.add(myNewRecord);
9921 </code></pre>
9922  * @method create
9923  * @static
9924  */
9925 Roo.data.Record.create = function(o){
9926     var f = function(){
9927         f.superclass.constructor.apply(this, arguments);
9928     };
9929     Roo.extend(f, Roo.data.Record);
9930     var p = f.prototype;
9931     p.fields = new Roo.util.MixedCollection(false, function(field){
9932         return field.name;
9933     });
9934     for(var i = 0, len = o.length; i < len; i++){
9935         p.fields.add(new Roo.data.Field(o[i]));
9936     }
9937     f.getField = function(name){
9938         return p.fields.get(name);  
9939     };
9940     return f;
9941 };
9942
9943 Roo.data.Record.AUTO_ID = 1000;
9944 Roo.data.Record.EDIT = 'edit';
9945 Roo.data.Record.REJECT = 'reject';
9946 Roo.data.Record.COMMIT = 'commit';
9947
9948 Roo.data.Record.prototype = {
9949     /**
9950      * Readonly flag - true if this record has been modified.
9951      * @type Boolean
9952      */
9953     dirty : false,
9954     editing : false,
9955     error: null,
9956     modified: null,
9957
9958     // private
9959     join : function(store){
9960         this.store = store;
9961     },
9962
9963     /**
9964      * Set the named field to the specified value.
9965      * @param {String} name The name of the field to set.
9966      * @param {Object} value The value to set the field to.
9967      */
9968     set : function(name, value){
9969         if(this.data[name] == value){
9970             return;
9971         }
9972         this.dirty = true;
9973         if(!this.modified){
9974             this.modified = {};
9975         }
9976         if(typeof this.modified[name] == 'undefined'){
9977             this.modified[name] = this.data[name];
9978         }
9979         this.data[name] = value;
9980         if(!this.editing && this.store){
9981             this.store.afterEdit(this);
9982         }       
9983     },
9984
9985     /**
9986      * Get the value of the named field.
9987      * @param {String} name The name of the field to get the value of.
9988      * @return {Object} The value of the field.
9989      */
9990     get : function(name){
9991         return this.data[name]; 
9992     },
9993
9994     // private
9995     beginEdit : function(){
9996         this.editing = true;
9997         this.modified = {}; 
9998     },
9999
10000     // private
10001     cancelEdit : function(){
10002         this.editing = false;
10003         delete this.modified;
10004     },
10005
10006     // private
10007     endEdit : function(){
10008         this.editing = false;
10009         if(this.dirty && this.store){
10010             this.store.afterEdit(this);
10011         }
10012     },
10013
10014     /**
10015      * Usually called by the {@link Roo.data.Store} which owns the Record.
10016      * Rejects all changes made to the Record since either creation, or the last commit operation.
10017      * Modified fields are reverted to their original values.
10018      * <p>
10019      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10020      * of reject operations.
10021      */
10022     reject : function(){
10023         var m = this.modified;
10024         for(var n in m){
10025             if(typeof m[n] != "function"){
10026                 this.data[n] = m[n];
10027             }
10028         }
10029         this.dirty = false;
10030         delete this.modified;
10031         this.editing = false;
10032         if(this.store){
10033             this.store.afterReject(this);
10034         }
10035     },
10036
10037     /**
10038      * Usually called by the {@link Roo.data.Store} which owns the Record.
10039      * Commits all changes made to the Record since either creation, or the last commit operation.
10040      * <p>
10041      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10042      * of commit operations.
10043      */
10044     commit : function(){
10045         this.dirty = false;
10046         delete this.modified;
10047         this.editing = false;
10048         if(this.store){
10049             this.store.afterCommit(this);
10050         }
10051     },
10052
10053     // private
10054     hasError : function(){
10055         return this.error != null;
10056     },
10057
10058     // private
10059     clearError : function(){
10060         this.error = null;
10061     },
10062
10063     /**
10064      * Creates a copy of this record.
10065      * @param {String} id (optional) A new record id if you don't want to use this record's id
10066      * @return {Record}
10067      */
10068     copy : function(newId) {
10069         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10070     }
10071 };/*
10072  * Based on:
10073  * Ext JS Library 1.1.1
10074  * Copyright(c) 2006-2007, Ext JS, LLC.
10075  *
10076  * Originally Released Under LGPL - original licence link has changed is not relivant.
10077  *
10078  * Fork - LGPL
10079  * <script type="text/javascript">
10080  */
10081
10082
10083
10084 /**
10085  * @class Roo.data.Store
10086  * @extends Roo.util.Observable
10087  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10088  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10089  * <p>
10090  * 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
10091  * has no knowledge of the format of the data returned by the Proxy.<br>
10092  * <p>
10093  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10094  * instances from the data object. These records are cached and made available through accessor functions.
10095  * @constructor
10096  * Creates a new Store.
10097  * @param {Object} config A config object containing the objects needed for the Store to access data,
10098  * and read the data into Records.
10099  */
10100 Roo.data.Store = function(config){
10101     this.data = new Roo.util.MixedCollection(false);
10102     this.data.getKey = function(o){
10103         return o.id;
10104     };
10105     this.baseParams = {};
10106     // private
10107     this.paramNames = {
10108         "start" : "start",
10109         "limit" : "limit",
10110         "sort" : "sort",
10111         "dir" : "dir",
10112         "multisort" : "_multisort"
10113     };
10114
10115     if(config && config.data){
10116         this.inlineData = config.data;
10117         delete config.data;
10118     }
10119
10120     Roo.apply(this, config);
10121     
10122     if(this.reader){ // reader passed
10123         this.reader = Roo.factory(this.reader, Roo.data);
10124         this.reader.xmodule = this.xmodule || false;
10125         if(!this.recordType){
10126             this.recordType = this.reader.recordType;
10127         }
10128         if(this.reader.onMetaChange){
10129             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10130         }
10131     }
10132
10133     if(this.recordType){
10134         this.fields = this.recordType.prototype.fields;
10135     }
10136     this.modified = [];
10137
10138     this.addEvents({
10139         /**
10140          * @event datachanged
10141          * Fires when the data cache has changed, and a widget which is using this Store
10142          * as a Record cache should refresh its view.
10143          * @param {Store} this
10144          */
10145         datachanged : true,
10146         /**
10147          * @event metachange
10148          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10149          * @param {Store} this
10150          * @param {Object} meta The JSON metadata
10151          */
10152         metachange : true,
10153         /**
10154          * @event add
10155          * Fires when Records have been added to the Store
10156          * @param {Store} this
10157          * @param {Roo.data.Record[]} records The array of Records added
10158          * @param {Number} index The index at which the record(s) were added
10159          */
10160         add : true,
10161         /**
10162          * @event remove
10163          * Fires when a Record has been removed from the Store
10164          * @param {Store} this
10165          * @param {Roo.data.Record} record The Record that was removed
10166          * @param {Number} index The index at which the record was removed
10167          */
10168         remove : true,
10169         /**
10170          * @event update
10171          * Fires when a Record has been updated
10172          * @param {Store} this
10173          * @param {Roo.data.Record} record The Record that was updated
10174          * @param {String} operation The update operation being performed.  Value may be one of:
10175          * <pre><code>
10176  Roo.data.Record.EDIT
10177  Roo.data.Record.REJECT
10178  Roo.data.Record.COMMIT
10179          * </code></pre>
10180          */
10181         update : true,
10182         /**
10183          * @event clear
10184          * Fires when the data cache has been cleared.
10185          * @param {Store} this
10186          */
10187         clear : true,
10188         /**
10189          * @event beforeload
10190          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10191          * the load action will be canceled.
10192          * @param {Store} this
10193          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10194          */
10195         beforeload : true,
10196         /**
10197          * @event beforeloadadd
10198          * Fires after a new set of Records has been loaded.
10199          * @param {Store} this
10200          * @param {Roo.data.Record[]} records The Records that were loaded
10201          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10202          */
10203         beforeloadadd : true,
10204         /**
10205          * @event load
10206          * Fires after a new set of Records has been loaded, before they are added to the store.
10207          * @param {Store} this
10208          * @param {Roo.data.Record[]} records The Records that were loaded
10209          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10210          * @params {Object} return from reader
10211          */
10212         load : true,
10213         /**
10214          * @event loadexception
10215          * Fires if an exception occurs in the Proxy during loading.
10216          * Called with the signature of the Proxy's "loadexception" event.
10217          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10218          * 
10219          * @param {Proxy} 
10220          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10221          * @param {Object} load options 
10222          * @param {Object} jsonData from your request (normally this contains the Exception)
10223          */
10224         loadexception : true
10225     });
10226     
10227     if(this.proxy){
10228         this.proxy = Roo.factory(this.proxy, Roo.data);
10229         this.proxy.xmodule = this.xmodule || false;
10230         this.relayEvents(this.proxy,  ["loadexception"]);
10231     }
10232     this.sortToggle = {};
10233     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10234
10235     Roo.data.Store.superclass.constructor.call(this);
10236
10237     if(this.inlineData){
10238         this.loadData(this.inlineData);
10239         delete this.inlineData;
10240     }
10241 };
10242
10243 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10244      /**
10245     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10246     * without a remote query - used by combo/forms at present.
10247     */
10248     
10249     /**
10250     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10251     */
10252     /**
10253     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10254     */
10255     /**
10256     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10257     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10258     */
10259     /**
10260     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10261     * on any HTTP request
10262     */
10263     /**
10264     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10265     */
10266     /**
10267     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10268     */
10269     multiSort: false,
10270     /**
10271     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10272     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10273     */
10274     remoteSort : false,
10275
10276     /**
10277     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10278      * loaded or when a record is removed. (defaults to false).
10279     */
10280     pruneModifiedRecords : false,
10281
10282     // private
10283     lastOptions : null,
10284
10285     /**
10286      * Add Records to the Store and fires the add event.
10287      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10288      */
10289     add : function(records){
10290         records = [].concat(records);
10291         for(var i = 0, len = records.length; i < len; i++){
10292             records[i].join(this);
10293         }
10294         var index = this.data.length;
10295         this.data.addAll(records);
10296         this.fireEvent("add", this, records, index);
10297     },
10298
10299     /**
10300      * Remove a Record from the Store and fires the remove event.
10301      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10302      */
10303     remove : function(record){
10304         var index = this.data.indexOf(record);
10305         this.data.removeAt(index);
10306         if(this.pruneModifiedRecords){
10307             this.modified.remove(record);
10308         }
10309         this.fireEvent("remove", this, record, index);
10310     },
10311
10312     /**
10313      * Remove all Records from the Store and fires the clear event.
10314      */
10315     removeAll : function(){
10316         this.data.clear();
10317         if(this.pruneModifiedRecords){
10318             this.modified = [];
10319         }
10320         this.fireEvent("clear", this);
10321     },
10322
10323     /**
10324      * Inserts Records to the Store at the given index and fires the add event.
10325      * @param {Number} index The start index at which to insert the passed Records.
10326      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10327      */
10328     insert : function(index, records){
10329         records = [].concat(records);
10330         for(var i = 0, len = records.length; i < len; i++){
10331             this.data.insert(index, records[i]);
10332             records[i].join(this);
10333         }
10334         this.fireEvent("add", this, records, index);
10335     },
10336
10337     /**
10338      * Get the index within the cache of the passed Record.
10339      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10340      * @return {Number} The index of the passed Record. Returns -1 if not found.
10341      */
10342     indexOf : function(record){
10343         return this.data.indexOf(record);
10344     },
10345
10346     /**
10347      * Get the index within the cache of the Record with the passed id.
10348      * @param {String} id The id of the Record to find.
10349      * @return {Number} The index of the Record. Returns -1 if not found.
10350      */
10351     indexOfId : function(id){
10352         return this.data.indexOfKey(id);
10353     },
10354
10355     /**
10356      * Get the Record with the specified id.
10357      * @param {String} id The id of the Record to find.
10358      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10359      */
10360     getById : function(id){
10361         return this.data.key(id);
10362     },
10363
10364     /**
10365      * Get the Record at the specified index.
10366      * @param {Number} index The index of the Record to find.
10367      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10368      */
10369     getAt : function(index){
10370         return this.data.itemAt(index);
10371     },
10372
10373     /**
10374      * Returns a range of Records between specified indices.
10375      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10376      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10377      * @return {Roo.data.Record[]} An array of Records
10378      */
10379     getRange : function(start, end){
10380         return this.data.getRange(start, end);
10381     },
10382
10383     // private
10384     storeOptions : function(o){
10385         o = Roo.apply({}, o);
10386         delete o.callback;
10387         delete o.scope;
10388         this.lastOptions = o;
10389     },
10390
10391     /**
10392      * Loads the Record cache from the configured Proxy using the configured Reader.
10393      * <p>
10394      * If using remote paging, then the first load call must specify the <em>start</em>
10395      * and <em>limit</em> properties in the options.params property to establish the initial
10396      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10397      * <p>
10398      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10399      * and this call will return before the new data has been loaded. Perform any post-processing
10400      * in a callback function, or in a "load" event handler.</strong>
10401      * <p>
10402      * @param {Object} options An object containing properties which control loading options:<ul>
10403      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10404      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10405      * passed the following arguments:<ul>
10406      * <li>r : Roo.data.Record[]</li>
10407      * <li>options: Options object from the load call</li>
10408      * <li>success: Boolean success indicator</li></ul></li>
10409      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10410      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10411      * </ul>
10412      */
10413     load : function(options){
10414         options = options || {};
10415         if(this.fireEvent("beforeload", this, options) !== false){
10416             this.storeOptions(options);
10417             var p = Roo.apply(options.params || {}, this.baseParams);
10418             // if meta was not loaded from remote source.. try requesting it.
10419             if (!this.reader.metaFromRemote) {
10420                 p._requestMeta = 1;
10421             }
10422             if(this.sortInfo && this.remoteSort){
10423                 var pn = this.paramNames;
10424                 p[pn["sort"]] = this.sortInfo.field;
10425                 p[pn["dir"]] = this.sortInfo.direction;
10426             }
10427             if (this.multiSort) {
10428                 var pn = this.paramNames;
10429                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10430             }
10431             
10432             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10433         }
10434     },
10435
10436     /**
10437      * Reloads the Record cache from the configured Proxy using the configured Reader and
10438      * the options from the last load operation performed.
10439      * @param {Object} options (optional) An object containing properties which may override the options
10440      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10441      * the most recently used options are reused).
10442      */
10443     reload : function(options){
10444         this.load(Roo.applyIf(options||{}, this.lastOptions));
10445     },
10446
10447     // private
10448     // Called as a callback by the Reader during a load operation.
10449     loadRecords : function(o, options, success){
10450         if(!o || success === false){
10451             if(success !== false){
10452                 this.fireEvent("load", this, [], options, o);
10453             }
10454             if(options.callback){
10455                 options.callback.call(options.scope || this, [], options, false);
10456             }
10457             return;
10458         }
10459         // if data returned failure - throw an exception.
10460         if (o.success === false) {
10461             // show a message if no listener is registered.
10462             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10463                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10464             }
10465             // loadmask wil be hooked into this..
10466             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10467             return;
10468         }
10469         var r = o.records, t = o.totalRecords || r.length;
10470         
10471         this.fireEvent("beforeloadadd", this, r, options, o);
10472         
10473         if(!options || options.add !== true){
10474             if(this.pruneModifiedRecords){
10475                 this.modified = [];
10476             }
10477             for(var i = 0, len = r.length; i < len; i++){
10478                 r[i].join(this);
10479             }
10480             if(this.snapshot){
10481                 this.data = this.snapshot;
10482                 delete this.snapshot;
10483             }
10484             this.data.clear();
10485             this.data.addAll(r);
10486             this.totalLength = t;
10487             this.applySort();
10488             this.fireEvent("datachanged", this);
10489         }else{
10490             this.totalLength = Math.max(t, this.data.length+r.length);
10491             this.add(r);
10492         }
10493         this.fireEvent("load", this, r, options, o);
10494         if(options.callback){
10495             options.callback.call(options.scope || this, r, options, true);
10496         }
10497     },
10498
10499
10500     /**
10501      * Loads data from a passed data block. A Reader which understands the format of the data
10502      * must have been configured in the constructor.
10503      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10504      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10505      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10506      */
10507     loadData : function(o, append){
10508         var r = this.reader.readRecords(o);
10509         this.loadRecords(r, {add: append}, true);
10510     },
10511
10512     /**
10513      * Gets the number of cached records.
10514      * <p>
10515      * <em>If using paging, this may not be the total size of the dataset. If the data object
10516      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10517      * the data set size</em>
10518      */
10519     getCount : function(){
10520         return this.data.length || 0;
10521     },
10522
10523     /**
10524      * Gets the total number of records in the dataset as returned by the server.
10525      * <p>
10526      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10527      * the dataset size</em>
10528      */
10529     getTotalCount : function(){
10530         return this.totalLength || 0;
10531     },
10532
10533     /**
10534      * Returns the sort state of the Store as an object with two properties:
10535      * <pre><code>
10536  field {String} The name of the field by which the Records are sorted
10537  direction {String} The sort order, "ASC" or "DESC"
10538      * </code></pre>
10539      */
10540     getSortState : function(){
10541         return this.sortInfo;
10542     },
10543
10544     // private
10545     applySort : function(){
10546         if(this.sortInfo && !this.remoteSort){
10547             var s = this.sortInfo, f = s.field;
10548             var st = this.fields.get(f).sortType;
10549             var fn = function(r1, r2){
10550                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10551                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10552             };
10553             this.data.sort(s.direction, fn);
10554             if(this.snapshot && this.snapshot != this.data){
10555                 this.snapshot.sort(s.direction, fn);
10556             }
10557         }
10558     },
10559
10560     /**
10561      * Sets the default sort column and order to be used by the next load operation.
10562      * @param {String} fieldName The name of the field to sort by.
10563      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10564      */
10565     setDefaultSort : function(field, dir){
10566         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10567     },
10568
10569     /**
10570      * Sort the Records.
10571      * If remote sorting is used, the sort is performed on the server, and the cache is
10572      * reloaded. If local sorting is used, the cache is sorted internally.
10573      * @param {String} fieldName The name of the field to sort by.
10574      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10575      */
10576     sort : function(fieldName, dir){
10577         var f = this.fields.get(fieldName);
10578         if(!dir){
10579             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10580             
10581             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10582                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10583             }else{
10584                 dir = f.sortDir;
10585             }
10586         }
10587         this.sortToggle[f.name] = dir;
10588         this.sortInfo = {field: f.name, direction: dir};
10589         if(!this.remoteSort){
10590             this.applySort();
10591             this.fireEvent("datachanged", this);
10592         }else{
10593             this.load(this.lastOptions);
10594         }
10595     },
10596
10597     /**
10598      * Calls the specified function for each of the Records in the cache.
10599      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10600      * Returning <em>false</em> aborts and exits the iteration.
10601      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10602      */
10603     each : function(fn, scope){
10604         this.data.each(fn, scope);
10605     },
10606
10607     /**
10608      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10609      * (e.g., during paging).
10610      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10611      */
10612     getModifiedRecords : function(){
10613         return this.modified;
10614     },
10615
10616     // private
10617     createFilterFn : function(property, value, anyMatch){
10618         if(!value.exec){ // not a regex
10619             value = String(value);
10620             if(value.length == 0){
10621                 return false;
10622             }
10623             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10624         }
10625         return function(r){
10626             return value.test(r.data[property]);
10627         };
10628     },
10629
10630     /**
10631      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10632      * @param {String} property A field on your records
10633      * @param {Number} start The record index to start at (defaults to 0)
10634      * @param {Number} end The last record index to include (defaults to length - 1)
10635      * @return {Number} The sum
10636      */
10637     sum : function(property, start, end){
10638         var rs = this.data.items, v = 0;
10639         start = start || 0;
10640         end = (end || end === 0) ? end : rs.length-1;
10641
10642         for(var i = start; i <= end; i++){
10643             v += (rs[i].data[property] || 0);
10644         }
10645         return v;
10646     },
10647
10648     /**
10649      * Filter the records by a specified property.
10650      * @param {String} field A field on your records
10651      * @param {String/RegExp} value Either a string that the field
10652      * should start with or a RegExp to test against the field
10653      * @param {Boolean} anyMatch True to match any part not just the beginning
10654      */
10655     filter : function(property, value, anyMatch){
10656         var fn = this.createFilterFn(property, value, anyMatch);
10657         return fn ? this.filterBy(fn) : this.clearFilter();
10658     },
10659
10660     /**
10661      * Filter by a function. The specified function will be called with each
10662      * record in this data source. If the function returns true the record is included,
10663      * otherwise it is filtered.
10664      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10665      * @param {Object} scope (optional) The scope of the function (defaults to this)
10666      */
10667     filterBy : function(fn, scope){
10668         this.snapshot = this.snapshot || this.data;
10669         this.data = this.queryBy(fn, scope||this);
10670         this.fireEvent("datachanged", this);
10671     },
10672
10673     /**
10674      * Query the records by a specified property.
10675      * @param {String} field A field on your records
10676      * @param {String/RegExp} value Either a string that the field
10677      * should start with or a RegExp to test against the field
10678      * @param {Boolean} anyMatch True to match any part not just the beginning
10679      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10680      */
10681     query : function(property, value, anyMatch){
10682         var fn = this.createFilterFn(property, value, anyMatch);
10683         return fn ? this.queryBy(fn) : this.data.clone();
10684     },
10685
10686     /**
10687      * Query by a function. The specified function will be called with each
10688      * record in this data source. If the function returns true the record is included
10689      * in the results.
10690      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10691      * @param {Object} scope (optional) The scope of the function (defaults to this)
10692       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10693      **/
10694     queryBy : function(fn, scope){
10695         var data = this.snapshot || this.data;
10696         return data.filterBy(fn, scope||this);
10697     },
10698
10699     /**
10700      * Collects unique values for a particular dataIndex from this store.
10701      * @param {String} dataIndex The property to collect
10702      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10703      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10704      * @return {Array} An array of the unique values
10705      **/
10706     collect : function(dataIndex, allowNull, bypassFilter){
10707         var d = (bypassFilter === true && this.snapshot) ?
10708                 this.snapshot.items : this.data.items;
10709         var v, sv, r = [], l = {};
10710         for(var i = 0, len = d.length; i < len; i++){
10711             v = d[i].data[dataIndex];
10712             sv = String(v);
10713             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10714                 l[sv] = true;
10715                 r[r.length] = v;
10716             }
10717         }
10718         return r;
10719     },
10720
10721     /**
10722      * Revert to a view of the Record cache with no filtering applied.
10723      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10724      */
10725     clearFilter : function(suppressEvent){
10726         if(this.snapshot && this.snapshot != this.data){
10727             this.data = this.snapshot;
10728             delete this.snapshot;
10729             if(suppressEvent !== true){
10730                 this.fireEvent("datachanged", this);
10731             }
10732         }
10733     },
10734
10735     // private
10736     afterEdit : function(record){
10737         if(this.modified.indexOf(record) == -1){
10738             this.modified.push(record);
10739         }
10740         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10741     },
10742     
10743     // private
10744     afterReject : function(record){
10745         this.modified.remove(record);
10746         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10747     },
10748
10749     // private
10750     afterCommit : function(record){
10751         this.modified.remove(record);
10752         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10753     },
10754
10755     /**
10756      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10757      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10758      */
10759     commitChanges : function(){
10760         var m = this.modified.slice(0);
10761         this.modified = [];
10762         for(var i = 0, len = m.length; i < len; i++){
10763             m[i].commit();
10764         }
10765     },
10766
10767     /**
10768      * Cancel outstanding changes on all changed records.
10769      */
10770     rejectChanges : function(){
10771         var m = this.modified.slice(0);
10772         this.modified = [];
10773         for(var i = 0, len = m.length; i < len; i++){
10774             m[i].reject();
10775         }
10776     },
10777
10778     onMetaChange : function(meta, rtype, o){
10779         this.recordType = rtype;
10780         this.fields = rtype.prototype.fields;
10781         delete this.snapshot;
10782         this.sortInfo = meta.sortInfo || this.sortInfo;
10783         this.modified = [];
10784         this.fireEvent('metachange', this, this.reader.meta);
10785     },
10786     
10787     moveIndex : function(data, type)
10788     {
10789         var index = this.indexOf(data);
10790         
10791         var newIndex = index + type;
10792         
10793         this.remove(data);
10794         
10795         this.insert(newIndex, data);
10796         
10797     }
10798 });/*
10799  * Based on:
10800  * Ext JS Library 1.1.1
10801  * Copyright(c) 2006-2007, Ext JS, LLC.
10802  *
10803  * Originally Released Under LGPL - original licence link has changed is not relivant.
10804  *
10805  * Fork - LGPL
10806  * <script type="text/javascript">
10807  */
10808
10809 /**
10810  * @class Roo.data.SimpleStore
10811  * @extends Roo.data.Store
10812  * Small helper class to make creating Stores from Array data easier.
10813  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10814  * @cfg {Array} fields An array of field definition objects, or field name strings.
10815  * @cfg {Array} data The multi-dimensional array of data
10816  * @constructor
10817  * @param {Object} config
10818  */
10819 Roo.data.SimpleStore = function(config){
10820     Roo.data.SimpleStore.superclass.constructor.call(this, {
10821         isLocal : true,
10822         reader: new Roo.data.ArrayReader({
10823                 id: config.id
10824             },
10825             Roo.data.Record.create(config.fields)
10826         ),
10827         proxy : new Roo.data.MemoryProxy(config.data)
10828     });
10829     this.load();
10830 };
10831 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10832  * Based on:
10833  * Ext JS Library 1.1.1
10834  * Copyright(c) 2006-2007, Ext JS, LLC.
10835  *
10836  * Originally Released Under LGPL - original licence link has changed is not relivant.
10837  *
10838  * Fork - LGPL
10839  * <script type="text/javascript">
10840  */
10841
10842 /**
10843 /**
10844  * @extends Roo.data.Store
10845  * @class Roo.data.JsonStore
10846  * Small helper class to make creating Stores for JSON data easier. <br/>
10847 <pre><code>
10848 var store = new Roo.data.JsonStore({
10849     url: 'get-images.php',
10850     root: 'images',
10851     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10852 });
10853 </code></pre>
10854  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10855  * JsonReader and HttpProxy (unless inline data is provided).</b>
10856  * @cfg {Array} fields An array of field definition objects, or field name strings.
10857  * @constructor
10858  * @param {Object} config
10859  */
10860 Roo.data.JsonStore = function(c){
10861     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10862         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10863         reader: new Roo.data.JsonReader(c, c.fields)
10864     }));
10865 };
10866 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10867  * Based on:
10868  * Ext JS Library 1.1.1
10869  * Copyright(c) 2006-2007, Ext JS, LLC.
10870  *
10871  * Originally Released Under LGPL - original licence link has changed is not relivant.
10872  *
10873  * Fork - LGPL
10874  * <script type="text/javascript">
10875  */
10876
10877  
10878 Roo.data.Field = function(config){
10879     if(typeof config == "string"){
10880         config = {name: config};
10881     }
10882     Roo.apply(this, config);
10883     
10884     if(!this.type){
10885         this.type = "auto";
10886     }
10887     
10888     var st = Roo.data.SortTypes;
10889     // named sortTypes are supported, here we look them up
10890     if(typeof this.sortType == "string"){
10891         this.sortType = st[this.sortType];
10892     }
10893     
10894     // set default sortType for strings and dates
10895     if(!this.sortType){
10896         switch(this.type){
10897             case "string":
10898                 this.sortType = st.asUCString;
10899                 break;
10900             case "date":
10901                 this.sortType = st.asDate;
10902                 break;
10903             default:
10904                 this.sortType = st.none;
10905         }
10906     }
10907
10908     // define once
10909     var stripRe = /[\$,%]/g;
10910
10911     // prebuilt conversion function for this field, instead of
10912     // switching every time we're reading a value
10913     if(!this.convert){
10914         var cv, dateFormat = this.dateFormat;
10915         switch(this.type){
10916             case "":
10917             case "auto":
10918             case undefined:
10919                 cv = function(v){ return v; };
10920                 break;
10921             case "string":
10922                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10923                 break;
10924             case "int":
10925                 cv = function(v){
10926                     return v !== undefined && v !== null && v !== '' ?
10927                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10928                     };
10929                 break;
10930             case "float":
10931                 cv = function(v){
10932                     return v !== undefined && v !== null && v !== '' ?
10933                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10934                     };
10935                 break;
10936             case "bool":
10937             case "boolean":
10938                 cv = function(v){ return v === true || v === "true" || v == 1; };
10939                 break;
10940             case "date":
10941                 cv = function(v){
10942                     if(!v){
10943                         return '';
10944                     }
10945                     if(v instanceof Date){
10946                         return v;
10947                     }
10948                     if(dateFormat){
10949                         if(dateFormat == "timestamp"){
10950                             return new Date(v*1000);
10951                         }
10952                         return Date.parseDate(v, dateFormat);
10953                     }
10954                     var parsed = Date.parse(v);
10955                     return parsed ? new Date(parsed) : null;
10956                 };
10957              break;
10958             
10959         }
10960         this.convert = cv;
10961     }
10962 };
10963
10964 Roo.data.Field.prototype = {
10965     dateFormat: null,
10966     defaultValue: "",
10967     mapping: null,
10968     sortType : null,
10969     sortDir : "ASC"
10970 };/*
10971  * Based on:
10972  * Ext JS Library 1.1.1
10973  * Copyright(c) 2006-2007, Ext JS, LLC.
10974  *
10975  * Originally Released Under LGPL - original licence link has changed is not relivant.
10976  *
10977  * Fork - LGPL
10978  * <script type="text/javascript">
10979  */
10980  
10981 // Base class for reading structured data from a data source.  This class is intended to be
10982 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10983
10984 /**
10985  * @class Roo.data.DataReader
10986  * Base class for reading structured data from a data source.  This class is intended to be
10987  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10988  */
10989
10990 Roo.data.DataReader = function(meta, recordType){
10991     
10992     this.meta = meta;
10993     
10994     this.recordType = recordType instanceof Array ? 
10995         Roo.data.Record.create(recordType) : recordType;
10996 };
10997
10998 Roo.data.DataReader.prototype = {
10999      /**
11000      * Create an empty record
11001      * @param {Object} data (optional) - overlay some values
11002      * @return {Roo.data.Record} record created.
11003      */
11004     newRow :  function(d) {
11005         var da =  {};
11006         this.recordType.prototype.fields.each(function(c) {
11007             switch( c.type) {
11008                 case 'int' : da[c.name] = 0; break;
11009                 case 'date' : da[c.name] = new Date(); break;
11010                 case 'float' : da[c.name] = 0.0; break;
11011                 case 'boolean' : da[c.name] = false; break;
11012                 default : da[c.name] = ""; break;
11013             }
11014             
11015         });
11016         return new this.recordType(Roo.apply(da, d));
11017     }
11018     
11019 };/*
11020  * Based on:
11021  * Ext JS Library 1.1.1
11022  * Copyright(c) 2006-2007, Ext JS, LLC.
11023  *
11024  * Originally Released Under LGPL - original licence link has changed is not relivant.
11025  *
11026  * Fork - LGPL
11027  * <script type="text/javascript">
11028  */
11029
11030 /**
11031  * @class Roo.data.DataProxy
11032  * @extends Roo.data.Observable
11033  * This class is an abstract base class for implementations which provide retrieval of
11034  * unformatted data objects.<br>
11035  * <p>
11036  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11037  * (of the appropriate type which knows how to parse the data object) to provide a block of
11038  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11039  * <p>
11040  * Custom implementations must implement the load method as described in
11041  * {@link Roo.data.HttpProxy#load}.
11042  */
11043 Roo.data.DataProxy = function(){
11044     this.addEvents({
11045         /**
11046          * @event beforeload
11047          * Fires before a network request is made to retrieve a data object.
11048          * @param {Object} This DataProxy object.
11049          * @param {Object} params The params parameter to the load function.
11050          */
11051         beforeload : true,
11052         /**
11053          * @event load
11054          * Fires before the load method's callback is called.
11055          * @param {Object} This DataProxy object.
11056          * @param {Object} o The data object.
11057          * @param {Object} arg The callback argument object passed to the load function.
11058          */
11059         load : true,
11060         /**
11061          * @event loadexception
11062          * Fires if an Exception occurs during data retrieval.
11063          * @param {Object} This DataProxy object.
11064          * @param {Object} o The data object.
11065          * @param {Object} arg The callback argument object passed to the load function.
11066          * @param {Object} e The Exception.
11067          */
11068         loadexception : true
11069     });
11070     Roo.data.DataProxy.superclass.constructor.call(this);
11071 };
11072
11073 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11074
11075     /**
11076      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11077      */
11078 /*
11079  * Based on:
11080  * Ext JS Library 1.1.1
11081  * Copyright(c) 2006-2007, Ext JS, LLC.
11082  *
11083  * Originally Released Under LGPL - original licence link has changed is not relivant.
11084  *
11085  * Fork - LGPL
11086  * <script type="text/javascript">
11087  */
11088 /**
11089  * @class Roo.data.MemoryProxy
11090  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11091  * to the Reader when its load method is called.
11092  * @constructor
11093  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11094  */
11095 Roo.data.MemoryProxy = function(data){
11096     if (data.data) {
11097         data = data.data;
11098     }
11099     Roo.data.MemoryProxy.superclass.constructor.call(this);
11100     this.data = data;
11101 };
11102
11103 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11104     
11105     /**
11106      * Load data from the requested source (in this case an in-memory
11107      * data object passed to the constructor), read the data object into
11108      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11109      * process that block using the passed callback.
11110      * @param {Object} params This parameter is not used by the MemoryProxy class.
11111      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11112      * object into a block of Roo.data.Records.
11113      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11114      * The function must be passed <ul>
11115      * <li>The Record block object</li>
11116      * <li>The "arg" argument from the load function</li>
11117      * <li>A boolean success indicator</li>
11118      * </ul>
11119      * @param {Object} scope The scope in which to call the callback
11120      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11121      */
11122     load : function(params, reader, callback, scope, arg){
11123         params = params || {};
11124         var result;
11125         try {
11126             result = reader.readRecords(this.data);
11127         }catch(e){
11128             this.fireEvent("loadexception", this, arg, null, e);
11129             callback.call(scope, null, arg, false);
11130             return;
11131         }
11132         callback.call(scope, result, arg, true);
11133     },
11134     
11135     // private
11136     update : function(params, records){
11137         
11138     }
11139 });/*
11140  * Based on:
11141  * Ext JS Library 1.1.1
11142  * Copyright(c) 2006-2007, Ext JS, LLC.
11143  *
11144  * Originally Released Under LGPL - original licence link has changed is not relivant.
11145  *
11146  * Fork - LGPL
11147  * <script type="text/javascript">
11148  */
11149 /**
11150  * @class Roo.data.HttpProxy
11151  * @extends Roo.data.DataProxy
11152  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11153  * configured to reference a certain URL.<br><br>
11154  * <p>
11155  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11156  * from which the running page was served.<br><br>
11157  * <p>
11158  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11159  * <p>
11160  * Be aware that to enable the browser to parse an XML document, the server must set
11161  * the Content-Type header in the HTTP response to "text/xml".
11162  * @constructor
11163  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11164  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11165  * will be used to make the request.
11166  */
11167 Roo.data.HttpProxy = function(conn){
11168     Roo.data.HttpProxy.superclass.constructor.call(this);
11169     // is conn a conn config or a real conn?
11170     this.conn = conn;
11171     this.useAjax = !conn || !conn.events;
11172   
11173 };
11174
11175 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11176     // thse are take from connection...
11177     
11178     /**
11179      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11180      */
11181     /**
11182      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11183      * extra parameters to each request made by this object. (defaults to undefined)
11184      */
11185     /**
11186      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11187      *  to each request made by this object. (defaults to undefined)
11188      */
11189     /**
11190      * @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)
11191      */
11192     /**
11193      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11194      */
11195      /**
11196      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11197      * @type Boolean
11198      */
11199   
11200
11201     /**
11202      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11203      * @type Boolean
11204      */
11205     /**
11206      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11207      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11208      * a finer-grained basis than the DataProxy events.
11209      */
11210     getConnection : function(){
11211         return this.useAjax ? Roo.Ajax : this.conn;
11212     },
11213
11214     /**
11215      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11216      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11217      * process that block using the passed callback.
11218      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11219      * for the request to the remote server.
11220      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11221      * object into a block of Roo.data.Records.
11222      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11223      * The function must be passed <ul>
11224      * <li>The Record block object</li>
11225      * <li>The "arg" argument from the load function</li>
11226      * <li>A boolean success indicator</li>
11227      * </ul>
11228      * @param {Object} scope The scope in which to call the callback
11229      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11230      */
11231     load : function(params, reader, callback, scope, arg){
11232         if(this.fireEvent("beforeload", this, params) !== false){
11233             var  o = {
11234                 params : params || {},
11235                 request: {
11236                     callback : callback,
11237                     scope : scope,
11238                     arg : arg
11239                 },
11240                 reader: reader,
11241                 callback : this.loadResponse,
11242                 scope: this
11243             };
11244             if(this.useAjax){
11245                 Roo.applyIf(o, this.conn);
11246                 if(this.activeRequest){
11247                     Roo.Ajax.abort(this.activeRequest);
11248                 }
11249                 this.activeRequest = Roo.Ajax.request(o);
11250             }else{
11251                 this.conn.request(o);
11252             }
11253         }else{
11254             callback.call(scope||this, null, arg, false);
11255         }
11256     },
11257
11258     // private
11259     loadResponse : function(o, success, response){
11260         delete this.activeRequest;
11261         if(!success){
11262             this.fireEvent("loadexception", this, o, response);
11263             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11264             return;
11265         }
11266         var result;
11267         try {
11268             result = o.reader.read(response);
11269         }catch(e){
11270             this.fireEvent("loadexception", this, o, response, e);
11271             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11272             return;
11273         }
11274         
11275         this.fireEvent("load", this, o, o.request.arg);
11276         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11277     },
11278
11279     // private
11280     update : function(dataSet){
11281
11282     },
11283
11284     // private
11285     updateResponse : function(dataSet){
11286
11287     }
11288 });/*
11289  * Based on:
11290  * Ext JS Library 1.1.1
11291  * Copyright(c) 2006-2007, Ext JS, LLC.
11292  *
11293  * Originally Released Under LGPL - original licence link has changed is not relivant.
11294  *
11295  * Fork - LGPL
11296  * <script type="text/javascript">
11297  */
11298
11299 /**
11300  * @class Roo.data.ScriptTagProxy
11301  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11302  * other than the originating domain of the running page.<br><br>
11303  * <p>
11304  * <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
11305  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11306  * <p>
11307  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11308  * source code that is used as the source inside a &lt;script> tag.<br><br>
11309  * <p>
11310  * In order for the browser to process the returned data, the server must wrap the data object
11311  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11312  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11313  * depending on whether the callback name was passed:
11314  * <p>
11315  * <pre><code>
11316 boolean scriptTag = false;
11317 String cb = request.getParameter("callback");
11318 if (cb != null) {
11319     scriptTag = true;
11320     response.setContentType("text/javascript");
11321 } else {
11322     response.setContentType("application/x-json");
11323 }
11324 Writer out = response.getWriter();
11325 if (scriptTag) {
11326     out.write(cb + "(");
11327 }
11328 out.print(dataBlock.toJsonString());
11329 if (scriptTag) {
11330     out.write(");");
11331 }
11332 </pre></code>
11333  *
11334  * @constructor
11335  * @param {Object} config A configuration object.
11336  */
11337 Roo.data.ScriptTagProxy = function(config){
11338     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11339     Roo.apply(this, config);
11340     this.head = document.getElementsByTagName("head")[0];
11341 };
11342
11343 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11344
11345 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11346     /**
11347      * @cfg {String} url The URL from which to request the data object.
11348      */
11349     /**
11350      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11351      */
11352     timeout : 30000,
11353     /**
11354      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11355      * the server the name of the callback function set up by the load call to process the returned data object.
11356      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11357      * javascript output which calls this named function passing the data object as its only parameter.
11358      */
11359     callbackParam : "callback",
11360     /**
11361      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11362      * name to the request.
11363      */
11364     nocache : true,
11365
11366     /**
11367      * Load data from the configured URL, read the data object into
11368      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11369      * process that block using the passed callback.
11370      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11371      * for the request to the remote server.
11372      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11373      * object into a block of Roo.data.Records.
11374      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11375      * The function must be passed <ul>
11376      * <li>The Record block object</li>
11377      * <li>The "arg" argument from the load function</li>
11378      * <li>A boolean success indicator</li>
11379      * </ul>
11380      * @param {Object} scope The scope in which to call the callback
11381      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11382      */
11383     load : function(params, reader, callback, scope, arg){
11384         if(this.fireEvent("beforeload", this, params) !== false){
11385
11386             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11387
11388             var url = this.url;
11389             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11390             if(this.nocache){
11391                 url += "&_dc=" + (new Date().getTime());
11392             }
11393             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11394             var trans = {
11395                 id : transId,
11396                 cb : "stcCallback"+transId,
11397                 scriptId : "stcScript"+transId,
11398                 params : params,
11399                 arg : arg,
11400                 url : url,
11401                 callback : callback,
11402                 scope : scope,
11403                 reader : reader
11404             };
11405             var conn = this;
11406
11407             window[trans.cb] = function(o){
11408                 conn.handleResponse(o, trans);
11409             };
11410
11411             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11412
11413             if(this.autoAbort !== false){
11414                 this.abort();
11415             }
11416
11417             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11418
11419             var script = document.createElement("script");
11420             script.setAttribute("src", url);
11421             script.setAttribute("type", "text/javascript");
11422             script.setAttribute("id", trans.scriptId);
11423             this.head.appendChild(script);
11424
11425             this.trans = trans;
11426         }else{
11427             callback.call(scope||this, null, arg, false);
11428         }
11429     },
11430
11431     // private
11432     isLoading : function(){
11433         return this.trans ? true : false;
11434     },
11435
11436     /**
11437      * Abort the current server request.
11438      */
11439     abort : function(){
11440         if(this.isLoading()){
11441             this.destroyTrans(this.trans);
11442         }
11443     },
11444
11445     // private
11446     destroyTrans : function(trans, isLoaded){
11447         this.head.removeChild(document.getElementById(trans.scriptId));
11448         clearTimeout(trans.timeoutId);
11449         if(isLoaded){
11450             window[trans.cb] = undefined;
11451             try{
11452                 delete window[trans.cb];
11453             }catch(e){}
11454         }else{
11455             // if hasn't been loaded, wait for load to remove it to prevent script error
11456             window[trans.cb] = function(){
11457                 window[trans.cb] = undefined;
11458                 try{
11459                     delete window[trans.cb];
11460                 }catch(e){}
11461             };
11462         }
11463     },
11464
11465     // private
11466     handleResponse : function(o, trans){
11467         this.trans = false;
11468         this.destroyTrans(trans, true);
11469         var result;
11470         try {
11471             result = trans.reader.readRecords(o);
11472         }catch(e){
11473             this.fireEvent("loadexception", this, o, trans.arg, e);
11474             trans.callback.call(trans.scope||window, null, trans.arg, false);
11475             return;
11476         }
11477         this.fireEvent("load", this, o, trans.arg);
11478         trans.callback.call(trans.scope||window, result, trans.arg, true);
11479     },
11480
11481     // private
11482     handleFailure : function(trans){
11483         this.trans = false;
11484         this.destroyTrans(trans, false);
11485         this.fireEvent("loadexception", this, null, trans.arg);
11486         trans.callback.call(trans.scope||window, null, trans.arg, false);
11487     }
11488 });/*
11489  * Based on:
11490  * Ext JS Library 1.1.1
11491  * Copyright(c) 2006-2007, Ext JS, LLC.
11492  *
11493  * Originally Released Under LGPL - original licence link has changed is not relivant.
11494  *
11495  * Fork - LGPL
11496  * <script type="text/javascript">
11497  */
11498
11499 /**
11500  * @class Roo.data.JsonReader
11501  * @extends Roo.data.DataReader
11502  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11503  * based on mappings in a provided Roo.data.Record constructor.
11504  * 
11505  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11506  * in the reply previously. 
11507  * 
11508  * <p>
11509  * Example code:
11510  * <pre><code>
11511 var RecordDef = Roo.data.Record.create([
11512     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11513     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11514 ]);
11515 var myReader = new Roo.data.JsonReader({
11516     totalProperty: "results",    // The property which contains the total dataset size (optional)
11517     root: "rows",                // The property which contains an Array of row objects
11518     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11519 }, RecordDef);
11520 </code></pre>
11521  * <p>
11522  * This would consume a JSON file like this:
11523  * <pre><code>
11524 { 'results': 2, 'rows': [
11525     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11526     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11527 }
11528 </code></pre>
11529  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11530  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11531  * paged from the remote server.
11532  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11533  * @cfg {String} root name of the property which contains the Array of row objects.
11534  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11535  * @cfg {Array} fields Array of field definition objects
11536  * @constructor
11537  * Create a new JsonReader
11538  * @param {Object} meta Metadata configuration options
11539  * @param {Object} recordType Either an Array of field definition objects,
11540  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11541  */
11542 Roo.data.JsonReader = function(meta, recordType){
11543     
11544     meta = meta || {};
11545     // set some defaults:
11546     Roo.applyIf(meta, {
11547         totalProperty: 'total',
11548         successProperty : 'success',
11549         root : 'data',
11550         id : 'id'
11551     });
11552     
11553     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11554 };
11555 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11556     
11557     /**
11558      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11559      * Used by Store query builder to append _requestMeta to params.
11560      * 
11561      */
11562     metaFromRemote : false,
11563     /**
11564      * This method is only used by a DataProxy which has retrieved data from a remote server.
11565      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11566      * @return {Object} data A data block which is used by an Roo.data.Store object as
11567      * a cache of Roo.data.Records.
11568      */
11569     read : function(response){
11570         var json = response.responseText;
11571        
11572         var o = /* eval:var:o */ eval("("+json+")");
11573         if(!o) {
11574             throw {message: "JsonReader.read: Json object not found"};
11575         }
11576         
11577         if(o.metaData){
11578             
11579             delete this.ef;
11580             this.metaFromRemote = true;
11581             this.meta = o.metaData;
11582             this.recordType = Roo.data.Record.create(o.metaData.fields);
11583             this.onMetaChange(this.meta, this.recordType, o);
11584         }
11585         return this.readRecords(o);
11586     },
11587
11588     // private function a store will implement
11589     onMetaChange : function(meta, recordType, o){
11590
11591     },
11592
11593     /**
11594          * @ignore
11595          */
11596     simpleAccess: function(obj, subsc) {
11597         return obj[subsc];
11598     },
11599
11600         /**
11601          * @ignore
11602          */
11603     getJsonAccessor: function(){
11604         var re = /[\[\.]/;
11605         return function(expr) {
11606             try {
11607                 return(re.test(expr))
11608                     ? new Function("obj", "return obj." + expr)
11609                     : function(obj){
11610                         return obj[expr];
11611                     };
11612             } catch(e){}
11613             return Roo.emptyFn;
11614         };
11615     }(),
11616
11617     /**
11618      * Create a data block containing Roo.data.Records from an XML document.
11619      * @param {Object} o An object which contains an Array of row objects in the property specified
11620      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11621      * which contains the total size of the dataset.
11622      * @return {Object} data A data block which is used by an Roo.data.Store object as
11623      * a cache of Roo.data.Records.
11624      */
11625     readRecords : function(o){
11626         /**
11627          * After any data loads, the raw JSON data is available for further custom processing.
11628          * @type Object
11629          */
11630         this.o = o;
11631         var s = this.meta, Record = this.recordType,
11632             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11633
11634 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11635         if (!this.ef) {
11636             if(s.totalProperty) {
11637                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11638                 }
11639                 if(s.successProperty) {
11640                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11641                 }
11642                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11643                 if (s.id) {
11644                         var g = this.getJsonAccessor(s.id);
11645                         this.getId = function(rec) {
11646                                 var r = g(rec);  
11647                                 return (r === undefined || r === "") ? null : r;
11648                         };
11649                 } else {
11650                         this.getId = function(){return null;};
11651                 }
11652             this.ef = [];
11653             for(var jj = 0; jj < fl; jj++){
11654                 f = fi[jj];
11655                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11656                 this.ef[jj] = this.getJsonAccessor(map);
11657             }
11658         }
11659
11660         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11661         if(s.totalProperty){
11662             var vt = parseInt(this.getTotal(o), 10);
11663             if(!isNaN(vt)){
11664                 totalRecords = vt;
11665             }
11666         }
11667         if(s.successProperty){
11668             var vs = this.getSuccess(o);
11669             if(vs === false || vs === 'false'){
11670                 success = false;
11671             }
11672         }
11673         var records = [];
11674         for(var i = 0; i < c; i++){
11675                 var n = root[i];
11676             var values = {};
11677             var id = this.getId(n);
11678             for(var j = 0; j < fl; j++){
11679                 f = fi[j];
11680             var v = this.ef[j](n);
11681             if (!f.convert) {
11682                 Roo.log('missing convert for ' + f.name);
11683                 Roo.log(f);
11684                 continue;
11685             }
11686             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11687             }
11688             var record = new Record(values, id);
11689             record.json = n;
11690             records[i] = record;
11691         }
11692         return {
11693             raw : o,
11694             success : success,
11695             records : records,
11696             totalRecords : totalRecords
11697         };
11698     }
11699 });/*
11700  * Based on:
11701  * Ext JS Library 1.1.1
11702  * Copyright(c) 2006-2007, Ext JS, LLC.
11703  *
11704  * Originally Released Under LGPL - original licence link has changed is not relivant.
11705  *
11706  * Fork - LGPL
11707  * <script type="text/javascript">
11708  */
11709
11710 /**
11711  * @class Roo.data.ArrayReader
11712  * @extends Roo.data.DataReader
11713  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11714  * Each element of that Array represents a row of data fields. The
11715  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11716  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11717  * <p>
11718  * Example code:.
11719  * <pre><code>
11720 var RecordDef = Roo.data.Record.create([
11721     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11722     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11723 ]);
11724 var myReader = new Roo.data.ArrayReader({
11725     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11726 }, RecordDef);
11727 </code></pre>
11728  * <p>
11729  * This would consume an Array like this:
11730  * <pre><code>
11731 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11732   </code></pre>
11733  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11734  * @constructor
11735  * Create a new JsonReader
11736  * @param {Object} meta Metadata configuration options.
11737  * @param {Object} recordType Either an Array of field definition objects
11738  * as specified to {@link Roo.data.Record#create},
11739  * or an {@link Roo.data.Record} object
11740  * created using {@link Roo.data.Record#create}.
11741  */
11742 Roo.data.ArrayReader = function(meta, recordType){
11743     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11744 };
11745
11746 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11747     /**
11748      * Create a data block containing Roo.data.Records from an XML document.
11749      * @param {Object} o An Array of row objects which represents the dataset.
11750      * @return {Object} data A data block which is used by an Roo.data.Store object as
11751      * a cache of Roo.data.Records.
11752      */
11753     readRecords : function(o){
11754         var sid = this.meta ? this.meta.id : null;
11755         var recordType = this.recordType, fields = recordType.prototype.fields;
11756         var records = [];
11757         var root = o;
11758             for(var i = 0; i < root.length; i++){
11759                     var n = root[i];
11760                 var values = {};
11761                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11762                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11763                 var f = fields.items[j];
11764                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11765                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11766                 v = f.convert(v);
11767                 values[f.name] = v;
11768             }
11769                 var record = new recordType(values, id);
11770                 record.json = n;
11771                 records[records.length] = record;
11772             }
11773             return {
11774                 records : records,
11775                 totalRecords : records.length
11776             };
11777     }
11778 });/*
11779  * - LGPL
11780  * * 
11781  */
11782
11783 /**
11784  * @class Roo.bootstrap.ComboBox
11785  * @extends Roo.bootstrap.TriggerField
11786  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11787  * @cfg {Boolean} append (true|false) default false
11788  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11789  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11790  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11791  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11792  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11793  * @cfg {Boolean} animate default true
11794  * @cfg {Boolean} emptyResultText only for touch device
11795  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11796  * @constructor
11797  * Create a new ComboBox.
11798  * @param {Object} config Configuration options
11799  */
11800 Roo.bootstrap.ComboBox = function(config){
11801     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11802     this.addEvents({
11803         /**
11804          * @event expand
11805          * Fires when the dropdown list is expanded
11806              * @param {Roo.bootstrap.ComboBox} combo This combo box
11807              */
11808         'expand' : true,
11809         /**
11810          * @event collapse
11811          * Fires when the dropdown list is collapsed
11812              * @param {Roo.bootstrap.ComboBox} combo This combo box
11813              */
11814         'collapse' : true,
11815         /**
11816          * @event beforeselect
11817          * Fires before a list item is selected. Return false to cancel the selection.
11818              * @param {Roo.bootstrap.ComboBox} combo This combo box
11819              * @param {Roo.data.Record} record The data record returned from the underlying store
11820              * @param {Number} index The index of the selected item in the dropdown list
11821              */
11822         'beforeselect' : true,
11823         /**
11824          * @event select
11825          * Fires when a list item is selected
11826              * @param {Roo.bootstrap.ComboBox} combo This combo box
11827              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11828              * @param {Number} index The index of the selected item in the dropdown list
11829              */
11830         'select' : true,
11831         /**
11832          * @event beforequery
11833          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11834          * The event object passed has these properties:
11835              * @param {Roo.bootstrap.ComboBox} combo This combo box
11836              * @param {String} query The query
11837              * @param {Boolean} forceAll true to force "all" query
11838              * @param {Boolean} cancel true to cancel the query
11839              * @param {Object} e The query event object
11840              */
11841         'beforequery': true,
11842          /**
11843          * @event add
11844          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11845              * @param {Roo.bootstrap.ComboBox} combo This combo box
11846              */
11847         'add' : true,
11848         /**
11849          * @event edit
11850          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11851              * @param {Roo.bootstrap.ComboBox} combo This combo box
11852              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11853              */
11854         'edit' : true,
11855         /**
11856          * @event remove
11857          * Fires when the remove value from the combobox array
11858              * @param {Roo.bootstrap.ComboBox} combo This combo box
11859              */
11860         'remove' : true,
11861         /**
11862          * @event afterremove
11863          * Fires when the remove value from the combobox array
11864              * @param {Roo.bootstrap.ComboBox} combo This combo box
11865              */
11866         'afterremove' : true,
11867         /**
11868          * @event specialfilter
11869          * Fires when specialfilter
11870             * @param {Roo.bootstrap.ComboBox} combo This combo box
11871             */
11872         'specialfilter' : true,
11873         /**
11874          * @event tick
11875          * Fires when tick the element
11876             * @param {Roo.bootstrap.ComboBox} combo This combo box
11877             */
11878         'tick' : true,
11879         /**
11880          * @event touchviewdisplay
11881          * Fires when touch view require special display (default is using displayField)
11882             * @param {Roo.bootstrap.ComboBox} combo This combo box
11883             * @param {Object} cfg set html .
11884             */
11885         'touchviewdisplay' : true
11886         
11887     });
11888     
11889     this.item = [];
11890     this.tickItems = [];
11891     
11892     this.selectedIndex = -1;
11893     if(this.mode == 'local'){
11894         if(config.queryDelay === undefined){
11895             this.queryDelay = 10;
11896         }
11897         if(config.minChars === undefined){
11898             this.minChars = 0;
11899         }
11900     }
11901 };
11902
11903 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11904      
11905     /**
11906      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11907      * rendering into an Roo.Editor, defaults to false)
11908      */
11909     /**
11910      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11911      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11912      */
11913     /**
11914      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11915      */
11916     /**
11917      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11918      * the dropdown list (defaults to undefined, with no header element)
11919      */
11920
11921      /**
11922      * @cfg {String/Roo.Template} tpl The template to use to render the output
11923      */
11924      
11925      /**
11926      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11927      */
11928     listWidth: undefined,
11929     /**
11930      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11931      * mode = 'remote' or 'text' if mode = 'local')
11932      */
11933     displayField: undefined,
11934     
11935     /**
11936      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11937      * mode = 'remote' or 'value' if mode = 'local'). 
11938      * Note: use of a valueField requires the user make a selection
11939      * in order for a value to be mapped.
11940      */
11941     valueField: undefined,
11942     
11943     
11944     /**
11945      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11946      * field's data value (defaults to the underlying DOM element's name)
11947      */
11948     hiddenName: undefined,
11949     /**
11950      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11951      */
11952     listClass: '',
11953     /**
11954      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11955      */
11956     selectedClass: 'active',
11957     
11958     /**
11959      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11960      */
11961     shadow:'sides',
11962     /**
11963      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11964      * anchor positions (defaults to 'tl-bl')
11965      */
11966     listAlign: 'tl-bl?',
11967     /**
11968      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11969      */
11970     maxHeight: 300,
11971     /**
11972      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11973      * query specified by the allQuery config option (defaults to 'query')
11974      */
11975     triggerAction: 'query',
11976     /**
11977      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11978      * (defaults to 4, does not apply if editable = false)
11979      */
11980     minChars : 4,
11981     /**
11982      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11983      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11984      */
11985     typeAhead: false,
11986     /**
11987      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11988      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11989      */
11990     queryDelay: 500,
11991     /**
11992      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11993      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11994      */
11995     pageSize: 0,
11996     /**
11997      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11998      * when editable = true (defaults to false)
11999      */
12000     selectOnFocus:false,
12001     /**
12002      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12003      */
12004     queryParam: 'query',
12005     /**
12006      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12007      * when mode = 'remote' (defaults to 'Loading...')
12008      */
12009     loadingText: 'Loading...',
12010     /**
12011      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12012      */
12013     resizable: false,
12014     /**
12015      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12016      */
12017     handleHeight : 8,
12018     /**
12019      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12020      * traditional select (defaults to true)
12021      */
12022     editable: true,
12023     /**
12024      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12025      */
12026     allQuery: '',
12027     /**
12028      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12029      */
12030     mode: 'remote',
12031     /**
12032      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12033      * listWidth has a higher value)
12034      */
12035     minListWidth : 70,
12036     /**
12037      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12038      * allow the user to set arbitrary text into the field (defaults to false)
12039      */
12040     forceSelection:false,
12041     /**
12042      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12043      * if typeAhead = true (defaults to 250)
12044      */
12045     typeAheadDelay : 250,
12046     /**
12047      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12048      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12049      */
12050     valueNotFoundText : undefined,
12051     /**
12052      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12053      */
12054     blockFocus : false,
12055     
12056     /**
12057      * @cfg {Boolean} disableClear Disable showing of clear button.
12058      */
12059     disableClear : false,
12060     /**
12061      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12062      */
12063     alwaysQuery : false,
12064     
12065     /**
12066      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12067      */
12068     multiple : false,
12069     
12070     /**
12071      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12072      */
12073     invalidClass : "has-warning",
12074     
12075     /**
12076      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12077      */
12078     validClass : "has-success",
12079     
12080     /**
12081      * @cfg {Boolean} specialFilter (true|false) special filter default false
12082      */
12083     specialFilter : false,
12084     
12085     /**
12086      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12087      */
12088     mobileTouchView : true,
12089     
12090     //private
12091     addicon : false,
12092     editicon: false,
12093     
12094     page: 0,
12095     hasQuery: false,
12096     append: false,
12097     loadNext: false,
12098     autoFocus : true,
12099     tickable : false,
12100     btnPosition : 'right',
12101     triggerList : true,
12102     showToggleBtn : true,
12103     animate : true,
12104     emptyResultText: 'Empty',
12105     triggerText : 'Select',
12106     
12107     // element that contains real text value.. (when hidden is used..)
12108     
12109     getAutoCreate : function()
12110     {
12111         var cfg = false;
12112         
12113         /*
12114          * Touch Devices
12115          */
12116         
12117         if(Roo.isTouch && this.mobileTouchView){
12118             cfg = this.getAutoCreateTouchView();
12119             return cfg;;
12120         }
12121         
12122         /*
12123          *  Normal ComboBox
12124          */
12125         if(!this.tickable){
12126             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12127             return cfg;
12128         }
12129         
12130         /*
12131          *  ComboBox with tickable selections
12132          */
12133              
12134         var align = this.labelAlign || this.parentLabelAlign();
12135         
12136         cfg = {
12137             cls : 'form-group roo-combobox-tickable' //input-group
12138         };
12139         
12140         var buttons = {
12141             tag : 'div',
12142             cls : 'tickable-buttons',
12143             cn : [
12144                 {
12145                     tag : 'button',
12146                     type : 'button',
12147                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12148                     html : this.triggerText
12149                 },
12150                 {
12151                     tag : 'button',
12152                     type : 'button',
12153                     name : 'ok',
12154                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12155                     html : 'Done'
12156                 },
12157                 {
12158                     tag : 'button',
12159                     type : 'button',
12160                     name : 'cancel',
12161                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12162                     html : 'Cancel'
12163                 }
12164             ]
12165         };
12166         
12167         if(this.editable){
12168             buttons.cn.unshift({
12169                 tag: 'input',
12170                 cls: 'roo-select2-search-field-input'
12171             });
12172         }
12173         
12174         var _this = this;
12175         
12176         Roo.each(buttons.cn, function(c){
12177             if (_this.size) {
12178                 c.cls += ' btn-' + _this.size;
12179             }
12180
12181             if (_this.disabled) {
12182                 c.disabled = true;
12183             }
12184         });
12185         
12186         var box = {
12187             tag: 'div',
12188             cn: [
12189                 {
12190                     tag: 'input',
12191                     type : 'hidden',
12192                     cls: 'form-hidden-field'
12193                 },
12194                 {
12195                     tag: 'ul',
12196                     cls: 'roo-select2-choices',
12197                     cn:[
12198                         {
12199                             tag: 'li',
12200                             cls: 'roo-select2-search-field',
12201                             cn: [
12202
12203                                 buttons
12204                             ]
12205                         }
12206                     ]
12207                 }
12208             ]
12209         };
12210         
12211         var combobox = {
12212             cls: 'roo-select2-container input-group roo-select2-container-multi',
12213             cn: [
12214                 box
12215 //                {
12216 //                    tag: 'ul',
12217 //                    cls: 'typeahead typeahead-long dropdown-menu',
12218 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12219 //                }
12220             ]
12221         };
12222         
12223         if(this.hasFeedback && !this.allowBlank){
12224             
12225             var feedback = {
12226                 tag: 'span',
12227                 cls: 'glyphicon form-control-feedback'
12228             };
12229
12230             combobox.cn.push(feedback);
12231         }
12232         
12233         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12234             
12235 //                Roo.log("left and has label");
12236                 cfg.cn = [
12237                     
12238                     {
12239                         tag: 'label',
12240                         'for' :  id,
12241                         cls : 'control-label col-sm-' + this.labelWidth,
12242                         html : this.fieldLabel
12243                         
12244                     },
12245                     {
12246                         cls : "col-sm-" + (12 - this.labelWidth), 
12247                         cn: [
12248                             combobox
12249                         ]
12250                     }
12251                     
12252                 ];
12253         } else if ( this.fieldLabel.length) {
12254 //                Roo.log(" label");
12255                  cfg.cn = [
12256                    
12257                     {
12258                         tag: 'label',
12259                         //cls : 'input-group-addon',
12260                         html : this.fieldLabel
12261                         
12262                     },
12263                     
12264                     combobox
12265                     
12266                 ];
12267
12268         } else {
12269             
12270 //                Roo.log(" no label && no align");
12271                 cfg = combobox
12272                      
12273                 
12274         }
12275          
12276         var settings=this;
12277         ['xs','sm','md','lg'].map(function(size){
12278             if (settings[size]) {
12279                 cfg.cls += ' col-' + size + '-' + settings[size];
12280             }
12281         });
12282         
12283         return cfg;
12284         
12285     },
12286     
12287     _initEventsCalled : false,
12288     
12289     // private
12290     initEvents: function()
12291     {
12292         
12293         if (this._initEventsCalled) { // as we call render... prevent looping...
12294             return;
12295         }
12296         this._initEventsCalled = true;
12297         
12298         if (!this.store) {
12299             throw "can not find store for combo";
12300         }
12301         
12302         this.store = Roo.factory(this.store, Roo.data);
12303         
12304         // if we are building from html. then this element is so complex, that we can not really
12305         // use the rendered HTML.
12306         // so we have to trash and replace the previous code.
12307         if (Roo.XComponent.build_from_html) {
12308             
12309             // remove this element....
12310             var e = this.el.dom, k=0;
12311             while (e ) { e = e.previousSibling;  ++k;}
12312
12313             this.el.remove();
12314             
12315             this.el=false;
12316             this.rendered = false;
12317             
12318             this.render(this.parent().getChildContainer(true), k);
12319             
12320             
12321             
12322         }
12323         
12324         
12325         /*
12326          * Touch Devices
12327          */
12328         
12329         if(Roo.isTouch && this.mobileTouchView){
12330             this.initTouchView();
12331             return;
12332         }
12333         
12334         if(this.tickable){
12335             this.initTickableEvents();
12336             return;
12337         }
12338         
12339         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12340         
12341         if(this.hiddenName){
12342             
12343             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12344             
12345             this.hiddenField.dom.value =
12346                 this.hiddenValue !== undefined ? this.hiddenValue :
12347                 this.value !== undefined ? this.value : '';
12348
12349             // prevent input submission
12350             this.el.dom.removeAttribute('name');
12351             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12352              
12353              
12354         }
12355         //if(Roo.isGecko){
12356         //    this.el.dom.setAttribute('autocomplete', 'off');
12357         //}
12358         
12359         var cls = 'x-combo-list';
12360         
12361         //this.list = new Roo.Layer({
12362         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12363         //});
12364         
12365         var _this = this;
12366         
12367         (function(){
12368             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12369             _this.list.setWidth(lw);
12370         }).defer(100);
12371         
12372         this.list.on('mouseover', this.onViewOver, this);
12373         this.list.on('mousemove', this.onViewMove, this);
12374         
12375         this.list.on('scroll', this.onViewScroll, this);
12376         
12377         /*
12378         this.list.swallowEvent('mousewheel');
12379         this.assetHeight = 0;
12380
12381         if(this.title){
12382             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12383             this.assetHeight += this.header.getHeight();
12384         }
12385
12386         this.innerList = this.list.createChild({cls:cls+'-inner'});
12387         this.innerList.on('mouseover', this.onViewOver, this);
12388         this.innerList.on('mousemove', this.onViewMove, this);
12389         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12390         
12391         if(this.allowBlank && !this.pageSize && !this.disableClear){
12392             this.footer = this.list.createChild({cls:cls+'-ft'});
12393             this.pageTb = new Roo.Toolbar(this.footer);
12394            
12395         }
12396         if(this.pageSize){
12397             this.footer = this.list.createChild({cls:cls+'-ft'});
12398             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12399                     {pageSize: this.pageSize});
12400             
12401         }
12402         
12403         if (this.pageTb && this.allowBlank && !this.disableClear) {
12404             var _this = this;
12405             this.pageTb.add(new Roo.Toolbar.Fill(), {
12406                 cls: 'x-btn-icon x-btn-clear',
12407                 text: '&#160;',
12408                 handler: function()
12409                 {
12410                     _this.collapse();
12411                     _this.clearValue();
12412                     _this.onSelect(false, -1);
12413                 }
12414             });
12415         }
12416         if (this.footer) {
12417             this.assetHeight += this.footer.getHeight();
12418         }
12419         */
12420             
12421         if(!this.tpl){
12422             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12423         }
12424
12425         this.view = new Roo.View(this.list, this.tpl, {
12426             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12427         });
12428         //this.view.wrapEl.setDisplayed(false);
12429         this.view.on('click', this.onViewClick, this);
12430         
12431         
12432         
12433         this.store.on('beforeload', this.onBeforeLoad, this);
12434         this.store.on('load', this.onLoad, this);
12435         this.store.on('loadexception', this.onLoadException, this);
12436         /*
12437         if(this.resizable){
12438             this.resizer = new Roo.Resizable(this.list,  {
12439                pinned:true, handles:'se'
12440             });
12441             this.resizer.on('resize', function(r, w, h){
12442                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12443                 this.listWidth = w;
12444                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12445                 this.restrictHeight();
12446             }, this);
12447             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12448         }
12449         */
12450         if(!this.editable){
12451             this.editable = true;
12452             this.setEditable(false);
12453         }
12454         
12455         /*
12456         
12457         if (typeof(this.events.add.listeners) != 'undefined') {
12458             
12459             this.addicon = this.wrap.createChild(
12460                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12461        
12462             this.addicon.on('click', function(e) {
12463                 this.fireEvent('add', this);
12464             }, this);
12465         }
12466         if (typeof(this.events.edit.listeners) != 'undefined') {
12467             
12468             this.editicon = this.wrap.createChild(
12469                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12470             if (this.addicon) {
12471                 this.editicon.setStyle('margin-left', '40px');
12472             }
12473             this.editicon.on('click', function(e) {
12474                 
12475                 // we fire even  if inothing is selected..
12476                 this.fireEvent('edit', this, this.lastData );
12477                 
12478             }, this);
12479         }
12480         */
12481         
12482         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12483             "up" : function(e){
12484                 this.inKeyMode = true;
12485                 this.selectPrev();
12486             },
12487
12488             "down" : function(e){
12489                 if(!this.isExpanded()){
12490                     this.onTriggerClick();
12491                 }else{
12492                     this.inKeyMode = true;
12493                     this.selectNext();
12494                 }
12495             },
12496
12497             "enter" : function(e){
12498 //                this.onViewClick();
12499                 //return true;
12500                 this.collapse();
12501                 
12502                 if(this.fireEvent("specialkey", this, e)){
12503                     this.onViewClick(false);
12504                 }
12505                 
12506                 return true;
12507             },
12508
12509             "esc" : function(e){
12510                 this.collapse();
12511             },
12512
12513             "tab" : function(e){
12514                 this.collapse();
12515                 
12516                 if(this.fireEvent("specialkey", this, e)){
12517                     this.onViewClick(false);
12518                 }
12519                 
12520                 return true;
12521             },
12522
12523             scope : this,
12524
12525             doRelay : function(foo, bar, hname){
12526                 if(hname == 'down' || this.scope.isExpanded()){
12527                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12528                 }
12529                 return true;
12530             },
12531
12532             forceKeyDown: true
12533         });
12534         
12535         
12536         this.queryDelay = Math.max(this.queryDelay || 10,
12537                 this.mode == 'local' ? 10 : 250);
12538         
12539         
12540         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12541         
12542         if(this.typeAhead){
12543             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12544         }
12545         if(this.editable !== false){
12546             this.inputEl().on("keyup", this.onKeyUp, this);
12547         }
12548         if(this.forceSelection){
12549             this.inputEl().on('blur', this.doForce, this);
12550         }
12551         
12552         if(this.multiple){
12553             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12554             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12555         }
12556     },
12557     
12558     initTickableEvents: function()
12559     {   
12560         this.createList();
12561         
12562         if(this.hiddenName){
12563             
12564             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12565             
12566             this.hiddenField.dom.value =
12567                 this.hiddenValue !== undefined ? this.hiddenValue :
12568                 this.value !== undefined ? this.value : '';
12569
12570             // prevent input submission
12571             this.el.dom.removeAttribute('name');
12572             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12573              
12574              
12575         }
12576         
12577 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12578         
12579         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12580         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12581         if(this.triggerList){
12582             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12583         }
12584          
12585         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12586         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12587         
12588         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12589         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12590         
12591         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12592         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12593         
12594         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12595         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12596         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12597         
12598         this.okBtn.hide();
12599         this.cancelBtn.hide();
12600         
12601         var _this = this;
12602         
12603         (function(){
12604             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12605             _this.list.setWidth(lw);
12606         }).defer(100);
12607         
12608         this.list.on('mouseover', this.onViewOver, this);
12609         this.list.on('mousemove', this.onViewMove, this);
12610         
12611         this.list.on('scroll', this.onViewScroll, this);
12612         
12613         if(!this.tpl){
12614             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12615         }
12616
12617         this.view = new Roo.View(this.list, this.tpl, {
12618             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12619         });
12620         
12621         //this.view.wrapEl.setDisplayed(false);
12622         this.view.on('click', this.onViewClick, this);
12623         
12624         
12625         
12626         this.store.on('beforeload', this.onBeforeLoad, this);
12627         this.store.on('load', this.onLoad, this);
12628         this.store.on('loadexception', this.onLoadException, this);
12629         
12630         if(this.editable){
12631             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12632                 "up" : function(e){
12633                     this.inKeyMode = true;
12634                     this.selectPrev();
12635                 },
12636
12637                 "down" : function(e){
12638                     this.inKeyMode = true;
12639                     this.selectNext();
12640                 },
12641
12642                 "enter" : function(e){
12643                     if(this.fireEvent("specialkey", this, e)){
12644                         this.onViewClick(false);
12645                     }
12646                     
12647                     return true;
12648                 },
12649
12650                 "esc" : function(e){
12651                     this.onTickableFooterButtonClick(e, false, false);
12652                 },
12653
12654                 "tab" : function(e){
12655                     this.fireEvent("specialkey", this, e);
12656                     
12657                     this.onTickableFooterButtonClick(e, false, false);
12658                     
12659                     return true;
12660                 },
12661
12662                 scope : this,
12663
12664                 doRelay : function(e, fn, key){
12665                     if(this.scope.isExpanded()){
12666                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12667                     }
12668                     return true;
12669                 },
12670
12671                 forceKeyDown: true
12672             });
12673         }
12674         
12675         this.queryDelay = Math.max(this.queryDelay || 10,
12676                 this.mode == 'local' ? 10 : 250);
12677         
12678         
12679         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12680         
12681         if(this.typeAhead){
12682             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12683         }
12684         
12685         if(this.editable !== false){
12686             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12687         }
12688         
12689     },
12690
12691     onDestroy : function(){
12692         if(this.view){
12693             this.view.setStore(null);
12694             this.view.el.removeAllListeners();
12695             this.view.el.remove();
12696             this.view.purgeListeners();
12697         }
12698         if(this.list){
12699             this.list.dom.innerHTML  = '';
12700         }
12701         
12702         if(this.store){
12703             this.store.un('beforeload', this.onBeforeLoad, this);
12704             this.store.un('load', this.onLoad, this);
12705             this.store.un('loadexception', this.onLoadException, this);
12706         }
12707         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12708     },
12709
12710     // private
12711     fireKey : function(e){
12712         if(e.isNavKeyPress() && !this.list.isVisible()){
12713             this.fireEvent("specialkey", this, e);
12714         }
12715     },
12716
12717     // private
12718     onResize: function(w, h){
12719 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12720 //        
12721 //        if(typeof w != 'number'){
12722 //            // we do not handle it!?!?
12723 //            return;
12724 //        }
12725 //        var tw = this.trigger.getWidth();
12726 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12727 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12728 //        var x = w - tw;
12729 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12730 //            
12731 //        //this.trigger.setStyle('left', x+'px');
12732 //        
12733 //        if(this.list && this.listWidth === undefined){
12734 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12735 //            this.list.setWidth(lw);
12736 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12737 //        }
12738         
12739     
12740         
12741     },
12742
12743     /**
12744      * Allow or prevent the user from directly editing the field text.  If false is passed,
12745      * the user will only be able to select from the items defined in the dropdown list.  This method
12746      * is the runtime equivalent of setting the 'editable' config option at config time.
12747      * @param {Boolean} value True to allow the user to directly edit the field text
12748      */
12749     setEditable : function(value){
12750         if(value == this.editable){
12751             return;
12752         }
12753         this.editable = value;
12754         if(!value){
12755             this.inputEl().dom.setAttribute('readOnly', true);
12756             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12757             this.inputEl().addClass('x-combo-noedit');
12758         }else{
12759             this.inputEl().dom.setAttribute('readOnly', false);
12760             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12761             this.inputEl().removeClass('x-combo-noedit');
12762         }
12763     },
12764
12765     // private
12766     
12767     onBeforeLoad : function(combo,opts){
12768         if(!this.hasFocus){
12769             return;
12770         }
12771          if (!opts.add) {
12772             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12773          }
12774         this.restrictHeight();
12775         this.selectedIndex = -1;
12776     },
12777
12778     // private
12779     onLoad : function(){
12780         
12781         this.hasQuery = false;
12782         
12783         if(!this.hasFocus){
12784             return;
12785         }
12786         
12787         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12788             this.loading.hide();
12789         }
12790              
12791         if(this.store.getCount() > 0){
12792             this.expand();
12793             this.restrictHeight();
12794             if(this.lastQuery == this.allQuery){
12795                 if(this.editable && !this.tickable){
12796                     this.inputEl().dom.select();
12797                 }
12798                 
12799                 if(
12800                     !this.selectByValue(this.value, true) &&
12801                     this.autoFocus && 
12802                     (
12803                         !this.store.lastOptions ||
12804                         typeof(this.store.lastOptions.add) == 'undefined' || 
12805                         this.store.lastOptions.add != true
12806                     )
12807                 ){
12808                     this.select(0, true);
12809                 }
12810             }else{
12811                 if(this.autoFocus){
12812                     this.selectNext();
12813                 }
12814                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12815                     this.taTask.delay(this.typeAheadDelay);
12816                 }
12817             }
12818         }else{
12819             this.onEmptyResults();
12820         }
12821         
12822         //this.el.focus();
12823     },
12824     // private
12825     onLoadException : function()
12826     {
12827         this.hasQuery = false;
12828         
12829         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12830             this.loading.hide();
12831         }
12832         
12833         if(this.tickable && this.editable){
12834             return;
12835         }
12836         
12837         this.collapse();
12838         // only causes errors at present
12839         //Roo.log(this.store.reader.jsonData);
12840         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12841             // fixme
12842             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12843         //}
12844         
12845         
12846     },
12847     // private
12848     onTypeAhead : function(){
12849         if(this.store.getCount() > 0){
12850             var r = this.store.getAt(0);
12851             var newValue = r.data[this.displayField];
12852             var len = newValue.length;
12853             var selStart = this.getRawValue().length;
12854             
12855             if(selStart != len){
12856                 this.setRawValue(newValue);
12857                 this.selectText(selStart, newValue.length);
12858             }
12859         }
12860     },
12861
12862     // private
12863     onSelect : function(record, index){
12864         
12865         if(this.fireEvent('beforeselect', this, record, index) !== false){
12866         
12867             this.setFromData(index > -1 ? record.data : false);
12868             
12869             this.collapse();
12870             this.fireEvent('select', this, record, index);
12871         }
12872     },
12873
12874     /**
12875      * Returns the currently selected field value or empty string if no value is set.
12876      * @return {String} value The selected value
12877      */
12878     getValue : function(){
12879         
12880         if(this.multiple){
12881             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12882         }
12883         
12884         if(this.valueField){
12885             return typeof this.value != 'undefined' ? this.value : '';
12886         }else{
12887             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12888         }
12889     },
12890
12891     /**
12892      * Clears any text/value currently set in the field
12893      */
12894     clearValue : function(){
12895         if(this.hiddenField){
12896             this.hiddenField.dom.value = '';
12897         }
12898         this.value = '';
12899         this.setRawValue('');
12900         this.lastSelectionText = '';
12901         this.lastData = false;
12902         
12903         var close = this.closeTriggerEl();
12904         
12905         if(close){
12906             close.hide();
12907         }
12908         
12909     },
12910
12911     /**
12912      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12913      * will be displayed in the field.  If the value does not match the data value of an existing item,
12914      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12915      * Otherwise the field will be blank (although the value will still be set).
12916      * @param {String} value The value to match
12917      */
12918     setValue : function(v){
12919         if(this.multiple){
12920             this.syncValue();
12921             return;
12922         }
12923         
12924         var text = v;
12925         if(this.valueField){
12926             var r = this.findRecord(this.valueField, v);
12927             if(r){
12928                 text = r.data[this.displayField];
12929             }else if(this.valueNotFoundText !== undefined){
12930                 text = this.valueNotFoundText;
12931             }
12932         }
12933         this.lastSelectionText = text;
12934         if(this.hiddenField){
12935             this.hiddenField.dom.value = v;
12936         }
12937         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12938         this.value = v;
12939         
12940         var close = this.closeTriggerEl();
12941         
12942         if(close){
12943             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12944         }
12945     },
12946     /**
12947      * @property {Object} the last set data for the element
12948      */
12949     
12950     lastData : false,
12951     /**
12952      * Sets the value of the field based on a object which is related to the record format for the store.
12953      * @param {Object} value the value to set as. or false on reset?
12954      */
12955     setFromData : function(o){
12956         
12957         if(this.multiple){
12958             this.addItem(o);
12959             return;
12960         }
12961             
12962         var dv = ''; // display value
12963         var vv = ''; // value value..
12964         this.lastData = o;
12965         if (this.displayField) {
12966             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12967         } else {
12968             // this is an error condition!!!
12969             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12970         }
12971         
12972         if(this.valueField){
12973             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12974         }
12975         
12976         var close = this.closeTriggerEl();
12977         
12978         if(close){
12979             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12980         }
12981         
12982         if(this.hiddenField){
12983             this.hiddenField.dom.value = vv;
12984             
12985             this.lastSelectionText = dv;
12986             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12987             this.value = vv;
12988             return;
12989         }
12990         // no hidden field.. - we store the value in 'value', but still display
12991         // display field!!!!
12992         this.lastSelectionText = dv;
12993         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12994         this.value = vv;
12995         
12996         
12997         
12998     },
12999     // private
13000     reset : function(){
13001         // overridden so that last data is reset..
13002         
13003         if(this.multiple){
13004             this.clearItem();
13005             return;
13006         }
13007         
13008         this.setValue(this.originalValue);
13009         this.clearInvalid();
13010         this.lastData = false;
13011         if (this.view) {
13012             this.view.clearSelections();
13013         }
13014     },
13015     // private
13016     findRecord : function(prop, value){
13017         var record;
13018         if(this.store.getCount() > 0){
13019             this.store.each(function(r){
13020                 if(r.data[prop] == value){
13021                     record = r;
13022                     return false;
13023                 }
13024                 return true;
13025             });
13026         }
13027         return record;
13028     },
13029     
13030     getName: function()
13031     {
13032         // returns hidden if it's set..
13033         if (!this.rendered) {return ''};
13034         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13035         
13036     },
13037     // private
13038     onViewMove : function(e, t){
13039         this.inKeyMode = false;
13040     },
13041
13042     // private
13043     onViewOver : function(e, t){
13044         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13045             return;
13046         }
13047         var item = this.view.findItemFromChild(t);
13048         
13049         if(item){
13050             var index = this.view.indexOf(item);
13051             this.select(index, false);
13052         }
13053     },
13054
13055     // private
13056     onViewClick : function(view, doFocus, el, e)
13057     {
13058         var index = this.view.getSelectedIndexes()[0];
13059         
13060         var r = this.store.getAt(index);
13061         
13062         if(this.tickable){
13063             
13064             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13065                 return;
13066             }
13067             
13068             var rm = false;
13069             var _this = this;
13070             
13071             Roo.each(this.tickItems, function(v,k){
13072                 
13073                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13074                     Roo.log(v);
13075                     _this.tickItems.splice(k, 1);
13076                     
13077                     if(typeof(e) == 'undefined' && view == false){
13078                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13079                     }
13080                     
13081                     rm = true;
13082                     return;
13083                 }
13084             });
13085             
13086             if(rm){
13087                 return;
13088             }
13089             
13090             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13091                 this.tickItems.push(r.data);
13092             }
13093             
13094             if(typeof(e) == 'undefined' && view == false){
13095                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13096             }
13097                     
13098             return;
13099         }
13100         
13101         if(r){
13102             this.onSelect(r, index);
13103         }
13104         if(doFocus !== false && !this.blockFocus){
13105             this.inputEl().focus();
13106         }
13107     },
13108
13109     // private
13110     restrictHeight : function(){
13111         //this.innerList.dom.style.height = '';
13112         //var inner = this.innerList.dom;
13113         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13114         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13115         //this.list.beginUpdate();
13116         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13117         this.list.alignTo(this.inputEl(), this.listAlign);
13118         this.list.alignTo(this.inputEl(), this.listAlign);
13119         //this.list.endUpdate();
13120     },
13121
13122     // private
13123     onEmptyResults : function(){
13124         
13125         if(this.tickable && this.editable){
13126             this.restrictHeight();
13127             return;
13128         }
13129         
13130         this.collapse();
13131     },
13132
13133     /**
13134      * Returns true if the dropdown list is expanded, else false.
13135      */
13136     isExpanded : function(){
13137         return this.list.isVisible();
13138     },
13139
13140     /**
13141      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13142      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13143      * @param {String} value The data value of the item to select
13144      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13145      * selected item if it is not currently in view (defaults to true)
13146      * @return {Boolean} True if the value matched an item in the list, else false
13147      */
13148     selectByValue : function(v, scrollIntoView){
13149         if(v !== undefined && v !== null){
13150             var r = this.findRecord(this.valueField || this.displayField, v);
13151             if(r){
13152                 this.select(this.store.indexOf(r), scrollIntoView);
13153                 return true;
13154             }
13155         }
13156         return false;
13157     },
13158
13159     /**
13160      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13161      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13162      * @param {Number} index The zero-based index of the list item to select
13163      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13164      * selected item if it is not currently in view (defaults to true)
13165      */
13166     select : function(index, scrollIntoView){
13167         this.selectedIndex = index;
13168         this.view.select(index);
13169         if(scrollIntoView !== false){
13170             var el = this.view.getNode(index);
13171             /*
13172              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13173              */
13174             if(el){
13175                 this.list.scrollChildIntoView(el, false);
13176             }
13177         }
13178     },
13179
13180     // private
13181     selectNext : function(){
13182         var ct = this.store.getCount();
13183         if(ct > 0){
13184             if(this.selectedIndex == -1){
13185                 this.select(0);
13186             }else if(this.selectedIndex < ct-1){
13187                 this.select(this.selectedIndex+1);
13188             }
13189         }
13190     },
13191
13192     // private
13193     selectPrev : function(){
13194         var ct = this.store.getCount();
13195         if(ct > 0){
13196             if(this.selectedIndex == -1){
13197                 this.select(0);
13198             }else if(this.selectedIndex != 0){
13199                 this.select(this.selectedIndex-1);
13200             }
13201         }
13202     },
13203
13204     // private
13205     onKeyUp : function(e){
13206         if(this.editable !== false && !e.isSpecialKey()){
13207             this.lastKey = e.getKey();
13208             this.dqTask.delay(this.queryDelay);
13209         }
13210     },
13211
13212     // private
13213     validateBlur : function(){
13214         return !this.list || !this.list.isVisible();   
13215     },
13216
13217     // private
13218     initQuery : function(){
13219         
13220         var v = this.getRawValue();
13221         
13222         if(this.tickable && this.editable){
13223             v = this.tickableInputEl().getValue();
13224         }
13225         
13226         this.doQuery(v);
13227     },
13228
13229     // private
13230     doForce : function(){
13231         if(this.inputEl().dom.value.length > 0){
13232             this.inputEl().dom.value =
13233                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13234              
13235         }
13236     },
13237
13238     /**
13239      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13240      * query allowing the query action to be canceled if needed.
13241      * @param {String} query The SQL query to execute
13242      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13243      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13244      * saved in the current store (defaults to false)
13245      */
13246     doQuery : function(q, forceAll){
13247         
13248         if(q === undefined || q === null){
13249             q = '';
13250         }
13251         var qe = {
13252             query: q,
13253             forceAll: forceAll,
13254             combo: this,
13255             cancel:false
13256         };
13257         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13258             return false;
13259         }
13260         q = qe.query;
13261         
13262         forceAll = qe.forceAll;
13263         if(forceAll === true || (q.length >= this.minChars)){
13264             
13265             this.hasQuery = true;
13266             
13267             if(this.lastQuery != q || this.alwaysQuery){
13268                 this.lastQuery = q;
13269                 if(this.mode == 'local'){
13270                     this.selectedIndex = -1;
13271                     if(forceAll){
13272                         this.store.clearFilter();
13273                     }else{
13274                         
13275                         if(this.specialFilter){
13276                             this.fireEvent('specialfilter', this);
13277                             this.onLoad();
13278                             return;
13279                         }
13280                         
13281                         this.store.filter(this.displayField, q);
13282                     }
13283                     
13284                     this.store.fireEvent("datachanged", this.store);
13285                     
13286                     this.onLoad();
13287                     
13288                     
13289                 }else{
13290                     
13291                     this.store.baseParams[this.queryParam] = q;
13292                     
13293                     var options = {params : this.getParams(q)};
13294                     
13295                     if(this.loadNext){
13296                         options.add = true;
13297                         options.params.start = this.page * this.pageSize;
13298                     }
13299                     
13300                     this.store.load(options);
13301                     
13302                     /*
13303                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13304                      *  we should expand the list on onLoad
13305                      *  so command out it
13306                      */
13307 //                    this.expand();
13308                 }
13309             }else{
13310                 this.selectedIndex = -1;
13311                 this.onLoad();   
13312             }
13313         }
13314         
13315         this.loadNext = false;
13316     },
13317     
13318     // private
13319     getParams : function(q){
13320         var p = {};
13321         //p[this.queryParam] = q;
13322         
13323         if(this.pageSize){
13324             p.start = 0;
13325             p.limit = this.pageSize;
13326         }
13327         return p;
13328     },
13329
13330     /**
13331      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13332      */
13333     collapse : function(){
13334         if(!this.isExpanded()){
13335             return;
13336         }
13337         
13338         this.list.hide();
13339         
13340         if(this.tickable){
13341             this.hasFocus = false;
13342             this.okBtn.hide();
13343             this.cancelBtn.hide();
13344             this.trigger.show();
13345             
13346             if(this.editable){
13347                 this.tickableInputEl().dom.value = '';
13348                 this.tickableInputEl().blur();
13349             }
13350             
13351         }
13352         
13353         Roo.get(document).un('mousedown', this.collapseIf, this);
13354         Roo.get(document).un('mousewheel', this.collapseIf, this);
13355         if (!this.editable) {
13356             Roo.get(document).un('keydown', this.listKeyPress, this);
13357         }
13358         this.fireEvent('collapse', this);
13359     },
13360
13361     // private
13362     collapseIf : function(e){
13363         var in_combo  = e.within(this.el);
13364         var in_list =  e.within(this.list);
13365         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13366         
13367         if (in_combo || in_list || is_list) {
13368             //e.stopPropagation();
13369             return;
13370         }
13371         
13372         if(this.tickable){
13373             this.onTickableFooterButtonClick(e, false, false);
13374         }
13375
13376         this.collapse();
13377         
13378     },
13379
13380     /**
13381      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13382      */
13383     expand : function(){
13384        
13385         if(this.isExpanded() || !this.hasFocus){
13386             return;
13387         }
13388         
13389         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13390         this.list.setWidth(lw);
13391         
13392         
13393          Roo.log('expand');
13394         
13395         this.list.show();
13396         
13397         this.restrictHeight();
13398         
13399         if(this.tickable){
13400             
13401             this.tickItems = Roo.apply([], this.item);
13402             
13403             this.okBtn.show();
13404             this.cancelBtn.show();
13405             this.trigger.hide();
13406             
13407             if(this.editable){
13408                 this.tickableInputEl().focus();
13409             }
13410             
13411         }
13412         
13413         Roo.get(document).on('mousedown', this.collapseIf, this);
13414         Roo.get(document).on('mousewheel', this.collapseIf, this);
13415         if (!this.editable) {
13416             Roo.get(document).on('keydown', this.listKeyPress, this);
13417         }
13418         
13419         this.fireEvent('expand', this);
13420     },
13421
13422     // private
13423     // Implements the default empty TriggerField.onTriggerClick function
13424     onTriggerClick : function(e)
13425     {
13426         Roo.log('trigger click');
13427         
13428         if(this.disabled || !this.triggerList){
13429             return;
13430         }
13431         
13432         this.page = 0;
13433         this.loadNext = false;
13434         
13435         if(this.isExpanded()){
13436             this.collapse();
13437             if (!this.blockFocus) {
13438                 this.inputEl().focus();
13439             }
13440             
13441         }else {
13442             this.hasFocus = true;
13443             if(this.triggerAction == 'all') {
13444                 this.doQuery(this.allQuery, true);
13445             } else {
13446                 this.doQuery(this.getRawValue());
13447             }
13448             if (!this.blockFocus) {
13449                 this.inputEl().focus();
13450             }
13451         }
13452     },
13453     
13454     onTickableTriggerClick : function(e)
13455     {
13456         if(this.disabled){
13457             return;
13458         }
13459         
13460         this.page = 0;
13461         this.loadNext = false;
13462         this.hasFocus = true;
13463         
13464         if(this.triggerAction == 'all') {
13465             this.doQuery(this.allQuery, true);
13466         } else {
13467             this.doQuery(this.getRawValue());
13468         }
13469     },
13470     
13471     onSearchFieldClick : function(e)
13472     {
13473         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13474             this.onTickableFooterButtonClick(e, false, false);
13475             return;
13476         }
13477         
13478         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13479             return;
13480         }
13481         
13482         this.page = 0;
13483         this.loadNext = false;
13484         this.hasFocus = true;
13485         
13486         if(this.triggerAction == 'all') {
13487             this.doQuery(this.allQuery, true);
13488         } else {
13489             this.doQuery(this.getRawValue());
13490         }
13491     },
13492     
13493     listKeyPress : function(e)
13494     {
13495         //Roo.log('listkeypress');
13496         // scroll to first matching element based on key pres..
13497         if (e.isSpecialKey()) {
13498             return false;
13499         }
13500         var k = String.fromCharCode(e.getKey()).toUpperCase();
13501         //Roo.log(k);
13502         var match  = false;
13503         var csel = this.view.getSelectedNodes();
13504         var cselitem = false;
13505         if (csel.length) {
13506             var ix = this.view.indexOf(csel[0]);
13507             cselitem  = this.store.getAt(ix);
13508             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13509                 cselitem = false;
13510             }
13511             
13512         }
13513         
13514         this.store.each(function(v) { 
13515             if (cselitem) {
13516                 // start at existing selection.
13517                 if (cselitem.id == v.id) {
13518                     cselitem = false;
13519                 }
13520                 return true;
13521             }
13522                 
13523             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13524                 match = this.store.indexOf(v);
13525                 return false;
13526             }
13527             return true;
13528         }, this);
13529         
13530         if (match === false) {
13531             return true; // no more action?
13532         }
13533         // scroll to?
13534         this.view.select(match);
13535         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13536         sn.scrollIntoView(sn.dom.parentNode, false);
13537     },
13538     
13539     onViewScroll : function(e, t){
13540         
13541         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){
13542             return;
13543         }
13544         
13545         this.hasQuery = true;
13546         
13547         this.loading = this.list.select('.loading', true).first();
13548         
13549         if(this.loading === null){
13550             this.list.createChild({
13551                 tag: 'div',
13552                 cls: 'loading roo-select2-more-results roo-select2-active',
13553                 html: 'Loading more results...'
13554             });
13555             
13556             this.loading = this.list.select('.loading', true).first();
13557             
13558             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13559             
13560             this.loading.hide();
13561         }
13562         
13563         this.loading.show();
13564         
13565         var _combo = this;
13566         
13567         this.page++;
13568         this.loadNext = true;
13569         
13570         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13571         
13572         return;
13573     },
13574     
13575     addItem : function(o)
13576     {   
13577         var dv = ''; // display value
13578         
13579         if (this.displayField) {
13580             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13581         } else {
13582             // this is an error condition!!!
13583             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13584         }
13585         
13586         if(!dv.length){
13587             return;
13588         }
13589         
13590         var choice = this.choices.createChild({
13591             tag: 'li',
13592             cls: 'roo-select2-search-choice',
13593             cn: [
13594                 {
13595                     tag: 'div',
13596                     html: dv
13597                 },
13598                 {
13599                     tag: 'a',
13600                     href: '#',
13601                     cls: 'roo-select2-search-choice-close',
13602                     tabindex: '-1'
13603                 }
13604             ]
13605             
13606         }, this.searchField);
13607         
13608         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13609         
13610         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13611         
13612         this.item.push(o);
13613         
13614         this.lastData = o;
13615         
13616         this.syncValue();
13617         
13618         this.inputEl().dom.value = '';
13619         
13620         this.validate();
13621     },
13622     
13623     onRemoveItem : function(e, _self, o)
13624     {
13625         e.preventDefault();
13626         
13627         this.lastItem = Roo.apply([], this.item);
13628         
13629         var index = this.item.indexOf(o.data) * 1;
13630         
13631         if( index < 0){
13632             Roo.log('not this item?!');
13633             return;
13634         }
13635         
13636         this.item.splice(index, 1);
13637         o.item.remove();
13638         
13639         this.syncValue();
13640         
13641         this.fireEvent('remove', this, e);
13642         
13643         this.validate();
13644         
13645     },
13646     
13647     syncValue : function()
13648     {
13649         if(!this.item.length){
13650             this.clearValue();
13651             return;
13652         }
13653             
13654         var value = [];
13655         var _this = this;
13656         Roo.each(this.item, function(i){
13657             if(_this.valueField){
13658                 value.push(i[_this.valueField]);
13659                 return;
13660             }
13661
13662             value.push(i);
13663         });
13664
13665         this.value = value.join(',');
13666
13667         if(this.hiddenField){
13668             this.hiddenField.dom.value = this.value;
13669         }
13670         
13671         this.store.fireEvent("datachanged", this.store);
13672     },
13673     
13674     clearItem : function()
13675     {
13676         if(!this.multiple){
13677             return;
13678         }
13679         
13680         this.item = [];
13681         
13682         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13683            c.remove();
13684         });
13685         
13686         this.syncValue();
13687         
13688         this.validate();
13689         
13690         if(this.tickable && !Roo.isTouch){
13691             this.view.refresh();
13692         }
13693     },
13694     
13695     inputEl: function ()
13696     {
13697         if(Roo.isTouch && this.mobileTouchView){
13698             return this.el.select('input.form-control',true).first();
13699         }
13700         
13701         if(this.tickable){
13702             return this.searchField;
13703         }
13704         
13705         return this.el.select('input.form-control',true).first();
13706     },
13707     
13708     
13709     onTickableFooterButtonClick : function(e, btn, el)
13710     {
13711         e.preventDefault();
13712         
13713         this.lastItem = Roo.apply([], this.item);
13714         
13715         if(btn && btn.name == 'cancel'){
13716             this.tickItems = Roo.apply([], this.item);
13717             this.collapse();
13718             return;
13719         }
13720         
13721         this.clearItem();
13722         
13723         var _this = this;
13724         
13725         Roo.each(this.tickItems, function(o){
13726             _this.addItem(o);
13727         });
13728         
13729         this.collapse();
13730         
13731     },
13732     
13733     validate : function()
13734     {
13735         var v = this.getRawValue();
13736         
13737         if(this.multiple){
13738             v = this.getValue();
13739         }
13740         
13741         if(this.disabled || this.allowBlank || v.length){
13742             this.markValid();
13743             return true;
13744         }
13745         
13746         this.markInvalid();
13747         return false;
13748     },
13749     
13750     tickableInputEl : function()
13751     {
13752         if(!this.tickable || !this.editable){
13753             return this.inputEl();
13754         }
13755         
13756         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13757     },
13758     
13759     
13760     getAutoCreateTouchView : function()
13761     {
13762         var id = Roo.id();
13763         
13764         var cfg = {
13765             cls: 'form-group' //input-group
13766         };
13767         
13768         var input =  {
13769             tag: 'input',
13770             id : id,
13771             type : this.inputType,
13772             cls : 'form-control x-combo-noedit',
13773             autocomplete: 'new-password',
13774             placeholder : this.placeholder || '',
13775             readonly : true
13776         };
13777         
13778         if (this.name) {
13779             input.name = this.name;
13780         }
13781         
13782         if (this.size) {
13783             input.cls += ' input-' + this.size;
13784         }
13785         
13786         if (this.disabled) {
13787             input.disabled = true;
13788         }
13789         
13790         var inputblock = {
13791             cls : '',
13792             cn : [
13793                 input
13794             ]
13795         };
13796         
13797         if(this.before){
13798             inputblock.cls += ' input-group';
13799             
13800             inputblock.cn.unshift({
13801                 tag :'span',
13802                 cls : 'input-group-addon',
13803                 html : this.before
13804             });
13805         }
13806         
13807         if(this.removable && !this.multiple){
13808             inputblock.cls += ' roo-removable';
13809             
13810             inputblock.cn.push({
13811                 tag: 'button',
13812                 html : 'x',
13813                 cls : 'roo-combo-removable-btn close'
13814             });
13815         }
13816
13817         if(this.hasFeedback && !this.allowBlank){
13818             
13819             inputblock.cls += ' has-feedback';
13820             
13821             inputblock.cn.push({
13822                 tag: 'span',
13823                 cls: 'glyphicon form-control-feedback'
13824             });
13825             
13826         }
13827         
13828         if (this.after) {
13829             
13830             inputblock.cls += (this.before) ? '' : ' input-group';
13831             
13832             inputblock.cn.push({
13833                 tag :'span',
13834                 cls : 'input-group-addon',
13835                 html : this.after
13836             });
13837         }
13838
13839         var box = {
13840             tag: 'div',
13841             cn: [
13842                 {
13843                     tag: 'input',
13844                     type : 'hidden',
13845                     cls: 'form-hidden-field'
13846                 },
13847                 inputblock
13848             ]
13849             
13850         };
13851         
13852         if(this.multiple){
13853             box = {
13854                 tag: 'div',
13855                 cn: [
13856                     {
13857                         tag: 'input',
13858                         type : 'hidden',
13859                         cls: 'form-hidden-field'
13860                     },
13861                     {
13862                         tag: 'ul',
13863                         cls: 'roo-select2-choices',
13864                         cn:[
13865                             {
13866                                 tag: 'li',
13867                                 cls: 'roo-select2-search-field',
13868                                 cn: [
13869
13870                                     inputblock
13871                                 ]
13872                             }
13873                         ]
13874                     }
13875                 ]
13876             }
13877         };
13878         
13879         var combobox = {
13880             cls: 'roo-select2-container input-group',
13881             cn: [
13882                 box
13883             ]
13884         };
13885         
13886 //        if(!this.multiple && this.showToggleBtn){
13887 //            
13888 //            var caret = {
13889 //                        tag: 'span',
13890 //                        cls: 'caret'
13891 //            };
13892 //            
13893 //            if (this.caret != false) {
13894 //                caret = {
13895 //                     tag: 'i',
13896 //                     cls: 'fa fa-' + this.caret
13897 //                };
13898 //                
13899 //            }
13900 //            
13901 //            combobox.cn.push({
13902 //                tag :'span',
13903 //                cls : 'input-group-addon btn dropdown-toggle',
13904 //                cn : [
13905 //                    caret,
13906 //                    {
13907 //                        tag: 'span',
13908 //                        cls: 'combobox-clear',
13909 //                        cn  : [
13910 //                            {
13911 //                                tag : 'i',
13912 //                                cls: 'icon-remove'
13913 //                            }
13914 //                        ]
13915 //                    }
13916 //                ]
13917 //
13918 //            })
13919 //        }
13920         
13921         if(this.multiple){
13922             combobox.cls += ' roo-select2-container-multi';
13923         }
13924         
13925         var align = this.labelAlign || this.parentLabelAlign();
13926         
13927         cfg.cn = combobox;
13928         
13929         if(this.fieldLabel.length && this.labelWidth){
13930             
13931             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13932             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13933             
13934             cfg.cn = [
13935                 {
13936                     tag: 'label',
13937                     cls : 'control-label ' + lw,
13938                     html : this.fieldLabel
13939
13940                 },
13941                 {
13942                     cls : cw, 
13943                     cn: [
13944                         combobox
13945                     ]
13946                 }
13947             ];
13948         }
13949         
13950         var settings = this;
13951         
13952         ['xs','sm','md','lg'].map(function(size){
13953             if (settings[size]) {
13954                 cfg.cls += ' col-' + size + '-' + settings[size];
13955             }
13956         });
13957         
13958         return cfg;
13959     },
13960     
13961     initTouchView : function()
13962     {
13963         this.renderTouchView();
13964         
13965         this.touchViewEl.on('scroll', function(){
13966             this.el.dom.scrollTop = 0;
13967         }, this);
13968         
13969         this.originalValue = this.getValue();
13970         
13971         this.inputEl().on("touch", this.showTouchView, this);
13972         
13973         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13974         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13975         
13976         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13977         
13978         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13979         this.store.on('load', this.onTouchViewLoad, this);
13980         this.store.on('loadexception', this.onTouchViewLoadException, this);
13981         
13982         if(this.hiddenName){
13983             
13984             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13985             
13986             this.hiddenField.dom.value =
13987                 this.hiddenValue !== undefined ? this.hiddenValue :
13988                 this.value !== undefined ? this.value : '';
13989         
13990             this.el.dom.removeAttribute('name');
13991             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13992         }
13993         
13994         if(this.multiple){
13995             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13996             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13997         }
13998         
13999         if(this.removable && !this.multiple){
14000             var close = this.closeTriggerEl();
14001             if(close){
14002                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14003                 close.on('click', this.removeBtnClick, this, close);
14004             }
14005         }
14006         /*
14007          * fix the bug in Safari iOS8
14008          */
14009         this.inputEl().on("focus", function(e){
14010             document.activeElement.blur();
14011         }, this);
14012         
14013         return;
14014         
14015         
14016     },
14017     
14018     renderTouchView : function()
14019     {
14020         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14021         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14022         
14023         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14024         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14025         
14026         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14027         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14028         this.touchViewBodyEl.setStyle('overflow', 'auto');
14029         
14030         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14031         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14032         
14033         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14034         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14035         
14036     },
14037     
14038     showTouchView : function()
14039     {
14040         if(this.disabled){
14041             return;
14042         }
14043         
14044         this.touchViewHeaderEl.hide();
14045
14046         if(this.fieldLabel.length){
14047             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
14048             this.touchViewHeaderEl.show();
14049         }
14050
14051         this.touchViewEl.show();
14052
14053         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14054         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14055                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14056
14057         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14058
14059         if(this.fieldLabel.length){
14060             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14061         }
14062         
14063         this.touchViewBodyEl.setHeight(bodyHeight);
14064
14065         if(this.animate){
14066             var _this = this;
14067             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14068         }else{
14069             this.touchViewEl.addClass('in');
14070         }
14071
14072         this.doTouchViewQuery();
14073         
14074     },
14075     
14076     hideTouchView : function()
14077     {
14078         this.touchViewEl.removeClass('in');
14079
14080         if(this.animate){
14081             var _this = this;
14082             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14083         }else{
14084             this.touchViewEl.setStyle('display', 'none');
14085         }
14086         
14087     },
14088     
14089     setTouchViewValue : function()
14090     {
14091         if(this.multiple){
14092             this.clearItem();
14093         
14094             var _this = this;
14095
14096             Roo.each(this.tickItems, function(o){
14097                 this.addItem(o);
14098             }, this);
14099         }
14100         
14101         this.hideTouchView();
14102     },
14103     
14104     doTouchViewQuery : function()
14105     {
14106         var qe = {
14107             query: '',
14108             forceAll: true,
14109             combo: this,
14110             cancel:false
14111         };
14112         
14113         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14114             return false;
14115         }
14116         
14117         if(!this.alwaysQuery || this.mode == 'local'){
14118             this.onTouchViewLoad();
14119             return;
14120         }
14121         
14122         this.store.load();
14123     },
14124     
14125     onTouchViewBeforeLoad : function(combo,opts)
14126     {
14127         return;
14128     },
14129
14130     // private
14131     onTouchViewLoad : function()
14132     {
14133         if(this.store.getCount() < 1){
14134             this.onTouchViewEmptyResults();
14135             return;
14136         }
14137         
14138         this.clearTouchView();
14139         
14140         var rawValue = this.getRawValue();
14141         
14142         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14143         
14144         this.tickItems = [];
14145         
14146         this.store.data.each(function(d, rowIndex){
14147             var row = this.touchViewListGroup.createChild(template);
14148             
14149             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14150                 row.addClass(d.data.cls);
14151             }
14152             
14153             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14154                 var cfg = {
14155                     data : d.data,
14156                     html : d.data[this.displayField]
14157                 };
14158                 
14159                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14160                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14161                 }
14162             }
14163             row.removeClass('selected');
14164             if(!this.multiple && this.valueField &&
14165                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14166             {
14167                 // radio buttons..
14168                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14169                 row.addClass('selected');
14170             }
14171             
14172             if(this.multiple && this.valueField &&
14173                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14174             {
14175                 
14176                 // checkboxes...
14177                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14178                 this.tickItems.push(d.data);
14179             }
14180             
14181             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14182             
14183         }, this);
14184         
14185         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14186         
14187         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14188
14189         if(this.fieldLabel.length){
14190             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14191         }
14192
14193         var listHeight = this.touchViewListGroup.getHeight();
14194         
14195         var _this = this;
14196         
14197         if(firstChecked && listHeight > bodyHeight){
14198             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14199         }
14200         
14201     },
14202     
14203     onTouchViewLoadException : function()
14204     {
14205         this.hideTouchView();
14206     },
14207     
14208     onTouchViewEmptyResults : function()
14209     {
14210         this.clearTouchView();
14211         
14212         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14213         
14214         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14215         
14216     },
14217     
14218     clearTouchView : function()
14219     {
14220         this.touchViewListGroup.dom.innerHTML = '';
14221     },
14222     
14223     onTouchViewClick : function(e, el, o)
14224     {
14225         e.preventDefault();
14226         
14227         var row = o.row;
14228         var rowIndex = o.rowIndex;
14229         
14230         var r = this.store.getAt(rowIndex);
14231         
14232         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14233             
14234             if(!this.multiple){
14235                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14236                     c.dom.removeAttribute('checked');
14237                 }, this);
14238
14239                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14240
14241                 this.setFromData(r.data);
14242
14243                 var close = this.closeTriggerEl();
14244
14245                 if(close){
14246                     close.show();
14247                 }
14248
14249                 this.hideTouchView();
14250
14251                 this.fireEvent('select', this, r, rowIndex);
14252
14253                 return;
14254             }
14255
14256             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14257                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14258                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14259                 return;
14260             }
14261
14262             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14263             this.addItem(r.data);
14264             this.tickItems.push(r.data);
14265         }
14266     }
14267     
14268
14269     /** 
14270     * @cfg {Boolean} grow 
14271     * @hide 
14272     */
14273     /** 
14274     * @cfg {Number} growMin 
14275     * @hide 
14276     */
14277     /** 
14278     * @cfg {Number} growMax 
14279     * @hide 
14280     */
14281     /**
14282      * @hide
14283      * @method autoSize
14284      */
14285 });
14286
14287 Roo.apply(Roo.bootstrap.ComboBox,  {
14288     
14289     header : {
14290         tag: 'div',
14291         cls: 'modal-header',
14292         cn: [
14293             {
14294                 tag: 'h4',
14295                 cls: 'modal-title'
14296             }
14297         ]
14298     },
14299     
14300     body : {
14301         tag: 'div',
14302         cls: 'modal-body',
14303         cn: [
14304             {
14305                 tag: 'ul',
14306                 cls: 'list-group'
14307             }
14308         ]
14309     },
14310     
14311     listItemRadio : {
14312         tag: 'li',
14313         cls: 'list-group-item',
14314         cn: [
14315             {
14316                 tag: 'span',
14317                 cls: 'roo-combobox-list-group-item-value'
14318             },
14319             {
14320                 tag: 'div',
14321                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14322                 cn: [
14323                     {
14324                         tag: 'input',
14325                         type: 'radio'
14326                     },
14327                     {
14328                         tag: 'label'
14329                     }
14330                 ]
14331             }
14332         ]
14333     },
14334     
14335     listItemCheckbox : {
14336         tag: 'li',
14337         cls: 'list-group-item',
14338         cn: [
14339             {
14340                 tag: 'span',
14341                 cls: 'roo-combobox-list-group-item-value'
14342             },
14343             {
14344                 tag: 'div',
14345                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14346                 cn: [
14347                     {
14348                         tag: 'input',
14349                         type: 'checkbox'
14350                     },
14351                     {
14352                         tag: 'label'
14353                     }
14354                 ]
14355             }
14356         ]
14357     },
14358     
14359     emptyResult : {
14360         tag: 'div',
14361         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14362     },
14363     
14364     footer : {
14365         tag: 'div',
14366         cls: 'modal-footer',
14367         cn: [
14368             {
14369                 tag: 'div',
14370                 cls: 'row',
14371                 cn: [
14372                     {
14373                         tag: 'div',
14374                         cls: 'col-xs-6 text-left',
14375                         cn: {
14376                             tag: 'button',
14377                             cls: 'btn btn-danger roo-touch-view-cancel',
14378                             html: 'Cancel'
14379                         }
14380                     },
14381                     {
14382                         tag: 'div',
14383                         cls: 'col-xs-6 text-right',
14384                         cn: {
14385                             tag: 'button',
14386                             cls: 'btn btn-success roo-touch-view-ok',
14387                             html: 'OK'
14388                         }
14389                     }
14390                 ]
14391             }
14392         ]
14393         
14394     }
14395 });
14396
14397 Roo.apply(Roo.bootstrap.ComboBox,  {
14398     
14399     touchViewTemplate : {
14400         tag: 'div',
14401         cls: 'modal fade roo-combobox-touch-view',
14402         cn: [
14403             {
14404                 tag: 'div',
14405                 cls: 'modal-dialog',
14406                 style : 'position:fixed', // we have to fix position....
14407                 cn: [
14408                     {
14409                         tag: 'div',
14410                         cls: 'modal-content',
14411                         cn: [
14412                             Roo.bootstrap.ComboBox.header,
14413                             Roo.bootstrap.ComboBox.body,
14414                             Roo.bootstrap.ComboBox.footer
14415                         ]
14416                     }
14417                 ]
14418             }
14419         ]
14420     }
14421 });/*
14422  * Based on:
14423  * Ext JS Library 1.1.1
14424  * Copyright(c) 2006-2007, Ext JS, LLC.
14425  *
14426  * Originally Released Under LGPL - original licence link has changed is not relivant.
14427  *
14428  * Fork - LGPL
14429  * <script type="text/javascript">
14430  */
14431
14432 /**
14433  * @class Roo.View
14434  * @extends Roo.util.Observable
14435  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14436  * This class also supports single and multi selection modes. <br>
14437  * Create a data model bound view:
14438  <pre><code>
14439  var store = new Roo.data.Store(...);
14440
14441  var view = new Roo.View({
14442     el : "my-element",
14443     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14444  
14445     singleSelect: true,
14446     selectedClass: "ydataview-selected",
14447     store: store
14448  });
14449
14450  // listen for node click?
14451  view.on("click", function(vw, index, node, e){
14452  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14453  });
14454
14455  // load XML data
14456  dataModel.load("foobar.xml");
14457  </code></pre>
14458  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14459  * <br><br>
14460  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14461  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14462  * 
14463  * Note: old style constructor is still suported (container, template, config)
14464  * 
14465  * @constructor
14466  * Create a new View
14467  * @param {Object} config The config object
14468  * 
14469  */
14470 Roo.View = function(config, depreciated_tpl, depreciated_config){
14471     
14472     this.parent = false;
14473     
14474     if (typeof(depreciated_tpl) == 'undefined') {
14475         // new way.. - universal constructor.
14476         Roo.apply(this, config);
14477         this.el  = Roo.get(this.el);
14478     } else {
14479         // old format..
14480         this.el  = Roo.get(config);
14481         this.tpl = depreciated_tpl;
14482         Roo.apply(this, depreciated_config);
14483     }
14484     this.wrapEl  = this.el.wrap().wrap();
14485     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14486     
14487     
14488     if(typeof(this.tpl) == "string"){
14489         this.tpl = new Roo.Template(this.tpl);
14490     } else {
14491         // support xtype ctors..
14492         this.tpl = new Roo.factory(this.tpl, Roo);
14493     }
14494     
14495     
14496     this.tpl.compile();
14497     
14498     /** @private */
14499     this.addEvents({
14500         /**
14501          * @event beforeclick
14502          * Fires before a click is processed. Returns false to cancel the default action.
14503          * @param {Roo.View} this
14504          * @param {Number} index The index of the target node
14505          * @param {HTMLElement} node The target node
14506          * @param {Roo.EventObject} e The raw event object
14507          */
14508             "beforeclick" : true,
14509         /**
14510          * @event click
14511          * Fires when a template node is clicked.
14512          * @param {Roo.View} this
14513          * @param {Number} index The index of the target node
14514          * @param {HTMLElement} node The target node
14515          * @param {Roo.EventObject} e The raw event object
14516          */
14517             "click" : true,
14518         /**
14519          * @event dblclick
14520          * Fires when a template node is double clicked.
14521          * @param {Roo.View} this
14522          * @param {Number} index The index of the target node
14523          * @param {HTMLElement} node The target node
14524          * @param {Roo.EventObject} e The raw event object
14525          */
14526             "dblclick" : true,
14527         /**
14528          * @event contextmenu
14529          * Fires when a template node is right clicked.
14530          * @param {Roo.View} this
14531          * @param {Number} index The index of the target node
14532          * @param {HTMLElement} node The target node
14533          * @param {Roo.EventObject} e The raw event object
14534          */
14535             "contextmenu" : true,
14536         /**
14537          * @event selectionchange
14538          * Fires when the selected nodes change.
14539          * @param {Roo.View} this
14540          * @param {Array} selections Array of the selected nodes
14541          */
14542             "selectionchange" : true,
14543     
14544         /**
14545          * @event beforeselect
14546          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14547          * @param {Roo.View} this
14548          * @param {HTMLElement} node The node to be selected
14549          * @param {Array} selections Array of currently selected nodes
14550          */
14551             "beforeselect" : true,
14552         /**
14553          * @event preparedata
14554          * Fires on every row to render, to allow you to change the data.
14555          * @param {Roo.View} this
14556          * @param {Object} data to be rendered (change this)
14557          */
14558           "preparedata" : true
14559           
14560           
14561         });
14562
14563
14564
14565     this.el.on({
14566         "click": this.onClick,
14567         "dblclick": this.onDblClick,
14568         "contextmenu": this.onContextMenu,
14569         scope:this
14570     });
14571
14572     this.selections = [];
14573     this.nodes = [];
14574     this.cmp = new Roo.CompositeElementLite([]);
14575     if(this.store){
14576         this.store = Roo.factory(this.store, Roo.data);
14577         this.setStore(this.store, true);
14578     }
14579     
14580     if ( this.footer && this.footer.xtype) {
14581            
14582          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14583         
14584         this.footer.dataSource = this.store;
14585         this.footer.container = fctr;
14586         this.footer = Roo.factory(this.footer, Roo);
14587         fctr.insertFirst(this.el);
14588         
14589         // this is a bit insane - as the paging toolbar seems to detach the el..
14590 //        dom.parentNode.parentNode.parentNode
14591          // they get detached?
14592     }
14593     
14594     
14595     Roo.View.superclass.constructor.call(this);
14596     
14597     
14598 };
14599
14600 Roo.extend(Roo.View, Roo.util.Observable, {
14601     
14602      /**
14603      * @cfg {Roo.data.Store} store Data store to load data from.
14604      */
14605     store : false,
14606     
14607     /**
14608      * @cfg {String|Roo.Element} el The container element.
14609      */
14610     el : '',
14611     
14612     /**
14613      * @cfg {String|Roo.Template} tpl The template used by this View 
14614      */
14615     tpl : false,
14616     /**
14617      * @cfg {String} dataName the named area of the template to use as the data area
14618      *                          Works with domtemplates roo-name="name"
14619      */
14620     dataName: false,
14621     /**
14622      * @cfg {String} selectedClass The css class to add to selected nodes
14623      */
14624     selectedClass : "x-view-selected",
14625      /**
14626      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14627      */
14628     emptyText : "",
14629     
14630     /**
14631      * @cfg {String} text to display on mask (default Loading)
14632      */
14633     mask : false,
14634     /**
14635      * @cfg {Boolean} multiSelect Allow multiple selection
14636      */
14637     multiSelect : false,
14638     /**
14639      * @cfg {Boolean} singleSelect Allow single selection
14640      */
14641     singleSelect:  false,
14642     
14643     /**
14644      * @cfg {Boolean} toggleSelect - selecting 
14645      */
14646     toggleSelect : false,
14647     
14648     /**
14649      * @cfg {Boolean} tickable - selecting 
14650      */
14651     tickable : false,
14652     
14653     /**
14654      * Returns the element this view is bound to.
14655      * @return {Roo.Element}
14656      */
14657     getEl : function(){
14658         return this.wrapEl;
14659     },
14660     
14661     
14662
14663     /**
14664      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14665      */
14666     refresh : function(){
14667         //Roo.log('refresh');
14668         var t = this.tpl;
14669         
14670         // if we are using something like 'domtemplate', then
14671         // the what gets used is:
14672         // t.applySubtemplate(NAME, data, wrapping data..)
14673         // the outer template then get' applied with
14674         //     the store 'extra data'
14675         // and the body get's added to the
14676         //      roo-name="data" node?
14677         //      <span class='roo-tpl-{name}'></span> ?????
14678         
14679         
14680         
14681         this.clearSelections();
14682         this.el.update("");
14683         var html = [];
14684         var records = this.store.getRange();
14685         if(records.length < 1) {
14686             
14687             // is this valid??  = should it render a template??
14688             
14689             this.el.update(this.emptyText);
14690             return;
14691         }
14692         var el = this.el;
14693         if (this.dataName) {
14694             this.el.update(t.apply(this.store.meta)); //????
14695             el = this.el.child('.roo-tpl-' + this.dataName);
14696         }
14697         
14698         for(var i = 0, len = records.length; i < len; i++){
14699             var data = this.prepareData(records[i].data, i, records[i]);
14700             this.fireEvent("preparedata", this, data, i, records[i]);
14701             
14702             var d = Roo.apply({}, data);
14703             
14704             if(this.tickable){
14705                 Roo.apply(d, {'roo-id' : Roo.id()});
14706                 
14707                 var _this = this;
14708             
14709                 Roo.each(this.parent.item, function(item){
14710                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14711                         return;
14712                     }
14713                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14714                 });
14715             }
14716             
14717             html[html.length] = Roo.util.Format.trim(
14718                 this.dataName ?
14719                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14720                     t.apply(d)
14721             );
14722         }
14723         
14724         
14725         
14726         el.update(html.join(""));
14727         this.nodes = el.dom.childNodes;
14728         this.updateIndexes(0);
14729     },
14730     
14731
14732     /**
14733      * Function to override to reformat the data that is sent to
14734      * the template for each node.
14735      * DEPRICATED - use the preparedata event handler.
14736      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14737      * a JSON object for an UpdateManager bound view).
14738      */
14739     prepareData : function(data, index, record)
14740     {
14741         this.fireEvent("preparedata", this, data, index, record);
14742         return data;
14743     },
14744
14745     onUpdate : function(ds, record){
14746         // Roo.log('on update');   
14747         this.clearSelections();
14748         var index = this.store.indexOf(record);
14749         var n = this.nodes[index];
14750         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14751         n.parentNode.removeChild(n);
14752         this.updateIndexes(index, index);
14753     },
14754
14755     
14756     
14757 // --------- FIXME     
14758     onAdd : function(ds, records, index)
14759     {
14760         //Roo.log(['on Add', ds, records, index] );        
14761         this.clearSelections();
14762         if(this.nodes.length == 0){
14763             this.refresh();
14764             return;
14765         }
14766         var n = this.nodes[index];
14767         for(var i = 0, len = records.length; i < len; i++){
14768             var d = this.prepareData(records[i].data, i, records[i]);
14769             if(n){
14770                 this.tpl.insertBefore(n, d);
14771             }else{
14772                 
14773                 this.tpl.append(this.el, d);
14774             }
14775         }
14776         this.updateIndexes(index);
14777     },
14778
14779     onRemove : function(ds, record, index){
14780        // Roo.log('onRemove');
14781         this.clearSelections();
14782         var el = this.dataName  ?
14783             this.el.child('.roo-tpl-' + this.dataName) :
14784             this.el; 
14785         
14786         el.dom.removeChild(this.nodes[index]);
14787         this.updateIndexes(index);
14788     },
14789
14790     /**
14791      * Refresh an individual node.
14792      * @param {Number} index
14793      */
14794     refreshNode : function(index){
14795         this.onUpdate(this.store, this.store.getAt(index));
14796     },
14797
14798     updateIndexes : function(startIndex, endIndex){
14799         var ns = this.nodes;
14800         startIndex = startIndex || 0;
14801         endIndex = endIndex || ns.length - 1;
14802         for(var i = startIndex; i <= endIndex; i++){
14803             ns[i].nodeIndex = i;
14804         }
14805     },
14806
14807     /**
14808      * Changes the data store this view uses and refresh the view.
14809      * @param {Store} store
14810      */
14811     setStore : function(store, initial){
14812         if(!initial && this.store){
14813             this.store.un("datachanged", this.refresh);
14814             this.store.un("add", this.onAdd);
14815             this.store.un("remove", this.onRemove);
14816             this.store.un("update", this.onUpdate);
14817             this.store.un("clear", this.refresh);
14818             this.store.un("beforeload", this.onBeforeLoad);
14819             this.store.un("load", this.onLoad);
14820             this.store.un("loadexception", this.onLoad);
14821         }
14822         if(store){
14823           
14824             store.on("datachanged", this.refresh, this);
14825             store.on("add", this.onAdd, this);
14826             store.on("remove", this.onRemove, this);
14827             store.on("update", this.onUpdate, this);
14828             store.on("clear", this.refresh, this);
14829             store.on("beforeload", this.onBeforeLoad, this);
14830             store.on("load", this.onLoad, this);
14831             store.on("loadexception", this.onLoad, this);
14832         }
14833         
14834         if(store){
14835             this.refresh();
14836         }
14837     },
14838     /**
14839      * onbeforeLoad - masks the loading area.
14840      *
14841      */
14842     onBeforeLoad : function(store,opts)
14843     {
14844          //Roo.log('onBeforeLoad');   
14845         if (!opts.add) {
14846             this.el.update("");
14847         }
14848         this.el.mask(this.mask ? this.mask : "Loading" ); 
14849     },
14850     onLoad : function ()
14851     {
14852         this.el.unmask();
14853     },
14854     
14855
14856     /**
14857      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14858      * @param {HTMLElement} node
14859      * @return {HTMLElement} The template node
14860      */
14861     findItemFromChild : function(node){
14862         var el = this.dataName  ?
14863             this.el.child('.roo-tpl-' + this.dataName,true) :
14864             this.el.dom; 
14865         
14866         if(!node || node.parentNode == el){
14867                     return node;
14868             }
14869             var p = node.parentNode;
14870             while(p && p != el){
14871             if(p.parentNode == el){
14872                 return p;
14873             }
14874             p = p.parentNode;
14875         }
14876             return null;
14877     },
14878
14879     /** @ignore */
14880     onClick : function(e){
14881         var item = this.findItemFromChild(e.getTarget());
14882         if(item){
14883             var index = this.indexOf(item);
14884             if(this.onItemClick(item, index, e) !== false){
14885                 this.fireEvent("click", this, index, item, e);
14886             }
14887         }else{
14888             this.clearSelections();
14889         }
14890     },
14891
14892     /** @ignore */
14893     onContextMenu : function(e){
14894         var item = this.findItemFromChild(e.getTarget());
14895         if(item){
14896             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14897         }
14898     },
14899
14900     /** @ignore */
14901     onDblClick : function(e){
14902         var item = this.findItemFromChild(e.getTarget());
14903         if(item){
14904             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14905         }
14906     },
14907
14908     onItemClick : function(item, index, e)
14909     {
14910         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14911             return false;
14912         }
14913         if (this.toggleSelect) {
14914             var m = this.isSelected(item) ? 'unselect' : 'select';
14915             //Roo.log(m);
14916             var _t = this;
14917             _t[m](item, true, false);
14918             return true;
14919         }
14920         if(this.multiSelect || this.singleSelect){
14921             if(this.multiSelect && e.shiftKey && this.lastSelection){
14922                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14923             }else{
14924                 this.select(item, this.multiSelect && e.ctrlKey);
14925                 this.lastSelection = item;
14926             }
14927             
14928             if(!this.tickable){
14929                 e.preventDefault();
14930             }
14931             
14932         }
14933         return true;
14934     },
14935
14936     /**
14937      * Get the number of selected nodes.
14938      * @return {Number}
14939      */
14940     getSelectionCount : function(){
14941         return this.selections.length;
14942     },
14943
14944     /**
14945      * Get the currently selected nodes.
14946      * @return {Array} An array of HTMLElements
14947      */
14948     getSelectedNodes : function(){
14949         return this.selections;
14950     },
14951
14952     /**
14953      * Get the indexes of the selected nodes.
14954      * @return {Array}
14955      */
14956     getSelectedIndexes : function(){
14957         var indexes = [], s = this.selections;
14958         for(var i = 0, len = s.length; i < len; i++){
14959             indexes.push(s[i].nodeIndex);
14960         }
14961         return indexes;
14962     },
14963
14964     /**
14965      * Clear all selections
14966      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14967      */
14968     clearSelections : function(suppressEvent){
14969         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14970             this.cmp.elements = this.selections;
14971             this.cmp.removeClass(this.selectedClass);
14972             this.selections = [];
14973             if(!suppressEvent){
14974                 this.fireEvent("selectionchange", this, this.selections);
14975             }
14976         }
14977     },
14978
14979     /**
14980      * Returns true if the passed node is selected
14981      * @param {HTMLElement/Number} node The node or node index
14982      * @return {Boolean}
14983      */
14984     isSelected : function(node){
14985         var s = this.selections;
14986         if(s.length < 1){
14987             return false;
14988         }
14989         node = this.getNode(node);
14990         return s.indexOf(node) !== -1;
14991     },
14992
14993     /**
14994      * Selects nodes.
14995      * @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
14996      * @param {Boolean} keepExisting (optional) true to keep existing selections
14997      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14998      */
14999     select : function(nodeInfo, keepExisting, suppressEvent){
15000         if(nodeInfo instanceof Array){
15001             if(!keepExisting){
15002                 this.clearSelections(true);
15003             }
15004             for(var i = 0, len = nodeInfo.length; i < len; i++){
15005                 this.select(nodeInfo[i], true, true);
15006             }
15007             return;
15008         } 
15009         var node = this.getNode(nodeInfo);
15010         if(!node || this.isSelected(node)){
15011             return; // already selected.
15012         }
15013         if(!keepExisting){
15014             this.clearSelections(true);
15015         }
15016         
15017         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15018             Roo.fly(node).addClass(this.selectedClass);
15019             this.selections.push(node);
15020             if(!suppressEvent){
15021                 this.fireEvent("selectionchange", this, this.selections);
15022             }
15023         }
15024         
15025         
15026     },
15027       /**
15028      * Unselects nodes.
15029      * @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
15030      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15031      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15032      */
15033     unselect : function(nodeInfo, keepExisting, suppressEvent)
15034     {
15035         if(nodeInfo instanceof Array){
15036             Roo.each(this.selections, function(s) {
15037                 this.unselect(s, nodeInfo);
15038             }, this);
15039             return;
15040         }
15041         var node = this.getNode(nodeInfo);
15042         if(!node || !this.isSelected(node)){
15043             //Roo.log("not selected");
15044             return; // not selected.
15045         }
15046         // fireevent???
15047         var ns = [];
15048         Roo.each(this.selections, function(s) {
15049             if (s == node ) {
15050                 Roo.fly(node).removeClass(this.selectedClass);
15051
15052                 return;
15053             }
15054             ns.push(s);
15055         },this);
15056         
15057         this.selections= ns;
15058         this.fireEvent("selectionchange", this, this.selections);
15059     },
15060
15061     /**
15062      * Gets a template node.
15063      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15064      * @return {HTMLElement} The node or null if it wasn't found
15065      */
15066     getNode : function(nodeInfo){
15067         if(typeof nodeInfo == "string"){
15068             return document.getElementById(nodeInfo);
15069         }else if(typeof nodeInfo == "number"){
15070             return this.nodes[nodeInfo];
15071         }
15072         return nodeInfo;
15073     },
15074
15075     /**
15076      * Gets a range template nodes.
15077      * @param {Number} startIndex
15078      * @param {Number} endIndex
15079      * @return {Array} An array of nodes
15080      */
15081     getNodes : function(start, end){
15082         var ns = this.nodes;
15083         start = start || 0;
15084         end = typeof end == "undefined" ? ns.length - 1 : end;
15085         var nodes = [];
15086         if(start <= end){
15087             for(var i = start; i <= end; i++){
15088                 nodes.push(ns[i]);
15089             }
15090         } else{
15091             for(var i = start; i >= end; i--){
15092                 nodes.push(ns[i]);
15093             }
15094         }
15095         return nodes;
15096     },
15097
15098     /**
15099      * Finds the index of the passed node
15100      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15101      * @return {Number} The index of the node or -1
15102      */
15103     indexOf : function(node){
15104         node = this.getNode(node);
15105         if(typeof node.nodeIndex == "number"){
15106             return node.nodeIndex;
15107         }
15108         var ns = this.nodes;
15109         for(var i = 0, len = ns.length; i < len; i++){
15110             if(ns[i] == node){
15111                 return i;
15112             }
15113         }
15114         return -1;
15115     }
15116 });
15117 /*
15118  * - LGPL
15119  *
15120  * based on jquery fullcalendar
15121  * 
15122  */
15123
15124 Roo.bootstrap = Roo.bootstrap || {};
15125 /**
15126  * @class Roo.bootstrap.Calendar
15127  * @extends Roo.bootstrap.Component
15128  * Bootstrap Calendar class
15129  * @cfg {Boolean} loadMask (true|false) default false
15130  * @cfg {Object} header generate the user specific header of the calendar, default false
15131
15132  * @constructor
15133  * Create a new Container
15134  * @param {Object} config The config object
15135  */
15136
15137
15138
15139 Roo.bootstrap.Calendar = function(config){
15140     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15141      this.addEvents({
15142         /**
15143              * @event select
15144              * Fires when a date is selected
15145              * @param {DatePicker} this
15146              * @param {Date} date The selected date
15147              */
15148         'select': true,
15149         /**
15150              * @event monthchange
15151              * Fires when the displayed month changes 
15152              * @param {DatePicker} this
15153              * @param {Date} date The selected month
15154              */
15155         'monthchange': true,
15156         /**
15157              * @event evententer
15158              * Fires when mouse over an event
15159              * @param {Calendar} this
15160              * @param {event} Event
15161              */
15162         'evententer': true,
15163         /**
15164              * @event eventleave
15165              * Fires when the mouse leaves an
15166              * @param {Calendar} this
15167              * @param {event}
15168              */
15169         'eventleave': true,
15170         /**
15171              * @event eventclick
15172              * Fires when the mouse click an
15173              * @param {Calendar} this
15174              * @param {event}
15175              */
15176         'eventclick': true
15177         
15178     });
15179
15180 };
15181
15182 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15183     
15184      /**
15185      * @cfg {Number} startDay
15186      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15187      */
15188     startDay : 0,
15189     
15190     loadMask : false,
15191     
15192     header : false,
15193       
15194     getAutoCreate : function(){
15195         
15196         
15197         var fc_button = function(name, corner, style, content ) {
15198             return Roo.apply({},{
15199                 tag : 'span',
15200                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15201                          (corner.length ?
15202                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15203                             ''
15204                         ),
15205                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15206                 unselectable: 'on'
15207             });
15208         };
15209         
15210         var header = {};
15211         
15212         if(!this.header){
15213             header = {
15214                 tag : 'table',
15215                 cls : 'fc-header',
15216                 style : 'width:100%',
15217                 cn : [
15218                     {
15219                         tag: 'tr',
15220                         cn : [
15221                             {
15222                                 tag : 'td',
15223                                 cls : 'fc-header-left',
15224                                 cn : [
15225                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15226                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15227                                     { tag: 'span', cls: 'fc-header-space' },
15228                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15229
15230
15231                                 ]
15232                             },
15233
15234                             {
15235                                 tag : 'td',
15236                                 cls : 'fc-header-center',
15237                                 cn : [
15238                                     {
15239                                         tag: 'span',
15240                                         cls: 'fc-header-title',
15241                                         cn : {
15242                                             tag: 'H2',
15243                                             html : 'month / year'
15244                                         }
15245                                     }
15246
15247                                 ]
15248                             },
15249                             {
15250                                 tag : 'td',
15251                                 cls : 'fc-header-right',
15252                                 cn : [
15253                               /*      fc_button('month', 'left', '', 'month' ),
15254                                     fc_button('week', '', '', 'week' ),
15255                                     fc_button('day', 'right', '', 'day' )
15256                                 */    
15257
15258                                 ]
15259                             }
15260
15261                         ]
15262                     }
15263                 ]
15264             };
15265         }
15266         
15267         header = this.header;
15268         
15269        
15270         var cal_heads = function() {
15271             var ret = [];
15272             // fixme - handle this.
15273             
15274             for (var i =0; i < Date.dayNames.length; i++) {
15275                 var d = Date.dayNames[i];
15276                 ret.push({
15277                     tag: 'th',
15278                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15279                     html : d.substring(0,3)
15280                 });
15281                 
15282             }
15283             ret[0].cls += ' fc-first';
15284             ret[6].cls += ' fc-last';
15285             return ret;
15286         };
15287         var cal_cell = function(n) {
15288             return  {
15289                 tag: 'td',
15290                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15291                 cn : [
15292                     {
15293                         cn : [
15294                             {
15295                                 cls: 'fc-day-number',
15296                                 html: 'D'
15297                             },
15298                             {
15299                                 cls: 'fc-day-content',
15300                              
15301                                 cn : [
15302                                      {
15303                                         style: 'position: relative;' // height: 17px;
15304                                     }
15305                                 ]
15306                             }
15307                             
15308                             
15309                         ]
15310                     }
15311                 ]
15312                 
15313             }
15314         };
15315         var cal_rows = function() {
15316             
15317             var ret = [];
15318             for (var r = 0; r < 6; r++) {
15319                 var row= {
15320                     tag : 'tr',
15321                     cls : 'fc-week',
15322                     cn : []
15323                 };
15324                 
15325                 for (var i =0; i < Date.dayNames.length; i++) {
15326                     var d = Date.dayNames[i];
15327                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15328
15329                 }
15330                 row.cn[0].cls+=' fc-first';
15331                 row.cn[0].cn[0].style = 'min-height:90px';
15332                 row.cn[6].cls+=' fc-last';
15333                 ret.push(row);
15334                 
15335             }
15336             ret[0].cls += ' fc-first';
15337             ret[4].cls += ' fc-prev-last';
15338             ret[5].cls += ' fc-last';
15339             return ret;
15340             
15341         };
15342         
15343         var cal_table = {
15344             tag: 'table',
15345             cls: 'fc-border-separate',
15346             style : 'width:100%',
15347             cellspacing  : 0,
15348             cn : [
15349                 { 
15350                     tag: 'thead',
15351                     cn : [
15352                         { 
15353                             tag: 'tr',
15354                             cls : 'fc-first fc-last',
15355                             cn : cal_heads()
15356                         }
15357                     ]
15358                 },
15359                 { 
15360                     tag: 'tbody',
15361                     cn : cal_rows()
15362                 }
15363                   
15364             ]
15365         };
15366          
15367          var cfg = {
15368             cls : 'fc fc-ltr',
15369             cn : [
15370                 header,
15371                 {
15372                     cls : 'fc-content',
15373                     style : "position: relative;",
15374                     cn : [
15375                         {
15376                             cls : 'fc-view fc-view-month fc-grid',
15377                             style : 'position: relative',
15378                             unselectable : 'on',
15379                             cn : [
15380                                 {
15381                                     cls : 'fc-event-container',
15382                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15383                                 },
15384                                 cal_table
15385                             ]
15386                         }
15387                     ]
15388     
15389                 }
15390            ] 
15391             
15392         };
15393         
15394          
15395         
15396         return cfg;
15397     },
15398     
15399     
15400     initEvents : function()
15401     {
15402         if(!this.store){
15403             throw "can not find store for calendar";
15404         }
15405         
15406         var mark = {
15407             tag: "div",
15408             cls:"x-dlg-mask",
15409             style: "text-align:center",
15410             cn: [
15411                 {
15412                     tag: "div",
15413                     style: "background-color:white;width:50%;margin:250 auto",
15414                     cn: [
15415                         {
15416                             tag: "img",
15417                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15418                         },
15419                         {
15420                             tag: "span",
15421                             html: "Loading"
15422                         }
15423                         
15424                     ]
15425                 }
15426             ]
15427         };
15428         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15429         
15430         var size = this.el.select('.fc-content', true).first().getSize();
15431         this.maskEl.setSize(size.width, size.height);
15432         this.maskEl.enableDisplayMode("block");
15433         if(!this.loadMask){
15434             this.maskEl.hide();
15435         }
15436         
15437         this.store = Roo.factory(this.store, Roo.data);
15438         this.store.on('load', this.onLoad, this);
15439         this.store.on('beforeload', this.onBeforeLoad, this);
15440         
15441         this.resize();
15442         
15443         this.cells = this.el.select('.fc-day',true);
15444         //Roo.log(this.cells);
15445         this.textNodes = this.el.query('.fc-day-number');
15446         this.cells.addClassOnOver('fc-state-hover');
15447         
15448         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15449         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15450         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15451         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15452         
15453         this.on('monthchange', this.onMonthChange, this);
15454         
15455         this.update(new Date().clearTime());
15456     },
15457     
15458     resize : function() {
15459         var sz  = this.el.getSize();
15460         
15461         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15462         this.el.select('.fc-day-content div',true).setHeight(34);
15463     },
15464     
15465     
15466     // private
15467     showPrevMonth : function(e){
15468         this.update(this.activeDate.add("mo", -1));
15469     },
15470     showToday : function(e){
15471         this.update(new Date().clearTime());
15472     },
15473     // private
15474     showNextMonth : function(e){
15475         this.update(this.activeDate.add("mo", 1));
15476     },
15477
15478     // private
15479     showPrevYear : function(){
15480         this.update(this.activeDate.add("y", -1));
15481     },
15482
15483     // private
15484     showNextYear : function(){
15485         this.update(this.activeDate.add("y", 1));
15486     },
15487
15488     
15489    // private
15490     update : function(date)
15491     {
15492         var vd = this.activeDate;
15493         this.activeDate = date;
15494 //        if(vd && this.el){
15495 //            var t = date.getTime();
15496 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15497 //                Roo.log('using add remove');
15498 //                
15499 //                this.fireEvent('monthchange', this, date);
15500 //                
15501 //                this.cells.removeClass("fc-state-highlight");
15502 //                this.cells.each(function(c){
15503 //                   if(c.dateValue == t){
15504 //                       c.addClass("fc-state-highlight");
15505 //                       setTimeout(function(){
15506 //                            try{c.dom.firstChild.focus();}catch(e){}
15507 //                       }, 50);
15508 //                       return false;
15509 //                   }
15510 //                   return true;
15511 //                });
15512 //                return;
15513 //            }
15514 //        }
15515         
15516         var days = date.getDaysInMonth();
15517         
15518         var firstOfMonth = date.getFirstDateOfMonth();
15519         var startingPos = firstOfMonth.getDay()-this.startDay;
15520         
15521         if(startingPos < this.startDay){
15522             startingPos += 7;
15523         }
15524         
15525         var pm = date.add(Date.MONTH, -1);
15526         var prevStart = pm.getDaysInMonth()-startingPos;
15527 //        
15528         this.cells = this.el.select('.fc-day',true);
15529         this.textNodes = this.el.query('.fc-day-number');
15530         this.cells.addClassOnOver('fc-state-hover');
15531         
15532         var cells = this.cells.elements;
15533         var textEls = this.textNodes;
15534         
15535         Roo.each(cells, function(cell){
15536             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15537         });
15538         
15539         days += startingPos;
15540
15541         // convert everything to numbers so it's fast
15542         var day = 86400000;
15543         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15544         //Roo.log(d);
15545         //Roo.log(pm);
15546         //Roo.log(prevStart);
15547         
15548         var today = new Date().clearTime().getTime();
15549         var sel = date.clearTime().getTime();
15550         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15551         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15552         var ddMatch = this.disabledDatesRE;
15553         var ddText = this.disabledDatesText;
15554         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15555         var ddaysText = this.disabledDaysText;
15556         var format = this.format;
15557         
15558         var setCellClass = function(cal, cell){
15559             cell.row = 0;
15560             cell.events = [];
15561             cell.more = [];
15562             //Roo.log('set Cell Class');
15563             cell.title = "";
15564             var t = d.getTime();
15565             
15566             //Roo.log(d);
15567             
15568             cell.dateValue = t;
15569             if(t == today){
15570                 cell.className += " fc-today";
15571                 cell.className += " fc-state-highlight";
15572                 cell.title = cal.todayText;
15573             }
15574             if(t == sel){
15575                 // disable highlight in other month..
15576                 //cell.className += " fc-state-highlight";
15577                 
15578             }
15579             // disabling
15580             if(t < min) {
15581                 cell.className = " fc-state-disabled";
15582                 cell.title = cal.minText;
15583                 return;
15584             }
15585             if(t > max) {
15586                 cell.className = " fc-state-disabled";
15587                 cell.title = cal.maxText;
15588                 return;
15589             }
15590             if(ddays){
15591                 if(ddays.indexOf(d.getDay()) != -1){
15592                     cell.title = ddaysText;
15593                     cell.className = " fc-state-disabled";
15594                 }
15595             }
15596             if(ddMatch && format){
15597                 var fvalue = d.dateFormat(format);
15598                 if(ddMatch.test(fvalue)){
15599                     cell.title = ddText.replace("%0", fvalue);
15600                     cell.className = " fc-state-disabled";
15601                 }
15602             }
15603             
15604             if (!cell.initialClassName) {
15605                 cell.initialClassName = cell.dom.className;
15606             }
15607             
15608             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15609         };
15610
15611         var i = 0;
15612         
15613         for(; i < startingPos; i++) {
15614             textEls[i].innerHTML = (++prevStart);
15615             d.setDate(d.getDate()+1);
15616             
15617             cells[i].className = "fc-past fc-other-month";
15618             setCellClass(this, cells[i]);
15619         }
15620         
15621         var intDay = 0;
15622         
15623         for(; i < days; i++){
15624             intDay = i - startingPos + 1;
15625             textEls[i].innerHTML = (intDay);
15626             d.setDate(d.getDate()+1);
15627             
15628             cells[i].className = ''; // "x-date-active";
15629             setCellClass(this, cells[i]);
15630         }
15631         var extraDays = 0;
15632         
15633         for(; i < 42; i++) {
15634             textEls[i].innerHTML = (++extraDays);
15635             d.setDate(d.getDate()+1);
15636             
15637             cells[i].className = "fc-future fc-other-month";
15638             setCellClass(this, cells[i]);
15639         }
15640         
15641         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15642         
15643         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15644         
15645         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15646         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15647         
15648         if(totalRows != 6){
15649             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15650             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15651         }
15652         
15653         this.fireEvent('monthchange', this, date);
15654         
15655         
15656         /*
15657         if(!this.internalRender){
15658             var main = this.el.dom.firstChild;
15659             var w = main.offsetWidth;
15660             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15661             Roo.fly(main).setWidth(w);
15662             this.internalRender = true;
15663             // opera does not respect the auto grow header center column
15664             // then, after it gets a width opera refuses to recalculate
15665             // without a second pass
15666             if(Roo.isOpera && !this.secondPass){
15667                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15668                 this.secondPass = true;
15669                 this.update.defer(10, this, [date]);
15670             }
15671         }
15672         */
15673         
15674     },
15675     
15676     findCell : function(dt) {
15677         dt = dt.clearTime().getTime();
15678         var ret = false;
15679         this.cells.each(function(c){
15680             //Roo.log("check " +c.dateValue + '?=' + dt);
15681             if(c.dateValue == dt){
15682                 ret = c;
15683                 return false;
15684             }
15685             return true;
15686         });
15687         
15688         return ret;
15689     },
15690     
15691     findCells : function(ev) {
15692         var s = ev.start.clone().clearTime().getTime();
15693        // Roo.log(s);
15694         var e= ev.end.clone().clearTime().getTime();
15695        // Roo.log(e);
15696         var ret = [];
15697         this.cells.each(function(c){
15698              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15699             
15700             if(c.dateValue > e){
15701                 return ;
15702             }
15703             if(c.dateValue < s){
15704                 return ;
15705             }
15706             ret.push(c);
15707         });
15708         
15709         return ret;    
15710     },
15711     
15712 //    findBestRow: function(cells)
15713 //    {
15714 //        var ret = 0;
15715 //        
15716 //        for (var i =0 ; i < cells.length;i++) {
15717 //            ret  = Math.max(cells[i].rows || 0,ret);
15718 //        }
15719 //        return ret;
15720 //        
15721 //    },
15722     
15723     
15724     addItem : function(ev)
15725     {
15726         // look for vertical location slot in
15727         var cells = this.findCells(ev);
15728         
15729 //        ev.row = this.findBestRow(cells);
15730         
15731         // work out the location.
15732         
15733         var crow = false;
15734         var rows = [];
15735         for(var i =0; i < cells.length; i++) {
15736             
15737             cells[i].row = cells[0].row;
15738             
15739             if(i == 0){
15740                 cells[i].row = cells[i].row + 1;
15741             }
15742             
15743             if (!crow) {
15744                 crow = {
15745                     start : cells[i],
15746                     end :  cells[i]
15747                 };
15748                 continue;
15749             }
15750             if (crow.start.getY() == cells[i].getY()) {
15751                 // on same row.
15752                 crow.end = cells[i];
15753                 continue;
15754             }
15755             // different row.
15756             rows.push(crow);
15757             crow = {
15758                 start: cells[i],
15759                 end : cells[i]
15760             };
15761             
15762         }
15763         
15764         rows.push(crow);
15765         ev.els = [];
15766         ev.rows = rows;
15767         ev.cells = cells;
15768         
15769         cells[0].events.push(ev);
15770         
15771         this.calevents.push(ev);
15772     },
15773     
15774     clearEvents: function() {
15775         
15776         if(!this.calevents){
15777             return;
15778         }
15779         
15780         Roo.each(this.cells.elements, function(c){
15781             c.row = 0;
15782             c.events = [];
15783             c.more = [];
15784         });
15785         
15786         Roo.each(this.calevents, function(e) {
15787             Roo.each(e.els, function(el) {
15788                 el.un('mouseenter' ,this.onEventEnter, this);
15789                 el.un('mouseleave' ,this.onEventLeave, this);
15790                 el.remove();
15791             },this);
15792         },this);
15793         
15794         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15795             e.remove();
15796         });
15797         
15798     },
15799     
15800     renderEvents: function()
15801     {   
15802         var _this = this;
15803         
15804         this.cells.each(function(c) {
15805             
15806             if(c.row < 5){
15807                 return;
15808             }
15809             
15810             var ev = c.events;
15811             
15812             var r = 4;
15813             if(c.row != c.events.length){
15814                 r = 4 - (4 - (c.row - c.events.length));
15815             }
15816             
15817             c.events = ev.slice(0, r);
15818             c.more = ev.slice(r);
15819             
15820             if(c.more.length && c.more.length == 1){
15821                 c.events.push(c.more.pop());
15822             }
15823             
15824             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15825             
15826         });
15827             
15828         this.cells.each(function(c) {
15829             
15830             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15831             
15832             
15833             for (var e = 0; e < c.events.length; e++){
15834                 var ev = c.events[e];
15835                 var rows = ev.rows;
15836                 
15837                 for(var i = 0; i < rows.length; i++) {
15838                 
15839                     // how many rows should it span..
15840
15841                     var  cfg = {
15842                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15843                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15844
15845                         unselectable : "on",
15846                         cn : [
15847                             {
15848                                 cls: 'fc-event-inner',
15849                                 cn : [
15850     //                                {
15851     //                                  tag:'span',
15852     //                                  cls: 'fc-event-time',
15853     //                                  html : cells.length > 1 ? '' : ev.time
15854     //                                },
15855                                     {
15856                                       tag:'span',
15857                                       cls: 'fc-event-title',
15858                                       html : String.format('{0}', ev.title)
15859                                     }
15860
15861
15862                                 ]
15863                             },
15864                             {
15865                                 cls: 'ui-resizable-handle ui-resizable-e',
15866                                 html : '&nbsp;&nbsp;&nbsp'
15867                             }
15868
15869                         ]
15870                     };
15871
15872                     if (i == 0) {
15873                         cfg.cls += ' fc-event-start';
15874                     }
15875                     if ((i+1) == rows.length) {
15876                         cfg.cls += ' fc-event-end';
15877                     }
15878
15879                     var ctr = _this.el.select('.fc-event-container',true).first();
15880                     var cg = ctr.createChild(cfg);
15881
15882                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15883                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15884
15885                     var r = (c.more.length) ? 1 : 0;
15886                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15887                     cg.setWidth(ebox.right - sbox.x -2);
15888
15889                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15890                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15891                     cg.on('click', _this.onEventClick, _this, ev);
15892
15893                     ev.els.push(cg);
15894                     
15895                 }
15896                 
15897             }
15898             
15899             
15900             if(c.more.length){
15901                 var  cfg = {
15902                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15903                     style : 'position: absolute',
15904                     unselectable : "on",
15905                     cn : [
15906                         {
15907                             cls: 'fc-event-inner',
15908                             cn : [
15909                                 {
15910                                   tag:'span',
15911                                   cls: 'fc-event-title',
15912                                   html : 'More'
15913                                 }
15914
15915
15916                             ]
15917                         },
15918                         {
15919                             cls: 'ui-resizable-handle ui-resizable-e',
15920                             html : '&nbsp;&nbsp;&nbsp'
15921                         }
15922
15923                     ]
15924                 };
15925
15926                 var ctr = _this.el.select('.fc-event-container',true).first();
15927                 var cg = ctr.createChild(cfg);
15928
15929                 var sbox = c.select('.fc-day-content',true).first().getBox();
15930                 var ebox = c.select('.fc-day-content',true).first().getBox();
15931                 //Roo.log(cg);
15932                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15933                 cg.setWidth(ebox.right - sbox.x -2);
15934
15935                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15936                 
15937             }
15938             
15939         });
15940         
15941         
15942         
15943     },
15944     
15945     onEventEnter: function (e, el,event,d) {
15946         this.fireEvent('evententer', this, el, event);
15947     },
15948     
15949     onEventLeave: function (e, el,event,d) {
15950         this.fireEvent('eventleave', this, el, event);
15951     },
15952     
15953     onEventClick: function (e, el,event,d) {
15954         this.fireEvent('eventclick', this, el, event);
15955     },
15956     
15957     onMonthChange: function () {
15958         this.store.load();
15959     },
15960     
15961     onMoreEventClick: function(e, el, more)
15962     {
15963         var _this = this;
15964         
15965         this.calpopover.placement = 'right';
15966         this.calpopover.setTitle('More');
15967         
15968         this.calpopover.setContent('');
15969         
15970         var ctr = this.calpopover.el.select('.popover-content', true).first();
15971         
15972         Roo.each(more, function(m){
15973             var cfg = {
15974                 cls : 'fc-event-hori fc-event-draggable',
15975                 html : m.title
15976             };
15977             var cg = ctr.createChild(cfg);
15978             
15979             cg.on('click', _this.onEventClick, _this, m);
15980         });
15981         
15982         this.calpopover.show(el);
15983         
15984         
15985     },
15986     
15987     onLoad: function () 
15988     {   
15989         this.calevents = [];
15990         var cal = this;
15991         
15992         if(this.store.getCount() > 0){
15993             this.store.data.each(function(d){
15994                cal.addItem({
15995                     id : d.data.id,
15996                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15997                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15998                     time : d.data.start_time,
15999                     title : d.data.title,
16000                     description : d.data.description,
16001                     venue : d.data.venue
16002                 });
16003             });
16004         }
16005         
16006         this.renderEvents();
16007         
16008         if(this.calevents.length && this.loadMask){
16009             this.maskEl.hide();
16010         }
16011     },
16012     
16013     onBeforeLoad: function()
16014     {
16015         this.clearEvents();
16016         if(this.loadMask){
16017             this.maskEl.show();
16018         }
16019     }
16020 });
16021
16022  
16023  /*
16024  * - LGPL
16025  *
16026  * element
16027  * 
16028  */
16029
16030 /**
16031  * @class Roo.bootstrap.Popover
16032  * @extends Roo.bootstrap.Component
16033  * Bootstrap Popover class
16034  * @cfg {String} html contents of the popover   (or false to use children..)
16035  * @cfg {String} title of popover (or false to hide)
16036  * @cfg {String} placement how it is placed
16037  * @cfg {String} trigger click || hover (or false to trigger manually)
16038  * @cfg {String} over what (parent or false to trigger manually.)
16039  * @cfg {Number} delay - delay before showing
16040  
16041  * @constructor
16042  * Create a new Popover
16043  * @param {Object} config The config object
16044  */
16045
16046 Roo.bootstrap.Popover = function(config){
16047     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16048     
16049     this.addEvents({
16050         // raw events
16051          /**
16052          * @event show
16053          * After the popover show
16054          * 
16055          * @param {Roo.bootstrap.Popover} this
16056          */
16057         "show" : true,
16058         /**
16059          * @event hide
16060          * After the popover hide
16061          * 
16062          * @param {Roo.bootstrap.Popover} this
16063          */
16064         "hide" : true
16065     });
16066 };
16067
16068 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16069     
16070     title: 'Fill in a title',
16071     html: false,
16072     
16073     placement : 'right',
16074     trigger : 'hover', // hover
16075     
16076     delay : 0,
16077     
16078     over: 'parent',
16079     
16080     can_build_overlaid : false,
16081     
16082     getChildContainer : function()
16083     {
16084         return this.el.select('.popover-content',true).first();
16085     },
16086     
16087     getAutoCreate : function(){
16088          
16089         var cfg = {
16090            cls : 'popover roo-dynamic',
16091            style: 'display:block',
16092            cn : [
16093                 {
16094                     cls : 'arrow'
16095                 },
16096                 {
16097                     cls : 'popover-inner',
16098                     cn : [
16099                         {
16100                             tag: 'h3',
16101                             cls: 'popover-title',
16102                             html : this.title
16103                         },
16104                         {
16105                             cls : 'popover-content',
16106                             html : this.html
16107                         }
16108                     ]
16109                     
16110                 }
16111            ]
16112         };
16113         
16114         return cfg;
16115     },
16116     setTitle: function(str)
16117     {
16118         this.title = str;
16119         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16120     },
16121     setContent: function(str)
16122     {
16123         this.html = str;
16124         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16125     },
16126     // as it get's added to the bottom of the page.
16127     onRender : function(ct, position)
16128     {
16129         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16130         if(!this.el){
16131             var cfg = Roo.apply({},  this.getAutoCreate());
16132             cfg.id = Roo.id();
16133             
16134             if (this.cls) {
16135                 cfg.cls += ' ' + this.cls;
16136             }
16137             if (this.style) {
16138                 cfg.style = this.style;
16139             }
16140             //Roo.log("adding to ");
16141             this.el = Roo.get(document.body).createChild(cfg, position);
16142 //            Roo.log(this.el);
16143         }
16144         this.initEvents();
16145     },
16146     
16147     initEvents : function()
16148     {
16149         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16150         this.el.enableDisplayMode('block');
16151         this.el.hide();
16152         if (this.over === false) {
16153             return; 
16154         }
16155         if (this.triggers === false) {
16156             return;
16157         }
16158         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16159         var triggers = this.trigger ? this.trigger.split(' ') : [];
16160         Roo.each(triggers, function(trigger) {
16161         
16162             if (trigger == 'click') {
16163                 on_el.on('click', this.toggle, this);
16164             } else if (trigger != 'manual') {
16165                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16166                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16167       
16168                 on_el.on(eventIn  ,this.enter, this);
16169                 on_el.on(eventOut, this.leave, this);
16170             }
16171         }, this);
16172         
16173     },
16174     
16175     
16176     // private
16177     timeout : null,
16178     hoverState : null,
16179     
16180     toggle : function () {
16181         this.hoverState == 'in' ? this.leave() : this.enter();
16182     },
16183     
16184     enter : function () {
16185         
16186         clearTimeout(this.timeout);
16187     
16188         this.hoverState = 'in';
16189     
16190         if (!this.delay || !this.delay.show) {
16191             this.show();
16192             return;
16193         }
16194         var _t = this;
16195         this.timeout = setTimeout(function () {
16196             if (_t.hoverState == 'in') {
16197                 _t.show();
16198             }
16199         }, this.delay.show)
16200     },
16201     
16202     leave : function() {
16203         clearTimeout(this.timeout);
16204     
16205         this.hoverState = 'out';
16206     
16207         if (!this.delay || !this.delay.hide) {
16208             this.hide();
16209             return;
16210         }
16211         var _t = this;
16212         this.timeout = setTimeout(function () {
16213             if (_t.hoverState == 'out') {
16214                 _t.hide();
16215             }
16216         }, this.delay.hide)
16217     },
16218     
16219     show : function (on_el)
16220     {
16221         if (!on_el) {
16222             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16223         }
16224         
16225         // set content.
16226         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16227         if (this.html !== false) {
16228             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16229         }
16230         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16231         if (!this.title.length) {
16232             this.el.select('.popover-title',true).hide();
16233         }
16234         
16235         var placement = typeof this.placement == 'function' ?
16236             this.placement.call(this, this.el, on_el) :
16237             this.placement;
16238             
16239         var autoToken = /\s?auto?\s?/i;
16240         var autoPlace = autoToken.test(placement);
16241         if (autoPlace) {
16242             placement = placement.replace(autoToken, '') || 'top';
16243         }
16244         
16245         //this.el.detach()
16246         //this.el.setXY([0,0]);
16247         this.el.show();
16248         this.el.dom.style.display='block';
16249         this.el.addClass(placement);
16250         
16251         //this.el.appendTo(on_el);
16252         
16253         var p = this.getPosition();
16254         var box = this.el.getBox();
16255         
16256         if (autoPlace) {
16257             // fixme..
16258         }
16259         var align = Roo.bootstrap.Popover.alignment[placement];
16260         this.el.alignTo(on_el, align[0],align[1]);
16261         //var arrow = this.el.select('.arrow',true).first();
16262         //arrow.set(align[2], 
16263         
16264         this.el.addClass('in');
16265         
16266         
16267         if (this.el.hasClass('fade')) {
16268             // fade it?
16269         }
16270         
16271         this.hoverState = 'in';
16272         
16273         this.fireEvent('show', this);
16274         
16275     },
16276     hide : function()
16277     {
16278         this.el.setXY([0,0]);
16279         this.el.removeClass('in');
16280         this.el.hide();
16281         this.hoverState = null;
16282         
16283         this.fireEvent('hide', this);
16284     }
16285     
16286 });
16287
16288 Roo.bootstrap.Popover.alignment = {
16289     'left' : ['r-l', [-10,0], 'right'],
16290     'right' : ['l-r', [10,0], 'left'],
16291     'bottom' : ['t-b', [0,10], 'top'],
16292     'top' : [ 'b-t', [0,-10], 'bottom']
16293 };
16294
16295  /*
16296  * - LGPL
16297  *
16298  * Progress
16299  * 
16300  */
16301
16302 /**
16303  * @class Roo.bootstrap.Progress
16304  * @extends Roo.bootstrap.Component
16305  * Bootstrap Progress class
16306  * @cfg {Boolean} striped striped of the progress bar
16307  * @cfg {Boolean} active animated of the progress bar
16308  * 
16309  * 
16310  * @constructor
16311  * Create a new Progress
16312  * @param {Object} config The config object
16313  */
16314
16315 Roo.bootstrap.Progress = function(config){
16316     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16317 };
16318
16319 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16320     
16321     striped : false,
16322     active: false,
16323     
16324     getAutoCreate : function(){
16325         var cfg = {
16326             tag: 'div',
16327             cls: 'progress'
16328         };
16329         
16330         
16331         if(this.striped){
16332             cfg.cls += ' progress-striped';
16333         }
16334       
16335         if(this.active){
16336             cfg.cls += ' active';
16337         }
16338         
16339         
16340         return cfg;
16341     }
16342    
16343 });
16344
16345  
16346
16347  /*
16348  * - LGPL
16349  *
16350  * ProgressBar
16351  * 
16352  */
16353
16354 /**
16355  * @class Roo.bootstrap.ProgressBar
16356  * @extends Roo.bootstrap.Component
16357  * Bootstrap ProgressBar class
16358  * @cfg {Number} aria_valuenow aria-value now
16359  * @cfg {Number} aria_valuemin aria-value min
16360  * @cfg {Number} aria_valuemax aria-value max
16361  * @cfg {String} label label for the progress bar
16362  * @cfg {String} panel (success | info | warning | danger )
16363  * @cfg {String} role role of the progress bar
16364  * @cfg {String} sr_only text
16365  * 
16366  * 
16367  * @constructor
16368  * Create a new ProgressBar
16369  * @param {Object} config The config object
16370  */
16371
16372 Roo.bootstrap.ProgressBar = function(config){
16373     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16374 };
16375
16376 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16377     
16378     aria_valuenow : 0,
16379     aria_valuemin : 0,
16380     aria_valuemax : 100,
16381     label : false,
16382     panel : false,
16383     role : false,
16384     sr_only: false,
16385     
16386     getAutoCreate : function()
16387     {
16388         
16389         var cfg = {
16390             tag: 'div',
16391             cls: 'progress-bar',
16392             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16393         };
16394         
16395         if(this.sr_only){
16396             cfg.cn = {
16397                 tag: 'span',
16398                 cls: 'sr-only',
16399                 html: this.sr_only
16400             }
16401         }
16402         
16403         if(this.role){
16404             cfg.role = this.role;
16405         }
16406         
16407         if(this.aria_valuenow){
16408             cfg['aria-valuenow'] = this.aria_valuenow;
16409         }
16410         
16411         if(this.aria_valuemin){
16412             cfg['aria-valuemin'] = this.aria_valuemin;
16413         }
16414         
16415         if(this.aria_valuemax){
16416             cfg['aria-valuemax'] = this.aria_valuemax;
16417         }
16418         
16419         if(this.label && !this.sr_only){
16420             cfg.html = this.label;
16421         }
16422         
16423         if(this.panel){
16424             cfg.cls += ' progress-bar-' + this.panel;
16425         }
16426         
16427         return cfg;
16428     },
16429     
16430     update : function(aria_valuenow)
16431     {
16432         this.aria_valuenow = aria_valuenow;
16433         
16434         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16435     }
16436    
16437 });
16438
16439  
16440
16441  /*
16442  * - LGPL
16443  *
16444  * column
16445  * 
16446  */
16447
16448 /**
16449  * @class Roo.bootstrap.TabGroup
16450  * @extends Roo.bootstrap.Column
16451  * Bootstrap Column class
16452  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16453  * @cfg {Boolean} carousel true to make the group behave like a carousel
16454  * @cfg {Boolean} bullets show bullets for the panels
16455  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16456  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16457  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16458  * @cfg {Boolean} showarrow (true|false) show arrow default true
16459  * 
16460  * @constructor
16461  * Create a new TabGroup
16462  * @param {Object} config The config object
16463  */
16464
16465 Roo.bootstrap.TabGroup = function(config){
16466     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16467     if (!this.navId) {
16468         this.navId = Roo.id();
16469     }
16470     this.tabs = [];
16471     Roo.bootstrap.TabGroup.register(this);
16472     
16473 };
16474
16475 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16476     
16477     carousel : false,
16478     transition : false,
16479     bullets : 0,
16480     timer : 0,
16481     autoslide : false,
16482     slideFn : false,
16483     slideOnTouch : false,
16484     showarrow : true,
16485     
16486     getAutoCreate : function()
16487     {
16488         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16489         
16490         cfg.cls += ' tab-content';
16491         
16492         if (this.carousel) {
16493             cfg.cls += ' carousel slide';
16494             
16495             cfg.cn = [{
16496                cls : 'carousel-inner',
16497                cn : []
16498             }];
16499         
16500             if(this.bullets  && !Roo.isTouch){
16501                 
16502                 var bullets = {
16503                     cls : 'carousel-bullets',
16504                     cn : []
16505                 };
16506                
16507                 if(this.bullets_cls){
16508                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16509                 }
16510                 
16511                 bullets.cn.push({
16512                     cls : 'clear'
16513                 });
16514                 
16515                 cfg.cn[0].cn.push(bullets);
16516             }
16517             
16518             if(this.showarrow){
16519                 cfg.cn[0].cn.push({
16520                     tag : 'div',
16521                     class : 'carousel-arrow',
16522                     cn : [
16523                         {
16524                             tag : 'div',
16525                             class : 'carousel-prev',
16526                             cn : [
16527                                 {
16528                                     tag : 'i',
16529                                     class : 'fa fa-chevron-left'
16530                                 }
16531                             ]
16532                         },
16533                         {
16534                             tag : 'div',
16535                             class : 'carousel-next',
16536                             cn : [
16537                                 {
16538                                     tag : 'i',
16539                                     class : 'fa fa-chevron-right'
16540                                 }
16541                             ]
16542                         }
16543                     ]
16544                 });
16545             }
16546             
16547         }
16548         
16549         return cfg;
16550     },
16551     
16552     initEvents:  function()
16553     {
16554         if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16555             this.el.on("touchstart", this.onTouchStart, this);
16556         }
16557         
16558         if(this.autoslide){
16559             var _this = this;
16560             
16561             this.slideFn = window.setInterval(function() {
16562                 _this.showPanelNext();
16563             }, this.timer);
16564         }
16565         
16566         if(this.showarrow){
16567             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16568             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16569         }
16570         
16571         
16572     },
16573     
16574     onTouchStart : function(e, el, o)
16575     {
16576         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16577             return;
16578         }
16579         
16580         this.showPanelNext();
16581     },
16582     
16583     getChildContainer : function()
16584     {
16585         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16586     },
16587     
16588     /**
16589     * register a Navigation item
16590     * @param {Roo.bootstrap.NavItem} the navitem to add
16591     */
16592     register : function(item)
16593     {
16594         this.tabs.push( item);
16595         item.navId = this.navId; // not really needed..
16596         this.addBullet();
16597     
16598     },
16599     
16600     getActivePanel : function()
16601     {
16602         var r = false;
16603         Roo.each(this.tabs, function(t) {
16604             if (t.active) {
16605                 r = t;
16606                 return false;
16607             }
16608             return null;
16609         });
16610         return r;
16611         
16612     },
16613     getPanelByName : function(n)
16614     {
16615         var r = false;
16616         Roo.each(this.tabs, function(t) {
16617             if (t.tabId == n) {
16618                 r = t;
16619                 return false;
16620             }
16621             return null;
16622         });
16623         return r;
16624     },
16625     indexOfPanel : function(p)
16626     {
16627         var r = false;
16628         Roo.each(this.tabs, function(t,i) {
16629             if (t.tabId == p.tabId) {
16630                 r = i;
16631                 return false;
16632             }
16633             return null;
16634         });
16635         return r;
16636     },
16637     /**
16638      * show a specific panel
16639      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16640      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16641      */
16642     showPanel : function (pan)
16643     {
16644         if(this.transition || typeof(pan) == 'undefined'){
16645             Roo.log("waiting for the transitionend");
16646             return;
16647         }
16648         
16649         if (typeof(pan) == 'number') {
16650             pan = this.tabs[pan];
16651         }
16652         
16653         if (typeof(pan) == 'string') {
16654             pan = this.getPanelByName(pan);
16655         }
16656         
16657         var cur = this.getActivePanel();
16658         
16659         if(!pan || !cur){
16660             Roo.log('pan or acitve pan is undefined');
16661             return false;
16662         }
16663         
16664         if (pan.tabId == this.getActivePanel().tabId) {
16665             return true;
16666         }
16667         
16668         if (false === cur.fireEvent('beforedeactivate')) {
16669             return false;
16670         }
16671         
16672         if(this.bullets > 0 && !Roo.isTouch){
16673             this.setActiveBullet(this.indexOfPanel(pan));
16674         }
16675         
16676         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16677             
16678             this.transition = true;
16679             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16680             var lr = dir == 'next' ? 'left' : 'right';
16681             pan.el.addClass(dir); // or prev
16682             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16683             cur.el.addClass(lr); // or right
16684             pan.el.addClass(lr);
16685             
16686             var _this = this;
16687             cur.el.on('transitionend', function() {
16688                 Roo.log("trans end?");
16689                 
16690                 pan.el.removeClass([lr,dir]);
16691                 pan.setActive(true);
16692                 
16693                 cur.el.removeClass([lr]);
16694                 cur.setActive(false);
16695                 
16696                 _this.transition = false;
16697                 
16698             }, this, { single:  true } );
16699             
16700             return true;
16701         }
16702         
16703         cur.setActive(false);
16704         pan.setActive(true);
16705         
16706         return true;
16707         
16708     },
16709     showPanelNext : function()
16710     {
16711         var i = this.indexOfPanel(this.getActivePanel());
16712         
16713         if (i >= this.tabs.length - 1 && !this.autoslide) {
16714             return;
16715         }
16716         
16717         if (i >= this.tabs.length - 1 && this.autoslide) {
16718             i = -1;
16719         }
16720         
16721         this.showPanel(this.tabs[i+1]);
16722     },
16723     
16724     showPanelPrev : function()
16725     {
16726         var i = this.indexOfPanel(this.getActivePanel());
16727         
16728         if (i  < 1 && !this.autoslide) {
16729             return;
16730         }
16731         
16732         if (i < 1 && this.autoslide) {
16733             i = this.tabs.length;
16734         }
16735         
16736         this.showPanel(this.tabs[i-1]);
16737     },
16738     
16739     
16740     addBullet: function()
16741     {
16742         if(!this.bullets || Roo.isTouch){
16743             return;
16744         }
16745         var ctr = this.el.select('.carousel-bullets',true).first();
16746         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16747         var bullet = ctr.createChild({
16748             cls : 'bullet bullet-' + i
16749         },ctr.dom.lastChild);
16750         
16751         
16752         var _this = this;
16753         
16754         bullet.on('click', (function(e, el, o, ii, t){
16755
16756             e.preventDefault();
16757
16758             this.showPanel(ii);
16759
16760             if(this.autoslide && this.slideFn){
16761                 clearInterval(this.slideFn);
16762                 this.slideFn = window.setInterval(function() {
16763                     _this.showPanelNext();
16764                 }, this.timer);
16765             }
16766
16767         }).createDelegate(this, [i, bullet], true));
16768                 
16769         
16770     },
16771      
16772     setActiveBullet : function(i)
16773     {
16774         if(Roo.isTouch){
16775             return;
16776         }
16777         
16778         Roo.each(this.el.select('.bullet', true).elements, function(el){
16779             el.removeClass('selected');
16780         });
16781
16782         var bullet = this.el.select('.bullet-' + i, true).first();
16783         
16784         if(!bullet){
16785             return;
16786         }
16787         
16788         bullet.addClass('selected');
16789     }
16790     
16791     
16792   
16793 });
16794
16795  
16796
16797  
16798  
16799 Roo.apply(Roo.bootstrap.TabGroup, {
16800     
16801     groups: {},
16802      /**
16803     * register a Navigation Group
16804     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16805     */
16806     register : function(navgrp)
16807     {
16808         this.groups[navgrp.navId] = navgrp;
16809         
16810     },
16811     /**
16812     * fetch a Navigation Group based on the navigation ID
16813     * if one does not exist , it will get created.
16814     * @param {string} the navgroup to add
16815     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16816     */
16817     get: function(navId) {
16818         if (typeof(this.groups[navId]) == 'undefined') {
16819             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16820         }
16821         return this.groups[navId] ;
16822     }
16823     
16824     
16825     
16826 });
16827
16828  /*
16829  * - LGPL
16830  *
16831  * TabPanel
16832  * 
16833  */
16834
16835 /**
16836  * @class Roo.bootstrap.TabPanel
16837  * @extends Roo.bootstrap.Component
16838  * Bootstrap TabPanel class
16839  * @cfg {Boolean} active panel active
16840  * @cfg {String} html panel content
16841  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16842  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16843  * 
16844  * 
16845  * @constructor
16846  * Create a new TabPanel
16847  * @param {Object} config The config object
16848  */
16849
16850 Roo.bootstrap.TabPanel = function(config){
16851     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16852     this.addEvents({
16853         /**
16854              * @event changed
16855              * Fires when the active status changes
16856              * @param {Roo.bootstrap.TabPanel} this
16857              * @param {Boolean} state the new state
16858             
16859          */
16860         'changed': true,
16861         /**
16862              * @event beforedeactivate
16863              * Fires before a tab is de-activated - can be used to do validation on a form.
16864              * @param {Roo.bootstrap.TabPanel} this
16865              * @return {Boolean} false if there is an error
16866             
16867          */
16868         'beforedeactivate': true
16869      });
16870     
16871     this.tabId = this.tabId || Roo.id();
16872   
16873 };
16874
16875 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16876     
16877     active: false,
16878     html: false,
16879     tabId: false,
16880     navId : false,
16881     
16882     getAutoCreate : function(){
16883         var cfg = {
16884             tag: 'div',
16885             // item is needed for carousel - not sure if it has any effect otherwise
16886             cls: 'tab-pane item',
16887             html: this.html || ''
16888         };
16889         
16890         if(this.active){
16891             cfg.cls += ' active';
16892         }
16893         
16894         if(this.tabId){
16895             cfg.tabId = this.tabId;
16896         }
16897         
16898         
16899         return cfg;
16900     },
16901     
16902     initEvents:  function()
16903     {
16904         var p = this.parent();
16905         this.navId = this.navId || p.navId;
16906         
16907         if (typeof(this.navId) != 'undefined') {
16908             // not really needed.. but just in case.. parent should be a NavGroup.
16909             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16910             
16911             tg.register(this);
16912             
16913             var i = tg.tabs.length - 1;
16914             
16915             if(this.active && tg.bullets > 0 && i < tg.bullets){
16916                 tg.setActiveBullet(i);
16917             }
16918         }
16919         
16920     },
16921     
16922     
16923     onRender : function(ct, position)
16924     {
16925        // Roo.log("Call onRender: " + this.xtype);
16926         
16927         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16928         
16929         
16930         
16931         
16932         
16933     },
16934     
16935     setActive: function(state)
16936     {
16937         Roo.log("panel - set active " + this.tabId + "=" + state);
16938         
16939         this.active = state;
16940         if (!state) {
16941             this.el.removeClass('active');
16942             
16943         } else  if (!this.el.hasClass('active')) {
16944             this.el.addClass('active');
16945         }
16946         
16947         this.fireEvent('changed', this, state);
16948     }
16949     
16950     
16951 });
16952  
16953
16954  
16955
16956  /*
16957  * - LGPL
16958  *
16959  * DateField
16960  * 
16961  */
16962
16963 /**
16964  * @class Roo.bootstrap.DateField
16965  * @extends Roo.bootstrap.Input
16966  * Bootstrap DateField class
16967  * @cfg {Number} weekStart default 0
16968  * @cfg {String} viewMode default empty, (months|years)
16969  * @cfg {String} minViewMode default empty, (months|years)
16970  * @cfg {Number} startDate default -Infinity
16971  * @cfg {Number} endDate default Infinity
16972  * @cfg {Boolean} todayHighlight default false
16973  * @cfg {Boolean} todayBtn default false
16974  * @cfg {Boolean} calendarWeeks default false
16975  * @cfg {Object} daysOfWeekDisabled default empty
16976  * @cfg {Boolean} singleMode default false (true | false)
16977  * 
16978  * @cfg {Boolean} keyboardNavigation default true
16979  * @cfg {String} language default en
16980  * 
16981  * @constructor
16982  * Create a new DateField
16983  * @param {Object} config The config object
16984  */
16985
16986 Roo.bootstrap.DateField = function(config){
16987     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16988      this.addEvents({
16989             /**
16990              * @event show
16991              * Fires when this field show.
16992              * @param {Roo.bootstrap.DateField} this
16993              * @param {Mixed} date The date value
16994              */
16995             show : true,
16996             /**
16997              * @event show
16998              * Fires when this field hide.
16999              * @param {Roo.bootstrap.DateField} this
17000              * @param {Mixed} date The date value
17001              */
17002             hide : true,
17003             /**
17004              * @event select
17005              * Fires when select a date.
17006              * @param {Roo.bootstrap.DateField} this
17007              * @param {Mixed} date The date value
17008              */
17009             select : true,
17010             /**
17011              * @event beforeselect
17012              * Fires when before select a date.
17013              * @param {Roo.bootstrap.DateField} this
17014              * @param {Mixed} date The date value
17015              */
17016             beforeselect : true
17017         });
17018 };
17019
17020 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17021     
17022     /**
17023      * @cfg {String} format
17024      * The default date format string which can be overriden for localization support.  The format must be
17025      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17026      */
17027     format : "m/d/y",
17028     /**
17029      * @cfg {String} altFormats
17030      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17031      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17032      */
17033     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17034     
17035     weekStart : 0,
17036     
17037     viewMode : '',
17038     
17039     minViewMode : '',
17040     
17041     todayHighlight : false,
17042     
17043     todayBtn: false,
17044     
17045     language: 'en',
17046     
17047     keyboardNavigation: true,
17048     
17049     calendarWeeks: false,
17050     
17051     startDate: -Infinity,
17052     
17053     endDate: Infinity,
17054     
17055     daysOfWeekDisabled: [],
17056     
17057     _events: [],
17058     
17059     singleMode : false,
17060     
17061     UTCDate: function()
17062     {
17063         return new Date(Date.UTC.apply(Date, arguments));
17064     },
17065     
17066     UTCToday: function()
17067     {
17068         var today = new Date();
17069         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17070     },
17071     
17072     getDate: function() {
17073             var d = this.getUTCDate();
17074             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17075     },
17076     
17077     getUTCDate: function() {
17078             return this.date;
17079     },
17080     
17081     setDate: function(d) {
17082             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17083     },
17084     
17085     setUTCDate: function(d) {
17086             this.date = d;
17087             this.setValue(this.formatDate(this.date));
17088     },
17089         
17090     onRender: function(ct, position)
17091     {
17092         
17093         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17094         
17095         this.language = this.language || 'en';
17096         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17097         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17098         
17099         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17100         this.format = this.format || 'm/d/y';
17101         this.isInline = false;
17102         this.isInput = true;
17103         this.component = this.el.select('.add-on', true).first() || false;
17104         this.component = (this.component && this.component.length === 0) ? false : this.component;
17105         this.hasInput = this.component && this.inputEL().length;
17106         
17107         if (typeof(this.minViewMode === 'string')) {
17108             switch (this.minViewMode) {
17109                 case 'months':
17110                     this.minViewMode = 1;
17111                     break;
17112                 case 'years':
17113                     this.minViewMode = 2;
17114                     break;
17115                 default:
17116                     this.minViewMode = 0;
17117                     break;
17118             }
17119         }
17120         
17121         if (typeof(this.viewMode === 'string')) {
17122             switch (this.viewMode) {
17123                 case 'months':
17124                     this.viewMode = 1;
17125                     break;
17126                 case 'years':
17127                     this.viewMode = 2;
17128                     break;
17129                 default:
17130                     this.viewMode = 0;
17131                     break;
17132             }
17133         }
17134                 
17135         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17136         
17137 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17138         
17139         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17140         
17141         this.picker().on('mousedown', this.onMousedown, this);
17142         this.picker().on('click', this.onClick, this);
17143         
17144         this.picker().addClass('datepicker-dropdown');
17145         
17146         this.startViewMode = this.viewMode;
17147         
17148         if(this.singleMode){
17149             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17150                 v.setVisibilityMode(Roo.Element.DISPLAY);
17151                 v.hide();
17152             });
17153             
17154             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17155                 v.setStyle('width', '189px');
17156             });
17157         }
17158         
17159         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17160             if(!this.calendarWeeks){
17161                 v.remove();
17162                 return;
17163             }
17164             
17165             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17166             v.attr('colspan', function(i, val){
17167                 return parseInt(val) + 1;
17168             });
17169         });
17170                         
17171         
17172         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17173         
17174         this.setStartDate(this.startDate);
17175         this.setEndDate(this.endDate);
17176         
17177         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17178         
17179         this.fillDow();
17180         this.fillMonths();
17181         this.update();
17182         this.showMode();
17183         
17184         if(this.isInline) {
17185             this.show();
17186         }
17187     },
17188     
17189     picker : function()
17190     {
17191         return this.pickerEl;
17192 //        return this.el.select('.datepicker', true).first();
17193     },
17194     
17195     fillDow: function()
17196     {
17197         var dowCnt = this.weekStart;
17198         
17199         var dow = {
17200             tag: 'tr',
17201             cn: [
17202                 
17203             ]
17204         };
17205         
17206         if(this.calendarWeeks){
17207             dow.cn.push({
17208                 tag: 'th',
17209                 cls: 'cw',
17210                 html: '&nbsp;'
17211             })
17212         }
17213         
17214         while (dowCnt < this.weekStart + 7) {
17215             dow.cn.push({
17216                 tag: 'th',
17217                 cls: 'dow',
17218                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17219             });
17220         }
17221         
17222         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17223     },
17224     
17225     fillMonths: function()
17226     {    
17227         var i = 0;
17228         var months = this.picker().select('>.datepicker-months td', true).first();
17229         
17230         months.dom.innerHTML = '';
17231         
17232         while (i < 12) {
17233             var month = {
17234                 tag: 'span',
17235                 cls: 'month',
17236                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17237             };
17238             
17239             months.createChild(month);
17240         }
17241         
17242     },
17243     
17244     update: function()
17245     {
17246         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;
17247         
17248         if (this.date < this.startDate) {
17249             this.viewDate = new Date(this.startDate);
17250         } else if (this.date > this.endDate) {
17251             this.viewDate = new Date(this.endDate);
17252         } else {
17253             this.viewDate = new Date(this.date);
17254         }
17255         
17256         this.fill();
17257     },
17258     
17259     fill: function() 
17260     {
17261         var d = new Date(this.viewDate),
17262                 year = d.getUTCFullYear(),
17263                 month = d.getUTCMonth(),
17264                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17265                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17266                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17267                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17268                 currentDate = this.date && this.date.valueOf(),
17269                 today = this.UTCToday();
17270         
17271         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17272         
17273 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17274         
17275 //        this.picker.select('>tfoot th.today').
17276 //                                              .text(dates[this.language].today)
17277 //                                              .toggle(this.todayBtn !== false);
17278     
17279         this.updateNavArrows();
17280         this.fillMonths();
17281                                                 
17282         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17283         
17284         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17285          
17286         prevMonth.setUTCDate(day);
17287         
17288         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17289         
17290         var nextMonth = new Date(prevMonth);
17291         
17292         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17293         
17294         nextMonth = nextMonth.valueOf();
17295         
17296         var fillMonths = false;
17297         
17298         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17299         
17300         while(prevMonth.valueOf() < nextMonth) {
17301             var clsName = '';
17302             
17303             if (prevMonth.getUTCDay() === this.weekStart) {
17304                 if(fillMonths){
17305                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17306                 }
17307                     
17308                 fillMonths = {
17309                     tag: 'tr',
17310                     cn: []
17311                 };
17312                 
17313                 if(this.calendarWeeks){
17314                     // ISO 8601: First week contains first thursday.
17315                     // ISO also states week starts on Monday, but we can be more abstract here.
17316                     var
17317                     // Start of current week: based on weekstart/current date
17318                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17319                     // Thursday of this week
17320                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17321                     // First Thursday of year, year from thursday
17322                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17323                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17324                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17325                     
17326                     fillMonths.cn.push({
17327                         tag: 'td',
17328                         cls: 'cw',
17329                         html: calWeek
17330                     });
17331                 }
17332             }
17333             
17334             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17335                 clsName += ' old';
17336             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17337                 clsName += ' new';
17338             }
17339             if (this.todayHighlight &&
17340                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17341                 prevMonth.getUTCMonth() == today.getMonth() &&
17342                 prevMonth.getUTCDate() == today.getDate()) {
17343                 clsName += ' today';
17344             }
17345             
17346             if (currentDate && prevMonth.valueOf() === currentDate) {
17347                 clsName += ' active';
17348             }
17349             
17350             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17351                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17352                     clsName += ' disabled';
17353             }
17354             
17355             fillMonths.cn.push({
17356                 tag: 'td',
17357                 cls: 'day ' + clsName,
17358                 html: prevMonth.getDate()
17359             });
17360             
17361             prevMonth.setDate(prevMonth.getDate()+1);
17362         }
17363           
17364         var currentYear = this.date && this.date.getUTCFullYear();
17365         var currentMonth = this.date && this.date.getUTCMonth();
17366         
17367         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17368         
17369         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17370             v.removeClass('active');
17371             
17372             if(currentYear === year && k === currentMonth){
17373                 v.addClass('active');
17374             }
17375             
17376             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17377                 v.addClass('disabled');
17378             }
17379             
17380         });
17381         
17382         
17383         year = parseInt(year/10, 10) * 10;
17384         
17385         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17386         
17387         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17388         
17389         year -= 1;
17390         for (var i = -1; i < 11; i++) {
17391             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17392                 tag: 'span',
17393                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17394                 html: year
17395             });
17396             
17397             year += 1;
17398         }
17399     },
17400     
17401     showMode: function(dir) 
17402     {
17403         if (dir) {
17404             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17405         }
17406         
17407         Roo.each(this.picker().select('>div',true).elements, function(v){
17408             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17409             v.hide();
17410         });
17411         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17412     },
17413     
17414     place: function()
17415     {
17416         if(this.isInline) {
17417             return;
17418         }
17419         
17420         this.picker().removeClass(['bottom', 'top']);
17421         
17422         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17423             /*
17424              * place to the top of element!
17425              *
17426              */
17427             
17428             this.picker().addClass('top');
17429             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17430             
17431             return;
17432         }
17433         
17434         this.picker().addClass('bottom');
17435         
17436         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17437     },
17438     
17439     parseDate : function(value)
17440     {
17441         if(!value || value instanceof Date){
17442             return value;
17443         }
17444         var v = Date.parseDate(value, this.format);
17445         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17446             v = Date.parseDate(value, 'Y-m-d');
17447         }
17448         if(!v && this.altFormats){
17449             if(!this.altFormatsArray){
17450                 this.altFormatsArray = this.altFormats.split("|");
17451             }
17452             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17453                 v = Date.parseDate(value, this.altFormatsArray[i]);
17454             }
17455         }
17456         return v;
17457     },
17458     
17459     formatDate : function(date, fmt)
17460     {   
17461         return (!date || !(date instanceof Date)) ?
17462         date : date.dateFormat(fmt || this.format);
17463     },
17464     
17465     onFocus : function()
17466     {
17467         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17468         this.show();
17469     },
17470     
17471     onBlur : function()
17472     {
17473         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17474         
17475         var d = this.inputEl().getValue();
17476         
17477         this.setValue(d);
17478                 
17479         this.hide();
17480     },
17481     
17482     show : function()
17483     {
17484         this.picker().show();
17485         this.update();
17486         this.place();
17487         
17488         this.fireEvent('show', this, this.date);
17489     },
17490     
17491     hide : function()
17492     {
17493         if(this.isInline) {
17494             return;
17495         }
17496         this.picker().hide();
17497         this.viewMode = this.startViewMode;
17498         this.showMode();
17499         
17500         this.fireEvent('hide', this, this.date);
17501         
17502     },
17503     
17504     onMousedown: function(e)
17505     {
17506         e.stopPropagation();
17507         e.preventDefault();
17508     },
17509     
17510     keyup: function(e)
17511     {
17512         Roo.bootstrap.DateField.superclass.keyup.call(this);
17513         this.update();
17514     },
17515
17516     setValue: function(v)
17517     {
17518         if(this.fireEvent('beforeselect', this, v) !== false){
17519             var d = new Date(this.parseDate(v) ).clearTime();
17520         
17521             if(isNaN(d.getTime())){
17522                 this.date = this.viewDate = '';
17523                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17524                 return;
17525             }
17526
17527             v = this.formatDate(d);
17528
17529             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17530
17531             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17532
17533             this.update();
17534
17535             this.fireEvent('select', this, this.date);
17536         }
17537     },
17538     
17539     getValue: function()
17540     {
17541         return this.formatDate(this.date);
17542     },
17543     
17544     fireKey: function(e)
17545     {
17546         if (!this.picker().isVisible()){
17547             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17548                 this.show();
17549             }
17550             return;
17551         }
17552         
17553         var dateChanged = false,
17554         dir, day, month,
17555         newDate, newViewDate;
17556         
17557         switch(e.keyCode){
17558             case 27: // escape
17559                 this.hide();
17560                 e.preventDefault();
17561                 break;
17562             case 37: // left
17563             case 39: // right
17564                 if (!this.keyboardNavigation) {
17565                     break;
17566                 }
17567                 dir = e.keyCode == 37 ? -1 : 1;
17568                 
17569                 if (e.ctrlKey){
17570                     newDate = this.moveYear(this.date, dir);
17571                     newViewDate = this.moveYear(this.viewDate, dir);
17572                 } else if (e.shiftKey){
17573                     newDate = this.moveMonth(this.date, dir);
17574                     newViewDate = this.moveMonth(this.viewDate, dir);
17575                 } else {
17576                     newDate = new Date(this.date);
17577                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17578                     newViewDate = new Date(this.viewDate);
17579                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17580                 }
17581                 if (this.dateWithinRange(newDate)){
17582                     this.date = newDate;
17583                     this.viewDate = newViewDate;
17584                     this.setValue(this.formatDate(this.date));
17585 //                    this.update();
17586                     e.preventDefault();
17587                     dateChanged = true;
17588                 }
17589                 break;
17590             case 38: // up
17591             case 40: // down
17592                 if (!this.keyboardNavigation) {
17593                     break;
17594                 }
17595                 dir = e.keyCode == 38 ? -1 : 1;
17596                 if (e.ctrlKey){
17597                     newDate = this.moveYear(this.date, dir);
17598                     newViewDate = this.moveYear(this.viewDate, dir);
17599                 } else if (e.shiftKey){
17600                     newDate = this.moveMonth(this.date, dir);
17601                     newViewDate = this.moveMonth(this.viewDate, dir);
17602                 } else {
17603                     newDate = new Date(this.date);
17604                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17605                     newViewDate = new Date(this.viewDate);
17606                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17607                 }
17608                 if (this.dateWithinRange(newDate)){
17609                     this.date = newDate;
17610                     this.viewDate = newViewDate;
17611                     this.setValue(this.formatDate(this.date));
17612 //                    this.update();
17613                     e.preventDefault();
17614                     dateChanged = true;
17615                 }
17616                 break;
17617             case 13: // enter
17618                 this.setValue(this.formatDate(this.date));
17619                 this.hide();
17620                 e.preventDefault();
17621                 break;
17622             case 9: // tab
17623                 this.setValue(this.formatDate(this.date));
17624                 this.hide();
17625                 break;
17626             case 16: // shift
17627             case 17: // ctrl
17628             case 18: // alt
17629                 break;
17630             default :
17631                 this.hide();
17632                 
17633         }
17634     },
17635     
17636     
17637     onClick: function(e) 
17638     {
17639         e.stopPropagation();
17640         e.preventDefault();
17641         
17642         var target = e.getTarget();
17643         
17644         if(target.nodeName.toLowerCase() === 'i'){
17645             target = Roo.get(target).dom.parentNode;
17646         }
17647         
17648         var nodeName = target.nodeName;
17649         var className = target.className;
17650         var html = target.innerHTML;
17651         //Roo.log(nodeName);
17652         
17653         switch(nodeName.toLowerCase()) {
17654             case 'th':
17655                 switch(className) {
17656                     case 'switch':
17657                         this.showMode(1);
17658                         break;
17659                     case 'prev':
17660                     case 'next':
17661                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17662                         switch(this.viewMode){
17663                                 case 0:
17664                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17665                                         break;
17666                                 case 1:
17667                                 case 2:
17668                                         this.viewDate = this.moveYear(this.viewDate, dir);
17669                                         break;
17670                         }
17671                         this.fill();
17672                         break;
17673                     case 'today':
17674                         var date = new Date();
17675                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17676 //                        this.fill()
17677                         this.setValue(this.formatDate(this.date));
17678                         
17679                         this.hide();
17680                         break;
17681                 }
17682                 break;
17683             case 'span':
17684                 if (className.indexOf('disabled') < 0) {
17685                     this.viewDate.setUTCDate(1);
17686                     if (className.indexOf('month') > -1) {
17687                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17688                     } else {
17689                         var year = parseInt(html, 10) || 0;
17690                         this.viewDate.setUTCFullYear(year);
17691                         
17692                     }
17693                     
17694                     if(this.singleMode){
17695                         this.setValue(this.formatDate(this.viewDate));
17696                         this.hide();
17697                         return;
17698                     }
17699                     
17700                     this.showMode(-1);
17701                     this.fill();
17702                 }
17703                 break;
17704                 
17705             case 'td':
17706                 //Roo.log(className);
17707                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17708                     var day = parseInt(html, 10) || 1;
17709                     var year = this.viewDate.getUTCFullYear(),
17710                         month = this.viewDate.getUTCMonth();
17711
17712                     if (className.indexOf('old') > -1) {
17713                         if(month === 0 ){
17714                             month = 11;
17715                             year -= 1;
17716                         }else{
17717                             month -= 1;
17718                         }
17719                     } else if (className.indexOf('new') > -1) {
17720                         if (month == 11) {
17721                             month = 0;
17722                             year += 1;
17723                         } else {
17724                             month += 1;
17725                         }
17726                     }
17727                     //Roo.log([year,month,day]);
17728                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17729                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17730 //                    this.fill();
17731                     //Roo.log(this.formatDate(this.date));
17732                     this.setValue(this.formatDate(this.date));
17733                     this.hide();
17734                 }
17735                 break;
17736         }
17737     },
17738     
17739     setStartDate: function(startDate)
17740     {
17741         this.startDate = startDate || -Infinity;
17742         if (this.startDate !== -Infinity) {
17743             this.startDate = this.parseDate(this.startDate);
17744         }
17745         this.update();
17746         this.updateNavArrows();
17747     },
17748
17749     setEndDate: function(endDate)
17750     {
17751         this.endDate = endDate || Infinity;
17752         if (this.endDate !== Infinity) {
17753             this.endDate = this.parseDate(this.endDate);
17754         }
17755         this.update();
17756         this.updateNavArrows();
17757     },
17758     
17759     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17760     {
17761         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17762         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17763             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17764         }
17765         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17766             return parseInt(d, 10);
17767         });
17768         this.update();
17769         this.updateNavArrows();
17770     },
17771     
17772     updateNavArrows: function() 
17773     {
17774         if(this.singleMode){
17775             return;
17776         }
17777         
17778         var d = new Date(this.viewDate),
17779         year = d.getUTCFullYear(),
17780         month = d.getUTCMonth();
17781         
17782         Roo.each(this.picker().select('.prev', true).elements, function(v){
17783             v.show();
17784             switch (this.viewMode) {
17785                 case 0:
17786
17787                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17788                         v.hide();
17789                     }
17790                     break;
17791                 case 1:
17792                 case 2:
17793                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17794                         v.hide();
17795                     }
17796                     break;
17797             }
17798         });
17799         
17800         Roo.each(this.picker().select('.next', true).elements, function(v){
17801             v.show();
17802             switch (this.viewMode) {
17803                 case 0:
17804
17805                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17806                         v.hide();
17807                     }
17808                     break;
17809                 case 1:
17810                 case 2:
17811                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17812                         v.hide();
17813                     }
17814                     break;
17815             }
17816         })
17817     },
17818     
17819     moveMonth: function(date, dir)
17820     {
17821         if (!dir) {
17822             return date;
17823         }
17824         var new_date = new Date(date.valueOf()),
17825         day = new_date.getUTCDate(),
17826         month = new_date.getUTCMonth(),
17827         mag = Math.abs(dir),
17828         new_month, test;
17829         dir = dir > 0 ? 1 : -1;
17830         if (mag == 1){
17831             test = dir == -1
17832             // If going back one month, make sure month is not current month
17833             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17834             ? function(){
17835                 return new_date.getUTCMonth() == month;
17836             }
17837             // If going forward one month, make sure month is as expected
17838             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17839             : function(){
17840                 return new_date.getUTCMonth() != new_month;
17841             };
17842             new_month = month + dir;
17843             new_date.setUTCMonth(new_month);
17844             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17845             if (new_month < 0 || new_month > 11) {
17846                 new_month = (new_month + 12) % 12;
17847             }
17848         } else {
17849             // For magnitudes >1, move one month at a time...
17850             for (var i=0; i<mag; i++) {
17851                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17852                 new_date = this.moveMonth(new_date, dir);
17853             }
17854             // ...then reset the day, keeping it in the new month
17855             new_month = new_date.getUTCMonth();
17856             new_date.setUTCDate(day);
17857             test = function(){
17858                 return new_month != new_date.getUTCMonth();
17859             };
17860         }
17861         // Common date-resetting loop -- if date is beyond end of month, make it
17862         // end of month
17863         while (test()){
17864             new_date.setUTCDate(--day);
17865             new_date.setUTCMonth(new_month);
17866         }
17867         return new_date;
17868     },
17869
17870     moveYear: function(date, dir)
17871     {
17872         return this.moveMonth(date, dir*12);
17873     },
17874
17875     dateWithinRange: function(date)
17876     {
17877         return date >= this.startDate && date <= this.endDate;
17878     },
17879
17880     
17881     remove: function() 
17882     {
17883         this.picker().remove();
17884     }
17885    
17886 });
17887
17888 Roo.apply(Roo.bootstrap.DateField,  {
17889     
17890     head : {
17891         tag: 'thead',
17892         cn: [
17893         {
17894             tag: 'tr',
17895             cn: [
17896             {
17897                 tag: 'th',
17898                 cls: 'prev',
17899                 html: '<i class="fa fa-arrow-left"/>'
17900             },
17901             {
17902                 tag: 'th',
17903                 cls: 'switch',
17904                 colspan: '5'
17905             },
17906             {
17907                 tag: 'th',
17908                 cls: 'next',
17909                 html: '<i class="fa fa-arrow-right"/>'
17910             }
17911
17912             ]
17913         }
17914         ]
17915     },
17916     
17917     content : {
17918         tag: 'tbody',
17919         cn: [
17920         {
17921             tag: 'tr',
17922             cn: [
17923             {
17924                 tag: 'td',
17925                 colspan: '7'
17926             }
17927             ]
17928         }
17929         ]
17930     },
17931     
17932     footer : {
17933         tag: 'tfoot',
17934         cn: [
17935         {
17936             tag: 'tr',
17937             cn: [
17938             {
17939                 tag: 'th',
17940                 colspan: '7',
17941                 cls: 'today'
17942             }
17943                     
17944             ]
17945         }
17946         ]
17947     },
17948     
17949     dates:{
17950         en: {
17951             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17952             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17953             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17954             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17955             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17956             today: "Today"
17957         }
17958     },
17959     
17960     modes: [
17961     {
17962         clsName: 'days',
17963         navFnc: 'Month',
17964         navStep: 1
17965     },
17966     {
17967         clsName: 'months',
17968         navFnc: 'FullYear',
17969         navStep: 1
17970     },
17971     {
17972         clsName: 'years',
17973         navFnc: 'FullYear',
17974         navStep: 10
17975     }]
17976 });
17977
17978 Roo.apply(Roo.bootstrap.DateField,  {
17979   
17980     template : {
17981         tag: 'div',
17982         cls: 'datepicker dropdown-menu roo-dynamic',
17983         cn: [
17984         {
17985             tag: 'div',
17986             cls: 'datepicker-days',
17987             cn: [
17988             {
17989                 tag: 'table',
17990                 cls: 'table-condensed',
17991                 cn:[
17992                 Roo.bootstrap.DateField.head,
17993                 {
17994                     tag: 'tbody'
17995                 },
17996                 Roo.bootstrap.DateField.footer
17997                 ]
17998             }
17999             ]
18000         },
18001         {
18002             tag: 'div',
18003             cls: 'datepicker-months',
18004             cn: [
18005             {
18006                 tag: 'table',
18007                 cls: 'table-condensed',
18008                 cn:[
18009                 Roo.bootstrap.DateField.head,
18010                 Roo.bootstrap.DateField.content,
18011                 Roo.bootstrap.DateField.footer
18012                 ]
18013             }
18014             ]
18015         },
18016         {
18017             tag: 'div',
18018             cls: 'datepicker-years',
18019             cn: [
18020             {
18021                 tag: 'table',
18022                 cls: 'table-condensed',
18023                 cn:[
18024                 Roo.bootstrap.DateField.head,
18025                 Roo.bootstrap.DateField.content,
18026                 Roo.bootstrap.DateField.footer
18027                 ]
18028             }
18029             ]
18030         }
18031         ]
18032     }
18033 });
18034
18035  
18036
18037  /*
18038  * - LGPL
18039  *
18040  * TimeField
18041  * 
18042  */
18043
18044 /**
18045  * @class Roo.bootstrap.TimeField
18046  * @extends Roo.bootstrap.Input
18047  * Bootstrap DateField class
18048  * 
18049  * 
18050  * @constructor
18051  * Create a new TimeField
18052  * @param {Object} config The config object
18053  */
18054
18055 Roo.bootstrap.TimeField = function(config){
18056     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18057     this.addEvents({
18058             /**
18059              * @event show
18060              * Fires when this field show.
18061              * @param {Roo.bootstrap.DateField} thisthis
18062              * @param {Mixed} date The date value
18063              */
18064             show : true,
18065             /**
18066              * @event show
18067              * Fires when this field hide.
18068              * @param {Roo.bootstrap.DateField} this
18069              * @param {Mixed} date The date value
18070              */
18071             hide : true,
18072             /**
18073              * @event select
18074              * Fires when select a date.
18075              * @param {Roo.bootstrap.DateField} this
18076              * @param {Mixed} date The date value
18077              */
18078             select : true
18079         });
18080 };
18081
18082 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18083     
18084     /**
18085      * @cfg {String} format
18086      * The default time format string which can be overriden for localization support.  The format must be
18087      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18088      */
18089     format : "H:i",
18090        
18091     onRender: function(ct, position)
18092     {
18093         
18094         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18095                 
18096         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18097         
18098         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18099         
18100         this.pop = this.picker().select('>.datepicker-time',true).first();
18101         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18102         
18103         this.picker().on('mousedown', this.onMousedown, this);
18104         this.picker().on('click', this.onClick, this);
18105         
18106         this.picker().addClass('datepicker-dropdown');
18107     
18108         this.fillTime();
18109         this.update();
18110             
18111         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18112         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18113         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18114         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18115         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18116         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18117
18118     },
18119     
18120     fireKey: function(e){
18121         if (!this.picker().isVisible()){
18122             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18123                 this.show();
18124             }
18125             return;
18126         }
18127
18128         e.preventDefault();
18129         
18130         switch(e.keyCode){
18131             case 27: // escape
18132                 this.hide();
18133                 break;
18134             case 37: // left
18135             case 39: // right
18136                 this.onTogglePeriod();
18137                 break;
18138             case 38: // up
18139                 this.onIncrementMinutes();
18140                 break;
18141             case 40: // down
18142                 this.onDecrementMinutes();
18143                 break;
18144             case 13: // enter
18145             case 9: // tab
18146                 this.setTime();
18147                 break;
18148         }
18149     },
18150     
18151     onClick: function(e) {
18152         e.stopPropagation();
18153         e.preventDefault();
18154     },
18155     
18156     picker : function()
18157     {
18158         return this.el.select('.datepicker', true).first();
18159     },
18160     
18161     fillTime: function()
18162     {    
18163         var time = this.pop.select('tbody', true).first();
18164         
18165         time.dom.innerHTML = '';
18166         
18167         time.createChild({
18168             tag: 'tr',
18169             cn: [
18170                 {
18171                     tag: 'td',
18172                     cn: [
18173                         {
18174                             tag: 'a',
18175                             href: '#',
18176                             cls: 'btn',
18177                             cn: [
18178                                 {
18179                                     tag: 'span',
18180                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18181                                 }
18182                             ]
18183                         } 
18184                     ]
18185                 },
18186                 {
18187                     tag: 'td',
18188                     cls: 'separator'
18189                 },
18190                 {
18191                     tag: 'td',
18192                     cn: [
18193                         {
18194                             tag: 'a',
18195                             href: '#',
18196                             cls: 'btn',
18197                             cn: [
18198                                 {
18199                                     tag: 'span',
18200                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18201                                 }
18202                             ]
18203                         }
18204                     ]
18205                 },
18206                 {
18207                     tag: 'td',
18208                     cls: 'separator'
18209                 }
18210             ]
18211         });
18212         
18213         time.createChild({
18214             tag: 'tr',
18215             cn: [
18216                 {
18217                     tag: 'td',
18218                     cn: [
18219                         {
18220                             tag: 'span',
18221                             cls: 'timepicker-hour',
18222                             html: '00'
18223                         }  
18224                     ]
18225                 },
18226                 {
18227                     tag: 'td',
18228                     cls: 'separator',
18229                     html: ':'
18230                 },
18231                 {
18232                     tag: 'td',
18233                     cn: [
18234                         {
18235                             tag: 'span',
18236                             cls: 'timepicker-minute',
18237                             html: '00'
18238                         }  
18239                     ]
18240                 },
18241                 {
18242                     tag: 'td',
18243                     cls: 'separator'
18244                 },
18245                 {
18246                     tag: 'td',
18247                     cn: [
18248                         {
18249                             tag: 'button',
18250                             type: 'button',
18251                             cls: 'btn btn-primary period',
18252                             html: 'AM'
18253                             
18254                         }
18255                     ]
18256                 }
18257             ]
18258         });
18259         
18260         time.createChild({
18261             tag: 'tr',
18262             cn: [
18263                 {
18264                     tag: 'td',
18265                     cn: [
18266                         {
18267                             tag: 'a',
18268                             href: '#',
18269                             cls: 'btn',
18270                             cn: [
18271                                 {
18272                                     tag: 'span',
18273                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18274                                 }
18275                             ]
18276                         }
18277                     ]
18278                 },
18279                 {
18280                     tag: 'td',
18281                     cls: 'separator'
18282                 },
18283                 {
18284                     tag: 'td',
18285                     cn: [
18286                         {
18287                             tag: 'a',
18288                             href: '#',
18289                             cls: 'btn',
18290                             cn: [
18291                                 {
18292                                     tag: 'span',
18293                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18294                                 }
18295                             ]
18296                         }
18297                     ]
18298                 },
18299                 {
18300                     tag: 'td',
18301                     cls: 'separator'
18302                 }
18303             ]
18304         });
18305         
18306     },
18307     
18308     update: function()
18309     {
18310         
18311         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18312         
18313         this.fill();
18314     },
18315     
18316     fill: function() 
18317     {
18318         var hours = this.time.getHours();
18319         var minutes = this.time.getMinutes();
18320         var period = 'AM';
18321         
18322         if(hours > 11){
18323             period = 'PM';
18324         }
18325         
18326         if(hours == 0){
18327             hours = 12;
18328         }
18329         
18330         
18331         if(hours > 12){
18332             hours = hours - 12;
18333         }
18334         
18335         if(hours < 10){
18336             hours = '0' + hours;
18337         }
18338         
18339         if(minutes < 10){
18340             minutes = '0' + minutes;
18341         }
18342         
18343         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18344         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18345         this.pop.select('button', true).first().dom.innerHTML = period;
18346         
18347     },
18348     
18349     place: function()
18350     {   
18351         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18352         
18353         var cls = ['bottom'];
18354         
18355         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18356             cls.pop();
18357             cls.push('top');
18358         }
18359         
18360         cls.push('right');
18361         
18362         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18363             cls.pop();
18364             cls.push('left');
18365         }
18366         
18367         this.picker().addClass(cls.join('-'));
18368         
18369         var _this = this;
18370         
18371         Roo.each(cls, function(c){
18372             if(c == 'bottom'){
18373                 _this.picker().setTop(_this.inputEl().getHeight());
18374                 return;
18375             }
18376             if(c == 'top'){
18377                 _this.picker().setTop(0 - _this.picker().getHeight());
18378                 return;
18379             }
18380             
18381             if(c == 'left'){
18382                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18383                 return;
18384             }
18385             if(c == 'right'){
18386                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18387                 return;
18388             }
18389         });
18390         
18391     },
18392   
18393     onFocus : function()
18394     {
18395         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18396         this.show();
18397     },
18398     
18399     onBlur : function()
18400     {
18401         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18402         this.hide();
18403     },
18404     
18405     show : function()
18406     {
18407         this.picker().show();
18408         this.pop.show();
18409         this.update();
18410         this.place();
18411         
18412         this.fireEvent('show', this, this.date);
18413     },
18414     
18415     hide : function()
18416     {
18417         this.picker().hide();
18418         this.pop.hide();
18419         
18420         this.fireEvent('hide', this, this.date);
18421     },
18422     
18423     setTime : function()
18424     {
18425         this.hide();
18426         this.setValue(this.time.format(this.format));
18427         
18428         this.fireEvent('select', this, this.date);
18429         
18430         
18431     },
18432     
18433     onMousedown: function(e){
18434         e.stopPropagation();
18435         e.preventDefault();
18436     },
18437     
18438     onIncrementHours: function()
18439     {
18440         Roo.log('onIncrementHours');
18441         this.time = this.time.add(Date.HOUR, 1);
18442         this.update();
18443         
18444     },
18445     
18446     onDecrementHours: function()
18447     {
18448         Roo.log('onDecrementHours');
18449         this.time = this.time.add(Date.HOUR, -1);
18450         this.update();
18451     },
18452     
18453     onIncrementMinutes: function()
18454     {
18455         Roo.log('onIncrementMinutes');
18456         this.time = this.time.add(Date.MINUTE, 1);
18457         this.update();
18458     },
18459     
18460     onDecrementMinutes: function()
18461     {
18462         Roo.log('onDecrementMinutes');
18463         this.time = this.time.add(Date.MINUTE, -1);
18464         this.update();
18465     },
18466     
18467     onTogglePeriod: function()
18468     {
18469         Roo.log('onTogglePeriod');
18470         this.time = this.time.add(Date.HOUR, 12);
18471         this.update();
18472     }
18473     
18474    
18475 });
18476
18477 Roo.apply(Roo.bootstrap.TimeField,  {
18478     
18479     content : {
18480         tag: 'tbody',
18481         cn: [
18482             {
18483                 tag: 'tr',
18484                 cn: [
18485                 {
18486                     tag: 'td',
18487                     colspan: '7'
18488                 }
18489                 ]
18490             }
18491         ]
18492     },
18493     
18494     footer : {
18495         tag: 'tfoot',
18496         cn: [
18497             {
18498                 tag: 'tr',
18499                 cn: [
18500                 {
18501                     tag: 'th',
18502                     colspan: '7',
18503                     cls: '',
18504                     cn: [
18505                         {
18506                             tag: 'button',
18507                             cls: 'btn btn-info ok',
18508                             html: 'OK'
18509                         }
18510                     ]
18511                 }
18512
18513                 ]
18514             }
18515         ]
18516     }
18517 });
18518
18519 Roo.apply(Roo.bootstrap.TimeField,  {
18520   
18521     template : {
18522         tag: 'div',
18523         cls: 'datepicker dropdown-menu',
18524         cn: [
18525             {
18526                 tag: 'div',
18527                 cls: 'datepicker-time',
18528                 cn: [
18529                 {
18530                     tag: 'table',
18531                     cls: 'table-condensed',
18532                     cn:[
18533                     Roo.bootstrap.TimeField.content,
18534                     Roo.bootstrap.TimeField.footer
18535                     ]
18536                 }
18537                 ]
18538             }
18539         ]
18540     }
18541 });
18542
18543  
18544
18545  /*
18546  * - LGPL
18547  *
18548  * MonthField
18549  * 
18550  */
18551
18552 /**
18553  * @class Roo.bootstrap.MonthField
18554  * @extends Roo.bootstrap.Input
18555  * Bootstrap MonthField class
18556  * 
18557  * @cfg {String} language default en
18558  * 
18559  * @constructor
18560  * Create a new MonthField
18561  * @param {Object} config The config object
18562  */
18563
18564 Roo.bootstrap.MonthField = function(config){
18565     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18566     
18567     this.addEvents({
18568         /**
18569          * @event show
18570          * Fires when this field show.
18571          * @param {Roo.bootstrap.MonthField} this
18572          * @param {Mixed} date The date value
18573          */
18574         show : true,
18575         /**
18576          * @event show
18577          * Fires when this field hide.
18578          * @param {Roo.bootstrap.MonthField} this
18579          * @param {Mixed} date The date value
18580          */
18581         hide : true,
18582         /**
18583          * @event select
18584          * Fires when select a date.
18585          * @param {Roo.bootstrap.MonthField} this
18586          * @param {String} oldvalue The old value
18587          * @param {String} newvalue The new value
18588          */
18589         select : true
18590     });
18591 };
18592
18593 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18594     
18595     onRender: function(ct, position)
18596     {
18597         
18598         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18599         
18600         this.language = this.language || 'en';
18601         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18602         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18603         
18604         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18605         this.isInline = false;
18606         this.isInput = true;
18607         this.component = this.el.select('.add-on', true).first() || false;
18608         this.component = (this.component && this.component.length === 0) ? false : this.component;
18609         this.hasInput = this.component && this.inputEL().length;
18610         
18611         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18612         
18613         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18614         
18615         this.picker().on('mousedown', this.onMousedown, this);
18616         this.picker().on('click', this.onClick, this);
18617         
18618         this.picker().addClass('datepicker-dropdown');
18619         
18620         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18621             v.setStyle('width', '189px');
18622         });
18623         
18624         this.fillMonths();
18625         
18626         this.update();
18627         
18628         if(this.isInline) {
18629             this.show();
18630         }
18631         
18632     },
18633     
18634     setValue: function(v, suppressEvent)
18635     {   
18636         var o = this.getValue();
18637         
18638         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18639         
18640         this.update();
18641
18642         if(suppressEvent !== true){
18643             this.fireEvent('select', this, o, v);
18644         }
18645         
18646     },
18647     
18648     getValue: function()
18649     {
18650         return this.value;
18651     },
18652     
18653     onClick: function(e) 
18654     {
18655         e.stopPropagation();
18656         e.preventDefault();
18657         
18658         var target = e.getTarget();
18659         
18660         if(target.nodeName.toLowerCase() === 'i'){
18661             target = Roo.get(target).dom.parentNode;
18662         }
18663         
18664         var nodeName = target.nodeName;
18665         var className = target.className;
18666         var html = target.innerHTML;
18667         
18668         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18669             return;
18670         }
18671         
18672         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18673         
18674         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18675         
18676         this.hide();
18677                         
18678     },
18679     
18680     picker : function()
18681     {
18682         return this.pickerEl;
18683     },
18684     
18685     fillMonths: function()
18686     {    
18687         var i = 0;
18688         var months = this.picker().select('>.datepicker-months td', true).first();
18689         
18690         months.dom.innerHTML = '';
18691         
18692         while (i < 12) {
18693             var month = {
18694                 tag: 'span',
18695                 cls: 'month',
18696                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18697             };
18698             
18699             months.createChild(month);
18700         }
18701         
18702     },
18703     
18704     update: function()
18705     {
18706         var _this = this;
18707         
18708         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18709             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18710         }
18711         
18712         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18713             e.removeClass('active');
18714             
18715             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18716                 e.addClass('active');
18717             }
18718         })
18719     },
18720     
18721     place: function()
18722     {
18723         if(this.isInline) {
18724             return;
18725         }
18726         
18727         this.picker().removeClass(['bottom', 'top']);
18728         
18729         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18730             /*
18731              * place to the top of element!
18732              *
18733              */
18734             
18735             this.picker().addClass('top');
18736             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18737             
18738             return;
18739         }
18740         
18741         this.picker().addClass('bottom');
18742         
18743         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18744     },
18745     
18746     onFocus : function()
18747     {
18748         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18749         this.show();
18750     },
18751     
18752     onBlur : function()
18753     {
18754         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18755         
18756         var d = this.inputEl().getValue();
18757         
18758         this.setValue(d);
18759                 
18760         this.hide();
18761     },
18762     
18763     show : function()
18764     {
18765         this.picker().show();
18766         this.picker().select('>.datepicker-months', true).first().show();
18767         this.update();
18768         this.place();
18769         
18770         this.fireEvent('show', this, this.date);
18771     },
18772     
18773     hide : function()
18774     {
18775         if(this.isInline) {
18776             return;
18777         }
18778         this.picker().hide();
18779         this.fireEvent('hide', this, this.date);
18780         
18781     },
18782     
18783     onMousedown: function(e)
18784     {
18785         e.stopPropagation();
18786         e.preventDefault();
18787     },
18788     
18789     keyup: function(e)
18790     {
18791         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18792         this.update();
18793     },
18794
18795     fireKey: function(e)
18796     {
18797         if (!this.picker().isVisible()){
18798             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18799                 this.show();
18800             }
18801             return;
18802         }
18803         
18804         var dir;
18805         
18806         switch(e.keyCode){
18807             case 27: // escape
18808                 this.hide();
18809                 e.preventDefault();
18810                 break;
18811             case 37: // left
18812             case 39: // right
18813                 dir = e.keyCode == 37 ? -1 : 1;
18814                 
18815                 this.vIndex = this.vIndex + dir;
18816                 
18817                 if(this.vIndex < 0){
18818                     this.vIndex = 0;
18819                 }
18820                 
18821                 if(this.vIndex > 11){
18822                     this.vIndex = 11;
18823                 }
18824                 
18825                 if(isNaN(this.vIndex)){
18826                     this.vIndex = 0;
18827                 }
18828                 
18829                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18830                 
18831                 break;
18832             case 38: // up
18833             case 40: // down
18834                 
18835                 dir = e.keyCode == 38 ? -1 : 1;
18836                 
18837                 this.vIndex = this.vIndex + dir * 4;
18838                 
18839                 if(this.vIndex < 0){
18840                     this.vIndex = 0;
18841                 }
18842                 
18843                 if(this.vIndex > 11){
18844                     this.vIndex = 11;
18845                 }
18846                 
18847                 if(isNaN(this.vIndex)){
18848                     this.vIndex = 0;
18849                 }
18850                 
18851                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18852                 break;
18853                 
18854             case 13: // enter
18855                 
18856                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18857                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18858                 }
18859                 
18860                 this.hide();
18861                 e.preventDefault();
18862                 break;
18863             case 9: // tab
18864                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18865                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18866                 }
18867                 this.hide();
18868                 break;
18869             case 16: // shift
18870             case 17: // ctrl
18871             case 18: // alt
18872                 break;
18873             default :
18874                 this.hide();
18875                 
18876         }
18877     },
18878     
18879     remove: function() 
18880     {
18881         this.picker().remove();
18882     }
18883    
18884 });
18885
18886 Roo.apply(Roo.bootstrap.MonthField,  {
18887     
18888     content : {
18889         tag: 'tbody',
18890         cn: [
18891         {
18892             tag: 'tr',
18893             cn: [
18894             {
18895                 tag: 'td',
18896                 colspan: '7'
18897             }
18898             ]
18899         }
18900         ]
18901     },
18902     
18903     dates:{
18904         en: {
18905             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18906             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18907         }
18908     }
18909 });
18910
18911 Roo.apply(Roo.bootstrap.MonthField,  {
18912   
18913     template : {
18914         tag: 'div',
18915         cls: 'datepicker dropdown-menu roo-dynamic',
18916         cn: [
18917             {
18918                 tag: 'div',
18919                 cls: 'datepicker-months',
18920                 cn: [
18921                 {
18922                     tag: 'table',
18923                     cls: 'table-condensed',
18924                     cn:[
18925                         Roo.bootstrap.DateField.content
18926                     ]
18927                 }
18928                 ]
18929             }
18930         ]
18931     }
18932 });
18933
18934  
18935
18936  
18937  /*
18938  * - LGPL
18939  *
18940  * CheckBox
18941  * 
18942  */
18943
18944 /**
18945  * @class Roo.bootstrap.CheckBox
18946  * @extends Roo.bootstrap.Input
18947  * Bootstrap CheckBox class
18948  * 
18949  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18950  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18951  * @cfg {String} boxLabel The text that appears beside the checkbox
18952  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18953  * @cfg {Boolean} checked initnal the element
18954  * @cfg {Boolean} inline inline the element (default false)
18955  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18956  * 
18957  * @constructor
18958  * Create a new CheckBox
18959  * @param {Object} config The config object
18960  */
18961
18962 Roo.bootstrap.CheckBox = function(config){
18963     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18964    
18965     this.addEvents({
18966         /**
18967         * @event check
18968         * Fires when the element is checked or unchecked.
18969         * @param {Roo.bootstrap.CheckBox} this This input
18970         * @param {Boolean} checked The new checked value
18971         */
18972        check : true
18973     });
18974     
18975 };
18976
18977 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18978   
18979     inputType: 'checkbox',
18980     inputValue: 1,
18981     valueOff: 0,
18982     boxLabel: false,
18983     checked: false,
18984     weight : false,
18985     inline: false,
18986     
18987     getAutoCreate : function()
18988     {
18989         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18990         
18991         var id = Roo.id();
18992         
18993         var cfg = {};
18994         
18995         cfg.cls = 'form-group ' + this.inputType; //input-group
18996         
18997         if(this.inline){
18998             cfg.cls += ' ' + this.inputType + '-inline';
18999         }
19000         
19001         var input =  {
19002             tag: 'input',
19003             id : id,
19004             type : this.inputType,
19005             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19006             cls : 'roo-' + this.inputType, //'form-box',
19007             placeholder : this.placeholder || ''
19008             
19009         };
19010         
19011         if (this.weight) { // Validity check?
19012             cfg.cls += " " + this.inputType + "-" + this.weight;
19013         }
19014         
19015         if (this.disabled) {
19016             input.disabled=true;
19017         }
19018         
19019         if(this.checked){
19020             input.checked = this.checked;
19021         }
19022         
19023         if (this.name) {
19024             input.name = this.name;
19025         }
19026         
19027         if (this.size) {
19028             input.cls += ' input-' + this.size;
19029         }
19030         
19031         var settings=this;
19032         
19033         ['xs','sm','md','lg'].map(function(size){
19034             if (settings[size]) {
19035                 cfg.cls += ' col-' + size + '-' + settings[size];
19036             }
19037         });
19038         
19039         var inputblock = input;
19040          
19041         if (this.before || this.after) {
19042             
19043             inputblock = {
19044                 cls : 'input-group',
19045                 cn :  [] 
19046             };
19047             
19048             if (this.before) {
19049                 inputblock.cn.push({
19050                     tag :'span',
19051                     cls : 'input-group-addon',
19052                     html : this.before
19053                 });
19054             }
19055             
19056             inputblock.cn.push(input);
19057             
19058             if (this.after) {
19059                 inputblock.cn.push({
19060                     tag :'span',
19061                     cls : 'input-group-addon',
19062                     html : this.after
19063                 });
19064             }
19065             
19066         }
19067         
19068         if (align ==='left' && this.fieldLabel.length) {
19069 //                Roo.log("left and has label");
19070                 cfg.cn = [
19071                     
19072                     {
19073                         tag: 'label',
19074                         'for' :  id,
19075                         cls : 'control-label col-md-' + this.labelWidth,
19076                         html : this.fieldLabel
19077                         
19078                     },
19079                     {
19080                         cls : "col-md-" + (12 - this.labelWidth), 
19081                         cn: [
19082                             inputblock
19083                         ]
19084                     }
19085                     
19086                 ];
19087         } else if ( this.fieldLabel.length) {
19088 //                Roo.log(" label");
19089                 cfg.cn = [
19090                    
19091                     {
19092                         tag: this.boxLabel ? 'span' : 'label',
19093                         'for': id,
19094                         cls: 'control-label box-input-label',
19095                         //cls : 'input-group-addon',
19096                         html : this.fieldLabel
19097                         
19098                     },
19099                     
19100                     inputblock
19101                     
19102                 ];
19103
19104         } else {
19105             
19106 //                Roo.log(" no label && no align");
19107                 cfg.cn = [  inputblock ] ;
19108                 
19109                 
19110         }
19111         
19112         if(this.boxLabel){
19113              var boxLabelCfg = {
19114                 tag: 'label',
19115                 //'for': id, // box label is handled by onclick - so no for...
19116                 cls: 'box-label',
19117                 html: this.boxLabel
19118             };
19119             
19120             if(this.tooltip){
19121                 boxLabelCfg.tooltip = this.tooltip;
19122             }
19123              
19124             cfg.cn.push(boxLabelCfg);
19125         }
19126         
19127         
19128        
19129         return cfg;
19130         
19131     },
19132     
19133     /**
19134      * return the real input element.
19135      */
19136     inputEl: function ()
19137     {
19138         return this.el.select('input.roo-' + this.inputType,true).first();
19139     },
19140     
19141     labelEl: function()
19142     {
19143         return this.el.select('label.control-label',true).first();
19144     },
19145     /* depricated... */
19146     
19147     label: function()
19148     {
19149         return this.labelEl();
19150     },
19151     
19152     boxLabelEl: function()
19153     {
19154         return this.el.select('label.box-label',true).first();
19155     },
19156     
19157     initEvents : function()
19158     {
19159 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19160         
19161         this.inputEl().on('click', this.onClick,  this);
19162         
19163         if (this.boxLabel) { 
19164             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19165         }
19166         
19167         this.startValue = this.getValue();
19168         
19169         if(this.groupId){
19170             Roo.bootstrap.CheckBox.register(this);
19171         }
19172     },
19173     
19174     onClick : function()
19175     {   
19176         this.setChecked(!this.checked);
19177     },
19178     
19179     setChecked : function(state,suppressEvent)
19180     {
19181         this.startValue = this.getValue();
19182         
19183         if(this.inputType == 'radio'){
19184             
19185             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19186                 e.dom.checked = false;
19187             });
19188             
19189             this.inputEl().dom.checked = true;
19190             
19191             this.inputEl().dom.value = this.inputValue;
19192             
19193             if(suppressEvent !== true){
19194                 this.fireEvent('check', this, true);
19195             }
19196             
19197             this.validate();
19198             
19199             return;
19200         }
19201         
19202         this.checked = state;
19203         
19204         this.inputEl().dom.checked = state;
19205         
19206         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19207         
19208         if(suppressEvent !== true){
19209             this.fireEvent('check', this, state);
19210         }
19211         
19212         this.validate();
19213     },
19214     
19215     getValue : function()
19216     {
19217         if(this.inputType == 'radio'){
19218             return this.getGroupValue();
19219         }
19220         
19221         return this.inputEl().getValue();
19222         
19223     },
19224     
19225     getGroupValue : function()
19226     {
19227         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19228             return '';
19229         }
19230         
19231         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19232     },
19233     
19234     setValue : function(v,suppressEvent)
19235     {
19236         if(this.inputType == 'radio'){
19237             this.setGroupValue(v, suppressEvent);
19238             return;
19239         }
19240         
19241         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19242         
19243         this.validate();
19244     },
19245     
19246     setGroupValue : function(v, suppressEvent)
19247     {
19248         this.startValue = this.getValue();
19249         
19250         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19251             e.dom.checked = false;
19252             
19253             if(e.dom.value == v){
19254                 e.dom.checked = true;
19255             }
19256         });
19257         
19258         if(suppressEvent !== true){
19259             this.fireEvent('check', this, true);
19260         }
19261
19262         this.validate();
19263         
19264         return;
19265     },
19266     
19267     validate : function()
19268     {
19269         if(
19270                 this.disabled || 
19271                 (this.inputType == 'radio' && this.validateRadio()) ||
19272                 (this.inputType == 'checkbox' && this.validateCheckbox())
19273         ){
19274             this.markValid();
19275             return true;
19276         }
19277         
19278         this.markInvalid();
19279         return false;
19280     },
19281     
19282     validateRadio : function()
19283     {
19284         var valid = false;
19285         
19286         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19287             if(!e.dom.checked){
19288                 return;
19289             }
19290             
19291             valid = true;
19292             
19293             return false;
19294         });
19295         
19296         return valid;
19297     },
19298     
19299     validateCheckbox : function()
19300     {
19301         if(!this.groupId){
19302             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19303         }
19304         
19305         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19306         
19307         if(!group){
19308             return false;
19309         }
19310         
19311         var r = false;
19312         
19313         for(var i in group){
19314             if(r){
19315                 break;
19316             }
19317             
19318             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19319         }
19320         
19321         return r;
19322     },
19323     
19324     /**
19325      * Mark this field as valid
19326      */
19327     markValid : function()
19328     {
19329         if(this.allowBlank){
19330             return;
19331         }
19332         
19333         var _this = this;
19334         
19335         this.fireEvent('valid', this);
19336         
19337         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19338         
19339         if(this.groupId){
19340             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19341         }
19342         
19343         if(label){
19344             label.markValid();
19345         }
19346         
19347         if(this.inputType == 'radio'){
19348             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19349                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19350                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19351             });
19352             
19353             return;
19354         }
19355         
19356         if(!this.groupId){
19357             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19358             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19359             return;
19360         }
19361         
19362         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19363             
19364         if(!group){
19365             return;
19366         }
19367         
19368         for(var i in group){
19369             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19370             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19371         }
19372     },
19373     
19374      /**
19375      * Mark this field as invalid
19376      * @param {String} msg The validation message
19377      */
19378     markInvalid : function(msg)
19379     {
19380         if(this.allowBlank){
19381             return;
19382         }
19383         
19384         var _this = this;
19385         
19386         this.fireEvent('invalid', this, msg);
19387         
19388         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19389         
19390         if(this.groupId){
19391             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19392         }
19393         
19394         if(label){
19395             label.markInvalid();
19396         }
19397             
19398         if(this.inputType == 'radio'){
19399             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19400                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19401                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19402             });
19403             
19404             return;
19405         }
19406         
19407         if(!this.groupId){
19408             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19409             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19410             return;
19411         }
19412         
19413         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19414         
19415         if(!group){
19416             return;
19417         }
19418         
19419         for(var i in group){
19420             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19421             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19422         }
19423         
19424     }
19425     
19426 });
19427
19428 Roo.apply(Roo.bootstrap.CheckBox, {
19429     
19430     groups: {},
19431     
19432      /**
19433     * register a CheckBox Group
19434     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19435     */
19436     register : function(checkbox)
19437     {
19438         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19439             this.groups[checkbox.groupId] = {};
19440         }
19441         
19442         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19443             return;
19444         }
19445         
19446         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19447         
19448     },
19449     /**
19450     * fetch a CheckBox Group based on the group ID
19451     * @param {string} the group ID
19452     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19453     */
19454     get: function(groupId) {
19455         if (typeof(this.groups[groupId]) == 'undefined') {
19456             return false;
19457         }
19458         
19459         return this.groups[groupId] ;
19460     }
19461     
19462     
19463 });
19464 /*
19465  * - LGPL
19466  *
19467  * Radio
19468  *
19469  *
19470  * not inline
19471  *<div class="radio">
19472   <label>
19473     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19474     Option one is this and that&mdash;be sure to include why it's great
19475   </label>
19476 </div>
19477  *
19478  *
19479  *inline
19480  *<span>
19481  *<label class="radio-inline">fieldLabel</label>
19482  *<label class="radio-inline">
19483   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19484 </label>
19485 <span>
19486  * 
19487  * 
19488  */
19489
19490 /**
19491  * @class Roo.bootstrap.Radio
19492  * @extends Roo.bootstrap.CheckBox
19493  * Bootstrap Radio class
19494
19495  * @constructor
19496  * Create a new Radio
19497  * @param {Object} config The config object
19498  */
19499
19500 Roo.bootstrap.Radio = function(config){
19501     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19502    
19503 };
19504
19505 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19506     
19507     inputType: 'radio',
19508     inputValue: '',
19509     valueOff: '',
19510     
19511     getAutoCreate : function()
19512     {
19513         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19514         align = align || 'left'; // default...
19515         
19516         
19517         
19518         var id = Roo.id();
19519         
19520         var cfg = {
19521                 tag : this.inline ? 'span' : 'div',
19522                 cls : '',
19523                 cn : []
19524         };
19525         
19526         var inline = this.inline ? ' radio-inline' : '';
19527         
19528         var lbl = {
19529                 tag: 'label' ,
19530                 // does not need for, as we wrap the input with it..
19531                 'for' : id,
19532                 cls : 'control-label box-label' + inline,
19533                 cn : []
19534         };
19535         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19536         
19537         var fieldLabel = {
19538             tag: 'label' ,
19539             //cls : 'control-label' + inline,
19540             html : this.fieldLabel,
19541             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19542         };
19543         
19544  
19545         
19546         
19547         var input =  {
19548             tag: 'input',
19549             id : id,
19550             type : this.inputType,
19551             //value : (!this.checked) ? this.valueOff : this.inputValue,
19552             value : this.inputValue,
19553             cls : 'roo-radio',
19554             placeholder : this.placeholder || '' // ?? needed????
19555             
19556         };
19557         if (this.weight) { // Validity check?
19558             input.cls += " radio-" + this.weight;
19559         }
19560         if (this.disabled) {
19561             input.disabled=true;
19562         }
19563         
19564         if(this.checked){
19565             input.checked = this.checked;
19566         }
19567         
19568         if (this.name) {
19569             input.name = this.name;
19570         }
19571         
19572         if (this.size) {
19573             input.cls += ' input-' + this.size;
19574         }
19575         
19576         //?? can span's inline have a width??
19577         
19578         var settings=this;
19579         ['xs','sm','md','lg'].map(function(size){
19580             if (settings[size]) {
19581                 cfg.cls += ' col-' + size + '-' + settings[size];
19582             }
19583         });
19584         
19585         var inputblock = input;
19586         
19587         if (this.before || this.after) {
19588             
19589             inputblock = {
19590                 cls : 'input-group',
19591                 tag : 'span',
19592                 cn :  [] 
19593             };
19594             if (this.before) {
19595                 inputblock.cn.push({
19596                     tag :'span',
19597                     cls : 'input-group-addon',
19598                     html : this.before
19599                 });
19600             }
19601             inputblock.cn.push(input);
19602             if (this.after) {
19603                 inputblock.cn.push({
19604                     tag :'span',
19605                     cls : 'input-group-addon',
19606                     html : this.after
19607                 });
19608             }
19609             
19610         };
19611         
19612         
19613         if (this.fieldLabel && this.fieldLabel.length) {
19614             cfg.cn.push(fieldLabel);
19615         }
19616        
19617         // normal bootstrap puts the input inside the label.
19618         // however with our styled version - it has to go after the input.
19619        
19620         //lbl.cn.push(inputblock);
19621         
19622         var lblwrap =  {
19623             tag: 'span',
19624             cls: 'radio' + inline,
19625             cn: [
19626                 inputblock,
19627                 lbl
19628             ]
19629         };
19630         
19631         cfg.cn.push( lblwrap);
19632         
19633         if(this.boxLabel){
19634             lbl.cn.push({
19635                 tag: 'span',
19636                 html: this.boxLabel
19637             })
19638         }
19639          
19640         
19641         return cfg;
19642         
19643     },
19644     
19645     initEvents : function()
19646     {
19647 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19648         
19649         this.inputEl().on('click', this.onClick,  this);
19650         if (this.boxLabel) {
19651             //Roo.log('find label');
19652             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19653         }
19654         
19655     },
19656     
19657     inputEl: function ()
19658     {
19659         return this.el.select('input.roo-radio',true).first();
19660     },
19661     onClick : function()
19662     {   
19663         Roo.log("click");
19664         this.setChecked(true);
19665     },
19666     
19667     setChecked : function(state,suppressEvent)
19668     {
19669         if(state){
19670             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19671                 v.dom.checked = false;
19672             });
19673         }
19674         Roo.log(this.inputEl().dom);
19675         this.checked = state;
19676         this.inputEl().dom.checked = state;
19677         
19678         if(suppressEvent !== true){
19679             this.fireEvent('check', this, state);
19680         }
19681         
19682         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19683         
19684     },
19685     
19686     getGroupValue : function()
19687     {
19688         var value = '';
19689         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19690             if(v.dom.checked == true){
19691                 value = v.dom.value;
19692             }
19693         });
19694         
19695         return value;
19696     },
19697     
19698     /**
19699      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19700      * @return {Mixed} value The field value
19701      */
19702     getValue : function(){
19703         return this.getGroupValue();
19704     }
19705     
19706 });
19707
19708  
19709 //<script type="text/javascript">
19710
19711 /*
19712  * Based  Ext JS Library 1.1.1
19713  * Copyright(c) 2006-2007, Ext JS, LLC.
19714  * LGPL
19715  *
19716  */
19717  
19718 /**
19719  * @class Roo.HtmlEditorCore
19720  * @extends Roo.Component
19721  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19722  *
19723  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19724  */
19725
19726 Roo.HtmlEditorCore = function(config){
19727     
19728     
19729     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19730     
19731     
19732     this.addEvents({
19733         /**
19734          * @event initialize
19735          * Fires when the editor is fully initialized (including the iframe)
19736          * @param {Roo.HtmlEditorCore} this
19737          */
19738         initialize: true,
19739         /**
19740          * @event activate
19741          * Fires when the editor is first receives the focus. Any insertion must wait
19742          * until after this event.
19743          * @param {Roo.HtmlEditorCore} this
19744          */
19745         activate: true,
19746          /**
19747          * @event beforesync
19748          * Fires before the textarea is updated with content from the editor iframe. Return false
19749          * to cancel the sync.
19750          * @param {Roo.HtmlEditorCore} this
19751          * @param {String} html
19752          */
19753         beforesync: true,
19754          /**
19755          * @event beforepush
19756          * Fires before the iframe editor is updated with content from the textarea. Return false
19757          * to cancel the push.
19758          * @param {Roo.HtmlEditorCore} this
19759          * @param {String} html
19760          */
19761         beforepush: true,
19762          /**
19763          * @event sync
19764          * Fires when the textarea is updated with content from the editor iframe.
19765          * @param {Roo.HtmlEditorCore} this
19766          * @param {String} html
19767          */
19768         sync: true,
19769          /**
19770          * @event push
19771          * Fires when the iframe editor is updated with content from the textarea.
19772          * @param {Roo.HtmlEditorCore} this
19773          * @param {String} html
19774          */
19775         push: true,
19776         
19777         /**
19778          * @event editorevent
19779          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19780          * @param {Roo.HtmlEditorCore} this
19781          */
19782         editorevent: true
19783         
19784     });
19785     
19786     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19787     
19788     // defaults : white / black...
19789     this.applyBlacklists();
19790     
19791     
19792     
19793 };
19794
19795
19796 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19797
19798
19799      /**
19800      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19801      */
19802     
19803     owner : false,
19804     
19805      /**
19806      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19807      *                        Roo.resizable.
19808      */
19809     resizable : false,
19810      /**
19811      * @cfg {Number} height (in pixels)
19812      */   
19813     height: 300,
19814    /**
19815      * @cfg {Number} width (in pixels)
19816      */   
19817     width: 500,
19818     
19819     /**
19820      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19821      * 
19822      */
19823     stylesheets: false,
19824     
19825     // id of frame..
19826     frameId: false,
19827     
19828     // private properties
19829     validationEvent : false,
19830     deferHeight: true,
19831     initialized : false,
19832     activated : false,
19833     sourceEditMode : false,
19834     onFocus : Roo.emptyFn,
19835     iframePad:3,
19836     hideMode:'offsets',
19837     
19838     clearUp: true,
19839     
19840     // blacklist + whitelisted elements..
19841     black: false,
19842     white: false,
19843      
19844     
19845
19846     /**
19847      * Protected method that will not generally be called directly. It
19848      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19849      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19850      */
19851     getDocMarkup : function(){
19852         // body styles..
19853         var st = '';
19854         
19855         // inherit styels from page...?? 
19856         if (this.stylesheets === false) {
19857             
19858             Roo.get(document.head).select('style').each(function(node) {
19859                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19860             });
19861             
19862             Roo.get(document.head).select('link').each(function(node) { 
19863                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19864             });
19865             
19866         } else if (!this.stylesheets.length) {
19867                 // simple..
19868                 st = '<style type="text/css">' +
19869                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19870                    '</style>';
19871         } else { 
19872             
19873         }
19874         
19875         st +=  '<style type="text/css">' +
19876             'IMG { cursor: pointer } ' +
19877         '</style>';
19878
19879         
19880         return '<html><head>' + st  +
19881             //<style type="text/css">' +
19882             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19883             //'</style>' +
19884             ' </head><body class="roo-htmleditor-body"></body></html>';
19885     },
19886
19887     // private
19888     onRender : function(ct, position)
19889     {
19890         var _t = this;
19891         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19892         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19893         
19894         
19895         this.el.dom.style.border = '0 none';
19896         this.el.dom.setAttribute('tabIndex', -1);
19897         this.el.addClass('x-hidden hide');
19898         
19899         
19900         
19901         if(Roo.isIE){ // fix IE 1px bogus margin
19902             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19903         }
19904        
19905         
19906         this.frameId = Roo.id();
19907         
19908          
19909         
19910         var iframe = this.owner.wrap.createChild({
19911             tag: 'iframe',
19912             cls: 'form-control', // bootstrap..
19913             id: this.frameId,
19914             name: this.frameId,
19915             frameBorder : 'no',
19916             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19917         }, this.el
19918         );
19919         
19920         
19921         this.iframe = iframe.dom;
19922
19923          this.assignDocWin();
19924         
19925         this.doc.designMode = 'on';
19926        
19927         this.doc.open();
19928         this.doc.write(this.getDocMarkup());
19929         this.doc.close();
19930
19931         
19932         var task = { // must defer to wait for browser to be ready
19933             run : function(){
19934                 //console.log("run task?" + this.doc.readyState);
19935                 this.assignDocWin();
19936                 if(this.doc.body || this.doc.readyState == 'complete'){
19937                     try {
19938                         this.doc.designMode="on";
19939                     } catch (e) {
19940                         return;
19941                     }
19942                     Roo.TaskMgr.stop(task);
19943                     this.initEditor.defer(10, this);
19944                 }
19945             },
19946             interval : 10,
19947             duration: 10000,
19948             scope: this
19949         };
19950         Roo.TaskMgr.start(task);
19951
19952     },
19953
19954     // private
19955     onResize : function(w, h)
19956     {
19957          Roo.log('resize: ' +w + ',' + h );
19958         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19959         if(!this.iframe){
19960             return;
19961         }
19962         if(typeof w == 'number'){
19963             
19964             this.iframe.style.width = w + 'px';
19965         }
19966         if(typeof h == 'number'){
19967             
19968             this.iframe.style.height = h + 'px';
19969             if(this.doc){
19970                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19971             }
19972         }
19973         
19974     },
19975
19976     /**
19977      * Toggles the editor between standard and source edit mode.
19978      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19979      */
19980     toggleSourceEdit : function(sourceEditMode){
19981         
19982         this.sourceEditMode = sourceEditMode === true;
19983         
19984         if(this.sourceEditMode){
19985  
19986             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19987             
19988         }else{
19989             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19990             //this.iframe.className = '';
19991             this.deferFocus();
19992         }
19993         //this.setSize(this.owner.wrap.getSize());
19994         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19995     },
19996
19997     
19998   
19999
20000     /**
20001      * Protected method that will not generally be called directly. If you need/want
20002      * custom HTML cleanup, this is the method you should override.
20003      * @param {String} html The HTML to be cleaned
20004      * return {String} The cleaned HTML
20005      */
20006     cleanHtml : function(html){
20007         html = String(html);
20008         if(html.length > 5){
20009             if(Roo.isSafari){ // strip safari nonsense
20010                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20011             }
20012         }
20013         if(html == '&nbsp;'){
20014             html = '';
20015         }
20016         return html;
20017     },
20018
20019     /**
20020      * HTML Editor -> Textarea
20021      * Protected method that will not generally be called directly. Syncs the contents
20022      * of the editor iframe with the textarea.
20023      */
20024     syncValue : function(){
20025         if(this.initialized){
20026             var bd = (this.doc.body || this.doc.documentElement);
20027             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20028             var html = bd.innerHTML;
20029             if(Roo.isSafari){
20030                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20031                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20032                 if(m && m[1]){
20033                     html = '<div style="'+m[0]+'">' + html + '</div>';
20034                 }
20035             }
20036             html = this.cleanHtml(html);
20037             // fix up the special chars.. normaly like back quotes in word...
20038             // however we do not want to do this with chinese..
20039             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20040                 var cc = b.charCodeAt();
20041                 if (
20042                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20043                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20044                     (cc >= 0xf900 && cc < 0xfb00 )
20045                 ) {
20046                         return b;
20047                 }
20048                 return "&#"+cc+";" 
20049             });
20050             if(this.owner.fireEvent('beforesync', this, html) !== false){
20051                 this.el.dom.value = html;
20052                 this.owner.fireEvent('sync', this, html);
20053             }
20054         }
20055     },
20056
20057     /**
20058      * Protected method that will not generally be called directly. Pushes the value of the textarea
20059      * into the iframe editor.
20060      */
20061     pushValue : function(){
20062         if(this.initialized){
20063             var v = this.el.dom.value.trim();
20064             
20065 //            if(v.length < 1){
20066 //                v = '&#160;';
20067 //            }
20068             
20069             if(this.owner.fireEvent('beforepush', this, v) !== false){
20070                 var d = (this.doc.body || this.doc.documentElement);
20071                 d.innerHTML = v;
20072                 this.cleanUpPaste();
20073                 this.el.dom.value = d.innerHTML;
20074                 this.owner.fireEvent('push', this, v);
20075             }
20076         }
20077     },
20078
20079     // private
20080     deferFocus : function(){
20081         this.focus.defer(10, this);
20082     },
20083
20084     // doc'ed in Field
20085     focus : function(){
20086         if(this.win && !this.sourceEditMode){
20087             this.win.focus();
20088         }else{
20089             this.el.focus();
20090         }
20091     },
20092     
20093     assignDocWin: function()
20094     {
20095         var iframe = this.iframe;
20096         
20097          if(Roo.isIE){
20098             this.doc = iframe.contentWindow.document;
20099             this.win = iframe.contentWindow;
20100         } else {
20101 //            if (!Roo.get(this.frameId)) {
20102 //                return;
20103 //            }
20104 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20105 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20106             
20107             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20108                 return;
20109             }
20110             
20111             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20112             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20113         }
20114     },
20115     
20116     // private
20117     initEditor : function(){
20118         //console.log("INIT EDITOR");
20119         this.assignDocWin();
20120         
20121         
20122         
20123         this.doc.designMode="on";
20124         this.doc.open();
20125         this.doc.write(this.getDocMarkup());
20126         this.doc.close();
20127         
20128         var dbody = (this.doc.body || this.doc.documentElement);
20129         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20130         // this copies styles from the containing element into thsi one..
20131         // not sure why we need all of this..
20132         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20133         
20134         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20135         //ss['background-attachment'] = 'fixed'; // w3c
20136         dbody.bgProperties = 'fixed'; // ie
20137         //Roo.DomHelper.applyStyles(dbody, ss);
20138         Roo.EventManager.on(this.doc, {
20139             //'mousedown': this.onEditorEvent,
20140             'mouseup': this.onEditorEvent,
20141             'dblclick': this.onEditorEvent,
20142             'click': this.onEditorEvent,
20143             'keyup': this.onEditorEvent,
20144             buffer:100,
20145             scope: this
20146         });
20147         if(Roo.isGecko){
20148             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20149         }
20150         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20151             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20152         }
20153         this.initialized = true;
20154
20155         this.owner.fireEvent('initialize', this);
20156         this.pushValue();
20157     },
20158
20159     // private
20160     onDestroy : function(){
20161         
20162         
20163         
20164         if(this.rendered){
20165             
20166             //for (var i =0; i < this.toolbars.length;i++) {
20167             //    // fixme - ask toolbars for heights?
20168             //    this.toolbars[i].onDestroy();
20169            // }
20170             
20171             //this.wrap.dom.innerHTML = '';
20172             //this.wrap.remove();
20173         }
20174     },
20175
20176     // private
20177     onFirstFocus : function(){
20178         
20179         this.assignDocWin();
20180         
20181         
20182         this.activated = true;
20183          
20184     
20185         if(Roo.isGecko){ // prevent silly gecko errors
20186             this.win.focus();
20187             var s = this.win.getSelection();
20188             if(!s.focusNode || s.focusNode.nodeType != 3){
20189                 var r = s.getRangeAt(0);
20190                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20191                 r.collapse(true);
20192                 this.deferFocus();
20193             }
20194             try{
20195                 this.execCmd('useCSS', true);
20196                 this.execCmd('styleWithCSS', false);
20197             }catch(e){}
20198         }
20199         this.owner.fireEvent('activate', this);
20200     },
20201
20202     // private
20203     adjustFont: function(btn){
20204         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20205         //if(Roo.isSafari){ // safari
20206         //    adjust *= 2;
20207        // }
20208         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20209         if(Roo.isSafari){ // safari
20210             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20211             v =  (v < 10) ? 10 : v;
20212             v =  (v > 48) ? 48 : v;
20213             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20214             
20215         }
20216         
20217         
20218         v = Math.max(1, v+adjust);
20219         
20220         this.execCmd('FontSize', v  );
20221     },
20222
20223     onEditorEvent : function(e)
20224     {
20225         this.owner.fireEvent('editorevent', this, e);
20226       //  this.updateToolbar();
20227         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20228     },
20229
20230     insertTag : function(tg)
20231     {
20232         // could be a bit smarter... -> wrap the current selected tRoo..
20233         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20234             
20235             range = this.createRange(this.getSelection());
20236             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20237             wrappingNode.appendChild(range.extractContents());
20238             range.insertNode(wrappingNode);
20239
20240             return;
20241             
20242             
20243             
20244         }
20245         this.execCmd("formatblock",   tg);
20246         
20247     },
20248     
20249     insertText : function(txt)
20250     {
20251         
20252         
20253         var range = this.createRange();
20254         range.deleteContents();
20255                //alert(Sender.getAttribute('label'));
20256                
20257         range.insertNode(this.doc.createTextNode(txt));
20258     } ,
20259     
20260      
20261
20262     /**
20263      * Executes a Midas editor command on the editor document and performs necessary focus and
20264      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20265      * @param {String} cmd The Midas command
20266      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20267      */
20268     relayCmd : function(cmd, value){
20269         this.win.focus();
20270         this.execCmd(cmd, value);
20271         this.owner.fireEvent('editorevent', this);
20272         //this.updateToolbar();
20273         this.owner.deferFocus();
20274     },
20275
20276     /**
20277      * Executes a Midas editor command directly on the editor document.
20278      * For visual commands, you should use {@link #relayCmd} instead.
20279      * <b>This should only be called after the editor is initialized.</b>
20280      * @param {String} cmd The Midas command
20281      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20282      */
20283     execCmd : function(cmd, value){
20284         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20285         this.syncValue();
20286     },
20287  
20288  
20289    
20290     /**
20291      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20292      * to insert tRoo.
20293      * @param {String} text | dom node.. 
20294      */
20295     insertAtCursor : function(text)
20296     {
20297         
20298         
20299         
20300         if(!this.activated){
20301             return;
20302         }
20303         /*
20304         if(Roo.isIE){
20305             this.win.focus();
20306             var r = this.doc.selection.createRange();
20307             if(r){
20308                 r.collapse(true);
20309                 r.pasteHTML(text);
20310                 this.syncValue();
20311                 this.deferFocus();
20312             
20313             }
20314             return;
20315         }
20316         */
20317         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20318             this.win.focus();
20319             
20320             
20321             // from jquery ui (MIT licenced)
20322             var range, node;
20323             var win = this.win;
20324             
20325             if (win.getSelection && win.getSelection().getRangeAt) {
20326                 range = win.getSelection().getRangeAt(0);
20327                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20328                 range.insertNode(node);
20329             } else if (win.document.selection && win.document.selection.createRange) {
20330                 // no firefox support
20331                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20332                 win.document.selection.createRange().pasteHTML(txt);
20333             } else {
20334                 // no firefox support
20335                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20336                 this.execCmd('InsertHTML', txt);
20337             } 
20338             
20339             this.syncValue();
20340             
20341             this.deferFocus();
20342         }
20343     },
20344  // private
20345     mozKeyPress : function(e){
20346         if(e.ctrlKey){
20347             var c = e.getCharCode(), cmd;
20348           
20349             if(c > 0){
20350                 c = String.fromCharCode(c).toLowerCase();
20351                 switch(c){
20352                     case 'b':
20353                         cmd = 'bold';
20354                         break;
20355                     case 'i':
20356                         cmd = 'italic';
20357                         break;
20358                     
20359                     case 'u':
20360                         cmd = 'underline';
20361                         break;
20362                     
20363                     case 'v':
20364                         this.cleanUpPaste.defer(100, this);
20365                         return;
20366                         
20367                 }
20368                 if(cmd){
20369                     this.win.focus();
20370                     this.execCmd(cmd);
20371                     this.deferFocus();
20372                     e.preventDefault();
20373                 }
20374                 
20375             }
20376         }
20377     },
20378
20379     // private
20380     fixKeys : function(){ // load time branching for fastest keydown performance
20381         if(Roo.isIE){
20382             return function(e){
20383                 var k = e.getKey(), r;
20384                 if(k == e.TAB){
20385                     e.stopEvent();
20386                     r = this.doc.selection.createRange();
20387                     if(r){
20388                         r.collapse(true);
20389                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20390                         this.deferFocus();
20391                     }
20392                     return;
20393                 }
20394                 
20395                 if(k == e.ENTER){
20396                     r = this.doc.selection.createRange();
20397                     if(r){
20398                         var target = r.parentElement();
20399                         if(!target || target.tagName.toLowerCase() != 'li'){
20400                             e.stopEvent();
20401                             r.pasteHTML('<br />');
20402                             r.collapse(false);
20403                             r.select();
20404                         }
20405                     }
20406                 }
20407                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20408                     this.cleanUpPaste.defer(100, this);
20409                     return;
20410                 }
20411                 
20412                 
20413             };
20414         }else if(Roo.isOpera){
20415             return function(e){
20416                 var k = e.getKey();
20417                 if(k == e.TAB){
20418                     e.stopEvent();
20419                     this.win.focus();
20420                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20421                     this.deferFocus();
20422                 }
20423                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20424                     this.cleanUpPaste.defer(100, this);
20425                     return;
20426                 }
20427                 
20428             };
20429         }else if(Roo.isSafari){
20430             return function(e){
20431                 var k = e.getKey();
20432                 
20433                 if(k == e.TAB){
20434                     e.stopEvent();
20435                     this.execCmd('InsertText','\t');
20436                     this.deferFocus();
20437                     return;
20438                 }
20439                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20440                     this.cleanUpPaste.defer(100, this);
20441                     return;
20442                 }
20443                 
20444              };
20445         }
20446     }(),
20447     
20448     getAllAncestors: function()
20449     {
20450         var p = this.getSelectedNode();
20451         var a = [];
20452         if (!p) {
20453             a.push(p); // push blank onto stack..
20454             p = this.getParentElement();
20455         }
20456         
20457         
20458         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20459             a.push(p);
20460             p = p.parentNode;
20461         }
20462         a.push(this.doc.body);
20463         return a;
20464     },
20465     lastSel : false,
20466     lastSelNode : false,
20467     
20468     
20469     getSelection : function() 
20470     {
20471         this.assignDocWin();
20472         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20473     },
20474     
20475     getSelectedNode: function() 
20476     {
20477         // this may only work on Gecko!!!
20478         
20479         // should we cache this!!!!
20480         
20481         
20482         
20483          
20484         var range = this.createRange(this.getSelection()).cloneRange();
20485         
20486         if (Roo.isIE) {
20487             var parent = range.parentElement();
20488             while (true) {
20489                 var testRange = range.duplicate();
20490                 testRange.moveToElementText(parent);
20491                 if (testRange.inRange(range)) {
20492                     break;
20493                 }
20494                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20495                     break;
20496                 }
20497                 parent = parent.parentElement;
20498             }
20499             return parent;
20500         }
20501         
20502         // is ancestor a text element.
20503         var ac =  range.commonAncestorContainer;
20504         if (ac.nodeType == 3) {
20505             ac = ac.parentNode;
20506         }
20507         
20508         var ar = ac.childNodes;
20509          
20510         var nodes = [];
20511         var other_nodes = [];
20512         var has_other_nodes = false;
20513         for (var i=0;i<ar.length;i++) {
20514             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20515                 continue;
20516             }
20517             // fullly contained node.
20518             
20519             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20520                 nodes.push(ar[i]);
20521                 continue;
20522             }
20523             
20524             // probably selected..
20525             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20526                 other_nodes.push(ar[i]);
20527                 continue;
20528             }
20529             // outer..
20530             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20531                 continue;
20532             }
20533             
20534             
20535             has_other_nodes = true;
20536         }
20537         if (!nodes.length && other_nodes.length) {
20538             nodes= other_nodes;
20539         }
20540         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20541             return false;
20542         }
20543         
20544         return nodes[0];
20545     },
20546     createRange: function(sel)
20547     {
20548         // this has strange effects when using with 
20549         // top toolbar - not sure if it's a great idea.
20550         //this.editor.contentWindow.focus();
20551         if (typeof sel != "undefined") {
20552             try {
20553                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20554             } catch(e) {
20555                 return this.doc.createRange();
20556             }
20557         } else {
20558             return this.doc.createRange();
20559         }
20560     },
20561     getParentElement: function()
20562     {
20563         
20564         this.assignDocWin();
20565         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20566         
20567         var range = this.createRange(sel);
20568          
20569         try {
20570             var p = range.commonAncestorContainer;
20571             while (p.nodeType == 3) { // text node
20572                 p = p.parentNode;
20573             }
20574             return p;
20575         } catch (e) {
20576             return null;
20577         }
20578     
20579     },
20580     /***
20581      *
20582      * Range intersection.. the hard stuff...
20583      *  '-1' = before
20584      *  '0' = hits..
20585      *  '1' = after.
20586      *         [ -- selected range --- ]
20587      *   [fail]                        [fail]
20588      *
20589      *    basically..
20590      *      if end is before start or  hits it. fail.
20591      *      if start is after end or hits it fail.
20592      *
20593      *   if either hits (but other is outside. - then it's not 
20594      *   
20595      *    
20596      **/
20597     
20598     
20599     // @see http://www.thismuchiknow.co.uk/?p=64.
20600     rangeIntersectsNode : function(range, node)
20601     {
20602         var nodeRange = node.ownerDocument.createRange();
20603         try {
20604             nodeRange.selectNode(node);
20605         } catch (e) {
20606             nodeRange.selectNodeContents(node);
20607         }
20608     
20609         var rangeStartRange = range.cloneRange();
20610         rangeStartRange.collapse(true);
20611     
20612         var rangeEndRange = range.cloneRange();
20613         rangeEndRange.collapse(false);
20614     
20615         var nodeStartRange = nodeRange.cloneRange();
20616         nodeStartRange.collapse(true);
20617     
20618         var nodeEndRange = nodeRange.cloneRange();
20619         nodeEndRange.collapse(false);
20620     
20621         return rangeStartRange.compareBoundaryPoints(
20622                  Range.START_TO_START, nodeEndRange) == -1 &&
20623                rangeEndRange.compareBoundaryPoints(
20624                  Range.START_TO_START, nodeStartRange) == 1;
20625         
20626          
20627     },
20628     rangeCompareNode : function(range, node)
20629     {
20630         var nodeRange = node.ownerDocument.createRange();
20631         try {
20632             nodeRange.selectNode(node);
20633         } catch (e) {
20634             nodeRange.selectNodeContents(node);
20635         }
20636         
20637         
20638         range.collapse(true);
20639     
20640         nodeRange.collapse(true);
20641      
20642         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20643         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20644          
20645         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20646         
20647         var nodeIsBefore   =  ss == 1;
20648         var nodeIsAfter    = ee == -1;
20649         
20650         if (nodeIsBefore && nodeIsAfter) {
20651             return 0; // outer
20652         }
20653         if (!nodeIsBefore && nodeIsAfter) {
20654             return 1; //right trailed.
20655         }
20656         
20657         if (nodeIsBefore && !nodeIsAfter) {
20658             return 2;  // left trailed.
20659         }
20660         // fully contined.
20661         return 3;
20662     },
20663
20664     // private? - in a new class?
20665     cleanUpPaste :  function()
20666     {
20667         // cleans up the whole document..
20668         Roo.log('cleanuppaste');
20669         
20670         this.cleanUpChildren(this.doc.body);
20671         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20672         if (clean != this.doc.body.innerHTML) {
20673             this.doc.body.innerHTML = clean;
20674         }
20675         
20676     },
20677     
20678     cleanWordChars : function(input) {// change the chars to hex code
20679         var he = Roo.HtmlEditorCore;
20680         
20681         var output = input;
20682         Roo.each(he.swapCodes, function(sw) { 
20683             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20684             
20685             output = output.replace(swapper, sw[1]);
20686         });
20687         
20688         return output;
20689     },
20690     
20691     
20692     cleanUpChildren : function (n)
20693     {
20694         if (!n.childNodes.length) {
20695             return;
20696         }
20697         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20698            this.cleanUpChild(n.childNodes[i]);
20699         }
20700     },
20701     
20702     
20703         
20704     
20705     cleanUpChild : function (node)
20706     {
20707         var ed = this;
20708         //console.log(node);
20709         if (node.nodeName == "#text") {
20710             // clean up silly Windows -- stuff?
20711             return; 
20712         }
20713         if (node.nodeName == "#comment") {
20714             node.parentNode.removeChild(node);
20715             // clean up silly Windows -- stuff?
20716             return; 
20717         }
20718         var lcname = node.tagName.toLowerCase();
20719         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20720         // whitelist of tags..
20721         
20722         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20723             // remove node.
20724             node.parentNode.removeChild(node);
20725             return;
20726             
20727         }
20728         
20729         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20730         
20731         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20732         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20733         
20734         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20735         //    remove_keep_children = true;
20736         //}
20737         
20738         if (remove_keep_children) {
20739             this.cleanUpChildren(node);
20740             // inserts everything just before this node...
20741             while (node.childNodes.length) {
20742                 var cn = node.childNodes[0];
20743                 node.removeChild(cn);
20744                 node.parentNode.insertBefore(cn, node);
20745             }
20746             node.parentNode.removeChild(node);
20747             return;
20748         }
20749         
20750         if (!node.attributes || !node.attributes.length) {
20751             this.cleanUpChildren(node);
20752             return;
20753         }
20754         
20755         function cleanAttr(n,v)
20756         {
20757             
20758             if (v.match(/^\./) || v.match(/^\//)) {
20759                 return;
20760             }
20761             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20762                 return;
20763             }
20764             if (v.match(/^#/)) {
20765                 return;
20766             }
20767 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20768             node.removeAttribute(n);
20769             
20770         }
20771         
20772         var cwhite = this.cwhite;
20773         var cblack = this.cblack;
20774             
20775         function cleanStyle(n,v)
20776         {
20777             if (v.match(/expression/)) { //XSS?? should we even bother..
20778                 node.removeAttribute(n);
20779                 return;
20780             }
20781             
20782             var parts = v.split(/;/);
20783             var clean = [];
20784             
20785             Roo.each(parts, function(p) {
20786                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20787                 if (!p.length) {
20788                     return true;
20789                 }
20790                 var l = p.split(':').shift().replace(/\s+/g,'');
20791                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20792                 
20793                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20794 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20795                     //node.removeAttribute(n);
20796                     return true;
20797                 }
20798                 //Roo.log()
20799                 // only allow 'c whitelisted system attributes'
20800                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20801 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20802                     //node.removeAttribute(n);
20803                     return true;
20804                 }
20805                 
20806                 
20807                  
20808                 
20809                 clean.push(p);
20810                 return true;
20811             });
20812             if (clean.length) { 
20813                 node.setAttribute(n, clean.join(';'));
20814             } else {
20815                 node.removeAttribute(n);
20816             }
20817             
20818         }
20819         
20820         
20821         for (var i = node.attributes.length-1; i > -1 ; i--) {
20822             var a = node.attributes[i];
20823             //console.log(a);
20824             
20825             if (a.name.toLowerCase().substr(0,2)=='on')  {
20826                 node.removeAttribute(a.name);
20827                 continue;
20828             }
20829             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20830                 node.removeAttribute(a.name);
20831                 continue;
20832             }
20833             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20834                 cleanAttr(a.name,a.value); // fixme..
20835                 continue;
20836             }
20837             if (a.name == 'style') {
20838                 cleanStyle(a.name,a.value);
20839                 continue;
20840             }
20841             /// clean up MS crap..
20842             // tecnically this should be a list of valid class'es..
20843             
20844             
20845             if (a.name == 'class') {
20846                 if (a.value.match(/^Mso/)) {
20847                     node.className = '';
20848                 }
20849                 
20850                 if (a.value.match(/body/)) {
20851                     node.className = '';
20852                 }
20853                 continue;
20854             }
20855             
20856             // style cleanup!?
20857             // class cleanup?
20858             
20859         }
20860         
20861         
20862         this.cleanUpChildren(node);
20863         
20864         
20865     },
20866     
20867     /**
20868      * Clean up MS wordisms...
20869      */
20870     cleanWord : function(node)
20871     {
20872         
20873         
20874         if (!node) {
20875             this.cleanWord(this.doc.body);
20876             return;
20877         }
20878         if (node.nodeName == "#text") {
20879             // clean up silly Windows -- stuff?
20880             return; 
20881         }
20882         if (node.nodeName == "#comment") {
20883             node.parentNode.removeChild(node);
20884             // clean up silly Windows -- stuff?
20885             return; 
20886         }
20887         
20888         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20889             node.parentNode.removeChild(node);
20890             return;
20891         }
20892         
20893         // remove - but keep children..
20894         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20895             while (node.childNodes.length) {
20896                 var cn = node.childNodes[0];
20897                 node.removeChild(cn);
20898                 node.parentNode.insertBefore(cn, node);
20899             }
20900             node.parentNode.removeChild(node);
20901             this.iterateChildren(node, this.cleanWord);
20902             return;
20903         }
20904         // clean styles
20905         if (node.className.length) {
20906             
20907             var cn = node.className.split(/\W+/);
20908             var cna = [];
20909             Roo.each(cn, function(cls) {
20910                 if (cls.match(/Mso[a-zA-Z]+/)) {
20911                     return;
20912                 }
20913                 cna.push(cls);
20914             });
20915             node.className = cna.length ? cna.join(' ') : '';
20916             if (!cna.length) {
20917                 node.removeAttribute("class");
20918             }
20919         }
20920         
20921         if (node.hasAttribute("lang")) {
20922             node.removeAttribute("lang");
20923         }
20924         
20925         if (node.hasAttribute("style")) {
20926             
20927             var styles = node.getAttribute("style").split(";");
20928             var nstyle = [];
20929             Roo.each(styles, function(s) {
20930                 if (!s.match(/:/)) {
20931                     return;
20932                 }
20933                 var kv = s.split(":");
20934                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20935                     return;
20936                 }
20937                 // what ever is left... we allow.
20938                 nstyle.push(s);
20939             });
20940             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20941             if (!nstyle.length) {
20942                 node.removeAttribute('style');
20943             }
20944         }
20945         this.iterateChildren(node, this.cleanWord);
20946         
20947         
20948         
20949     },
20950     /**
20951      * iterateChildren of a Node, calling fn each time, using this as the scole..
20952      * @param {DomNode} node node to iterate children of.
20953      * @param {Function} fn method of this class to call on each item.
20954      */
20955     iterateChildren : function(node, fn)
20956     {
20957         if (!node.childNodes.length) {
20958                 return;
20959         }
20960         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20961            fn.call(this, node.childNodes[i])
20962         }
20963     },
20964     
20965     
20966     /**
20967      * cleanTableWidths.
20968      *
20969      * Quite often pasting from word etc.. results in tables with column and widths.
20970      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20971      *
20972      */
20973     cleanTableWidths : function(node)
20974     {
20975          
20976          
20977         if (!node) {
20978             this.cleanTableWidths(this.doc.body);
20979             return;
20980         }
20981         
20982         // ignore list...
20983         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20984             return; 
20985         }
20986         Roo.log(node.tagName);
20987         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20988             this.iterateChildren(node, this.cleanTableWidths);
20989             return;
20990         }
20991         if (node.hasAttribute('width')) {
20992             node.removeAttribute('width');
20993         }
20994         
20995          
20996         if (node.hasAttribute("style")) {
20997             // pretty basic...
20998             
20999             var styles = node.getAttribute("style").split(";");
21000             var nstyle = [];
21001             Roo.each(styles, function(s) {
21002                 if (!s.match(/:/)) {
21003                     return;
21004                 }
21005                 var kv = s.split(":");
21006                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21007                     return;
21008                 }
21009                 // what ever is left... we allow.
21010                 nstyle.push(s);
21011             });
21012             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21013             if (!nstyle.length) {
21014                 node.removeAttribute('style');
21015             }
21016         }
21017         
21018         this.iterateChildren(node, this.cleanTableWidths);
21019         
21020         
21021     },
21022     
21023     
21024     
21025     
21026     domToHTML : function(currentElement, depth, nopadtext) {
21027         
21028         depth = depth || 0;
21029         nopadtext = nopadtext || false;
21030     
21031         if (!currentElement) {
21032             return this.domToHTML(this.doc.body);
21033         }
21034         
21035         //Roo.log(currentElement);
21036         var j;
21037         var allText = false;
21038         var nodeName = currentElement.nodeName;
21039         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21040         
21041         if  (nodeName == '#text') {
21042             
21043             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21044         }
21045         
21046         
21047         var ret = '';
21048         if (nodeName != 'BODY') {
21049              
21050             var i = 0;
21051             // Prints the node tagName, such as <A>, <IMG>, etc
21052             if (tagName) {
21053                 var attr = [];
21054                 for(i = 0; i < currentElement.attributes.length;i++) {
21055                     // quoting?
21056                     var aname = currentElement.attributes.item(i).name;
21057                     if (!currentElement.attributes.item(i).value.length) {
21058                         continue;
21059                     }
21060                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21061                 }
21062                 
21063                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21064             } 
21065             else {
21066                 
21067                 // eack
21068             }
21069         } else {
21070             tagName = false;
21071         }
21072         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21073             return ret;
21074         }
21075         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21076             nopadtext = true;
21077         }
21078         
21079         
21080         // Traverse the tree
21081         i = 0;
21082         var currentElementChild = currentElement.childNodes.item(i);
21083         var allText = true;
21084         var innerHTML  = '';
21085         lastnode = '';
21086         while (currentElementChild) {
21087             // Formatting code (indent the tree so it looks nice on the screen)
21088             var nopad = nopadtext;
21089             if (lastnode == 'SPAN') {
21090                 nopad  = true;
21091             }
21092             // text
21093             if  (currentElementChild.nodeName == '#text') {
21094                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21095                 toadd = nopadtext ? toadd : toadd.trim();
21096                 if (!nopad && toadd.length > 80) {
21097                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21098                 }
21099                 innerHTML  += toadd;
21100                 
21101                 i++;
21102                 currentElementChild = currentElement.childNodes.item(i);
21103                 lastNode = '';
21104                 continue;
21105             }
21106             allText = false;
21107             
21108             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21109                 
21110             // Recursively traverse the tree structure of the child node
21111             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21112             lastnode = currentElementChild.nodeName;
21113             i++;
21114             currentElementChild=currentElement.childNodes.item(i);
21115         }
21116         
21117         ret += innerHTML;
21118         
21119         if (!allText) {
21120                 // The remaining code is mostly for formatting the tree
21121             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21122         }
21123         
21124         
21125         if (tagName) {
21126             ret+= "</"+tagName+">";
21127         }
21128         return ret;
21129         
21130     },
21131         
21132     applyBlacklists : function()
21133     {
21134         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21135         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21136         
21137         this.white = [];
21138         this.black = [];
21139         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21140             if (b.indexOf(tag) > -1) {
21141                 return;
21142             }
21143             this.white.push(tag);
21144             
21145         }, this);
21146         
21147         Roo.each(w, function(tag) {
21148             if (b.indexOf(tag) > -1) {
21149                 return;
21150             }
21151             if (this.white.indexOf(tag) > -1) {
21152                 return;
21153             }
21154             this.white.push(tag);
21155             
21156         }, this);
21157         
21158         
21159         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21160             if (w.indexOf(tag) > -1) {
21161                 return;
21162             }
21163             this.black.push(tag);
21164             
21165         }, this);
21166         
21167         Roo.each(b, function(tag) {
21168             if (w.indexOf(tag) > -1) {
21169                 return;
21170             }
21171             if (this.black.indexOf(tag) > -1) {
21172                 return;
21173             }
21174             this.black.push(tag);
21175             
21176         }, this);
21177         
21178         
21179         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21180         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21181         
21182         this.cwhite = [];
21183         this.cblack = [];
21184         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21185             if (b.indexOf(tag) > -1) {
21186                 return;
21187             }
21188             this.cwhite.push(tag);
21189             
21190         }, this);
21191         
21192         Roo.each(w, function(tag) {
21193             if (b.indexOf(tag) > -1) {
21194                 return;
21195             }
21196             if (this.cwhite.indexOf(tag) > -1) {
21197                 return;
21198             }
21199             this.cwhite.push(tag);
21200             
21201         }, this);
21202         
21203         
21204         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21205             if (w.indexOf(tag) > -1) {
21206                 return;
21207             }
21208             this.cblack.push(tag);
21209             
21210         }, this);
21211         
21212         Roo.each(b, function(tag) {
21213             if (w.indexOf(tag) > -1) {
21214                 return;
21215             }
21216             if (this.cblack.indexOf(tag) > -1) {
21217                 return;
21218             }
21219             this.cblack.push(tag);
21220             
21221         }, this);
21222     },
21223     
21224     setStylesheets : function(stylesheets)
21225     {
21226         if(typeof(stylesheets) == 'string'){
21227             Roo.get(this.iframe.contentDocument.head).createChild({
21228                 tag : 'link',
21229                 rel : 'stylesheet',
21230                 type : 'text/css',
21231                 href : stylesheets
21232             });
21233             
21234             return;
21235         }
21236         var _this = this;
21237      
21238         Roo.each(stylesheets, function(s) {
21239             if(!s.length){
21240                 return;
21241             }
21242             
21243             Roo.get(_this.iframe.contentDocument.head).createChild({
21244                 tag : 'link',
21245                 rel : 'stylesheet',
21246                 type : 'text/css',
21247                 href : s
21248             });
21249         });
21250
21251         
21252     },
21253     
21254     removeStylesheets : function()
21255     {
21256         var _this = this;
21257         
21258         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21259             s.remove();
21260         });
21261     }
21262     
21263     // hide stuff that is not compatible
21264     /**
21265      * @event blur
21266      * @hide
21267      */
21268     /**
21269      * @event change
21270      * @hide
21271      */
21272     /**
21273      * @event focus
21274      * @hide
21275      */
21276     /**
21277      * @event specialkey
21278      * @hide
21279      */
21280     /**
21281      * @cfg {String} fieldClass @hide
21282      */
21283     /**
21284      * @cfg {String} focusClass @hide
21285      */
21286     /**
21287      * @cfg {String} autoCreate @hide
21288      */
21289     /**
21290      * @cfg {String} inputType @hide
21291      */
21292     /**
21293      * @cfg {String} invalidClass @hide
21294      */
21295     /**
21296      * @cfg {String} invalidText @hide
21297      */
21298     /**
21299      * @cfg {String} msgFx @hide
21300      */
21301     /**
21302      * @cfg {String} validateOnBlur @hide
21303      */
21304 });
21305
21306 Roo.HtmlEditorCore.white = [
21307         'area', 'br', 'img', 'input', 'hr', 'wbr',
21308         
21309        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21310        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21311        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21312        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21313        'table',   'ul',         'xmp', 
21314        
21315        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21316       'thead',   'tr', 
21317      
21318       'dir', 'menu', 'ol', 'ul', 'dl',
21319        
21320       'embed',  'object'
21321 ];
21322
21323
21324 Roo.HtmlEditorCore.black = [
21325     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21326         'applet', // 
21327         'base',   'basefont', 'bgsound', 'blink',  'body', 
21328         'frame',  'frameset', 'head',    'html',   'ilayer', 
21329         'iframe', 'layer',  'link',     'meta',    'object',   
21330         'script', 'style' ,'title',  'xml' // clean later..
21331 ];
21332 Roo.HtmlEditorCore.clean = [
21333     'script', 'style', 'title', 'xml'
21334 ];
21335 Roo.HtmlEditorCore.remove = [
21336     'font'
21337 ];
21338 // attributes..
21339
21340 Roo.HtmlEditorCore.ablack = [
21341     'on'
21342 ];
21343     
21344 Roo.HtmlEditorCore.aclean = [ 
21345     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21346 ];
21347
21348 // protocols..
21349 Roo.HtmlEditorCore.pwhite= [
21350         'http',  'https',  'mailto'
21351 ];
21352
21353 // white listed style attributes.
21354 Roo.HtmlEditorCore.cwhite= [
21355       //  'text-align', /// default is to allow most things..
21356       
21357          
21358 //        'font-size'//??
21359 ];
21360
21361 // black listed style attributes.
21362 Roo.HtmlEditorCore.cblack= [
21363       //  'font-size' -- this can be set by the project 
21364 ];
21365
21366
21367 Roo.HtmlEditorCore.swapCodes   =[ 
21368     [    8211, "--" ], 
21369     [    8212, "--" ], 
21370     [    8216,  "'" ],  
21371     [    8217, "'" ],  
21372     [    8220, '"' ],  
21373     [    8221, '"' ],  
21374     [    8226, "*" ],  
21375     [    8230, "..." ]
21376 ]; 
21377
21378     /*
21379  * - LGPL
21380  *
21381  * HtmlEditor
21382  * 
21383  */
21384
21385 /**
21386  * @class Roo.bootstrap.HtmlEditor
21387  * @extends Roo.bootstrap.TextArea
21388  * Bootstrap HtmlEditor class
21389
21390  * @constructor
21391  * Create a new HtmlEditor
21392  * @param {Object} config The config object
21393  */
21394
21395 Roo.bootstrap.HtmlEditor = function(config){
21396     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21397     if (!this.toolbars) {
21398         this.toolbars = [];
21399     }
21400     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21401     this.addEvents({
21402             /**
21403              * @event initialize
21404              * Fires when the editor is fully initialized (including the iframe)
21405              * @param {HtmlEditor} this
21406              */
21407             initialize: true,
21408             /**
21409              * @event activate
21410              * Fires when the editor is first receives the focus. Any insertion must wait
21411              * until after this event.
21412              * @param {HtmlEditor} this
21413              */
21414             activate: true,
21415              /**
21416              * @event beforesync
21417              * Fires before the textarea is updated with content from the editor iframe. Return false
21418              * to cancel the sync.
21419              * @param {HtmlEditor} this
21420              * @param {String} html
21421              */
21422             beforesync: true,
21423              /**
21424              * @event beforepush
21425              * Fires before the iframe editor is updated with content from the textarea. Return false
21426              * to cancel the push.
21427              * @param {HtmlEditor} this
21428              * @param {String} html
21429              */
21430             beforepush: true,
21431              /**
21432              * @event sync
21433              * Fires when the textarea is updated with content from the editor iframe.
21434              * @param {HtmlEditor} this
21435              * @param {String} html
21436              */
21437             sync: true,
21438              /**
21439              * @event push
21440              * Fires when the iframe editor is updated with content from the textarea.
21441              * @param {HtmlEditor} this
21442              * @param {String} html
21443              */
21444             push: true,
21445              /**
21446              * @event editmodechange
21447              * Fires when the editor switches edit modes
21448              * @param {HtmlEditor} this
21449              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21450              */
21451             editmodechange: true,
21452             /**
21453              * @event editorevent
21454              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21455              * @param {HtmlEditor} this
21456              */
21457             editorevent: true,
21458             /**
21459              * @event firstfocus
21460              * Fires when on first focus - needed by toolbars..
21461              * @param {HtmlEditor} this
21462              */
21463             firstfocus: true,
21464             /**
21465              * @event autosave
21466              * Auto save the htmlEditor value as a file into Events
21467              * @param {HtmlEditor} this
21468              */
21469             autosave: true,
21470             /**
21471              * @event savedpreview
21472              * preview the saved version of htmlEditor
21473              * @param {HtmlEditor} this
21474              */
21475             savedpreview: true
21476         });
21477 };
21478
21479
21480 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21481     
21482     
21483       /**
21484      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21485      */
21486     toolbars : false,
21487    
21488      /**
21489      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21490      *                        Roo.resizable.
21491      */
21492     resizable : false,
21493      /**
21494      * @cfg {Number} height (in pixels)
21495      */   
21496     height: 300,
21497    /**
21498      * @cfg {Number} width (in pixels)
21499      */   
21500     width: false,
21501     
21502     /**
21503      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21504      * 
21505      */
21506     stylesheets: false,
21507     
21508     // id of frame..
21509     frameId: false,
21510     
21511     // private properties
21512     validationEvent : false,
21513     deferHeight: true,
21514     initialized : false,
21515     activated : false,
21516     
21517     onFocus : Roo.emptyFn,
21518     iframePad:3,
21519     hideMode:'offsets',
21520     
21521     
21522     tbContainer : false,
21523     
21524     toolbarContainer :function() {
21525         return this.wrap.select('.x-html-editor-tb',true).first();
21526     },
21527
21528     /**
21529      * Protected method that will not generally be called directly. It
21530      * is called when the editor creates its toolbar. Override this method if you need to
21531      * add custom toolbar buttons.
21532      * @param {HtmlEditor} editor
21533      */
21534     createToolbar : function(){
21535         
21536         Roo.log("create toolbars");
21537         
21538         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21539         this.toolbars[0].render(this.toolbarContainer());
21540         
21541         return;
21542         
21543 //        if (!editor.toolbars || !editor.toolbars.length) {
21544 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21545 //        }
21546 //        
21547 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21548 //            editor.toolbars[i] = Roo.factory(
21549 //                    typeof(editor.toolbars[i]) == 'string' ?
21550 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21551 //                Roo.bootstrap.HtmlEditor);
21552 //            editor.toolbars[i].init(editor);
21553 //        }
21554     },
21555
21556      
21557     // private
21558     onRender : function(ct, position)
21559     {
21560        // Roo.log("Call onRender: " + this.xtype);
21561         var _t = this;
21562         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21563       
21564         this.wrap = this.inputEl().wrap({
21565             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21566         });
21567         
21568         this.editorcore.onRender(ct, position);
21569          
21570         if (this.resizable) {
21571             this.resizeEl = new Roo.Resizable(this.wrap, {
21572                 pinned : true,
21573                 wrap: true,
21574                 dynamic : true,
21575                 minHeight : this.height,
21576                 height: this.height,
21577                 handles : this.resizable,
21578                 width: this.width,
21579                 listeners : {
21580                     resize : function(r, w, h) {
21581                         _t.onResize(w,h); // -something
21582                     }
21583                 }
21584             });
21585             
21586         }
21587         this.createToolbar(this);
21588        
21589         
21590         if(!this.width && this.resizable){
21591             this.setSize(this.wrap.getSize());
21592         }
21593         if (this.resizeEl) {
21594             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21595             // should trigger onReize..
21596         }
21597         
21598     },
21599
21600     // private
21601     onResize : function(w, h)
21602     {
21603         Roo.log('resize: ' +w + ',' + h );
21604         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21605         var ew = false;
21606         var eh = false;
21607         
21608         if(this.inputEl() ){
21609             if(typeof w == 'number'){
21610                 var aw = w - this.wrap.getFrameWidth('lr');
21611                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21612                 ew = aw;
21613             }
21614             if(typeof h == 'number'){
21615                  var tbh = -11;  // fixme it needs to tool bar size!
21616                 for (var i =0; i < this.toolbars.length;i++) {
21617                     // fixme - ask toolbars for heights?
21618                     tbh += this.toolbars[i].el.getHeight();
21619                     //if (this.toolbars[i].footer) {
21620                     //    tbh += this.toolbars[i].footer.el.getHeight();
21621                     //}
21622                 }
21623               
21624                 
21625                 
21626                 
21627                 
21628                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21629                 ah -= 5; // knock a few pixes off for look..
21630                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21631                 var eh = ah;
21632             }
21633         }
21634         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21635         this.editorcore.onResize(ew,eh);
21636         
21637     },
21638
21639     /**
21640      * Toggles the editor between standard and source edit mode.
21641      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21642      */
21643     toggleSourceEdit : function(sourceEditMode)
21644     {
21645         this.editorcore.toggleSourceEdit(sourceEditMode);
21646         
21647         if(this.editorcore.sourceEditMode){
21648             Roo.log('editor - showing textarea');
21649             
21650 //            Roo.log('in');
21651 //            Roo.log(this.syncValue());
21652             this.syncValue();
21653             this.inputEl().removeClass(['hide', 'x-hidden']);
21654             this.inputEl().dom.removeAttribute('tabIndex');
21655             this.inputEl().focus();
21656         }else{
21657             Roo.log('editor - hiding textarea');
21658 //            Roo.log('out')
21659 //            Roo.log(this.pushValue()); 
21660             this.pushValue();
21661             
21662             this.inputEl().addClass(['hide', 'x-hidden']);
21663             this.inputEl().dom.setAttribute('tabIndex', -1);
21664             //this.deferFocus();
21665         }
21666          
21667         if(this.resizable){
21668             this.setSize(this.wrap.getSize());
21669         }
21670         
21671         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21672     },
21673  
21674     // private (for BoxComponent)
21675     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21676
21677     // private (for BoxComponent)
21678     getResizeEl : function(){
21679         return this.wrap;
21680     },
21681
21682     // private (for BoxComponent)
21683     getPositionEl : function(){
21684         return this.wrap;
21685     },
21686
21687     // private
21688     initEvents : function(){
21689         this.originalValue = this.getValue();
21690     },
21691
21692 //    /**
21693 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21694 //     * @method
21695 //     */
21696 //    markInvalid : Roo.emptyFn,
21697 //    /**
21698 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21699 //     * @method
21700 //     */
21701 //    clearInvalid : Roo.emptyFn,
21702
21703     setValue : function(v){
21704         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21705         this.editorcore.pushValue();
21706     },
21707
21708      
21709     // private
21710     deferFocus : function(){
21711         this.focus.defer(10, this);
21712     },
21713
21714     // doc'ed in Field
21715     focus : function(){
21716         this.editorcore.focus();
21717         
21718     },
21719       
21720
21721     // private
21722     onDestroy : function(){
21723         
21724         
21725         
21726         if(this.rendered){
21727             
21728             for (var i =0; i < this.toolbars.length;i++) {
21729                 // fixme - ask toolbars for heights?
21730                 this.toolbars[i].onDestroy();
21731             }
21732             
21733             this.wrap.dom.innerHTML = '';
21734             this.wrap.remove();
21735         }
21736     },
21737
21738     // private
21739     onFirstFocus : function(){
21740         //Roo.log("onFirstFocus");
21741         this.editorcore.onFirstFocus();
21742          for (var i =0; i < this.toolbars.length;i++) {
21743             this.toolbars[i].onFirstFocus();
21744         }
21745         
21746     },
21747     
21748     // private
21749     syncValue : function()
21750     {   
21751         this.editorcore.syncValue();
21752     },
21753     
21754     pushValue : function()
21755     {   
21756         this.editorcore.pushValue();
21757     }
21758      
21759     
21760     // hide stuff that is not compatible
21761     /**
21762      * @event blur
21763      * @hide
21764      */
21765     /**
21766      * @event change
21767      * @hide
21768      */
21769     /**
21770      * @event focus
21771      * @hide
21772      */
21773     /**
21774      * @event specialkey
21775      * @hide
21776      */
21777     /**
21778      * @cfg {String} fieldClass @hide
21779      */
21780     /**
21781      * @cfg {String} focusClass @hide
21782      */
21783     /**
21784      * @cfg {String} autoCreate @hide
21785      */
21786     /**
21787      * @cfg {String} inputType @hide
21788      */
21789     /**
21790      * @cfg {String} invalidClass @hide
21791      */
21792     /**
21793      * @cfg {String} invalidText @hide
21794      */
21795     /**
21796      * @cfg {String} msgFx @hide
21797      */
21798     /**
21799      * @cfg {String} validateOnBlur @hide
21800      */
21801 });
21802  
21803     
21804    
21805    
21806    
21807       
21808 Roo.namespace('Roo.bootstrap.htmleditor');
21809 /**
21810  * @class Roo.bootstrap.HtmlEditorToolbar1
21811  * Basic Toolbar
21812  * 
21813  * Usage:
21814  *
21815  new Roo.bootstrap.HtmlEditor({
21816     ....
21817     toolbars : [
21818         new Roo.bootstrap.HtmlEditorToolbar1({
21819             disable : { fonts: 1 , format: 1, ..., ... , ...],
21820             btns : [ .... ]
21821         })
21822     }
21823      
21824  * 
21825  * @cfg {Object} disable List of elements to disable..
21826  * @cfg {Array} btns List of additional buttons.
21827  * 
21828  * 
21829  * NEEDS Extra CSS? 
21830  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21831  */
21832  
21833 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21834 {
21835     
21836     Roo.apply(this, config);
21837     
21838     // default disabled, based on 'good practice'..
21839     this.disable = this.disable || {};
21840     Roo.applyIf(this.disable, {
21841         fontSize : true,
21842         colors : true,
21843         specialElements : true
21844     });
21845     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21846     
21847     this.editor = config.editor;
21848     this.editorcore = config.editor.editorcore;
21849     
21850     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21851     
21852     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21853     // dont call parent... till later.
21854 }
21855 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21856      
21857     bar : true,
21858     
21859     editor : false,
21860     editorcore : false,
21861     
21862     
21863     formats : [
21864         "p" ,  
21865         "h1","h2","h3","h4","h5","h6", 
21866         "pre", "code", 
21867         "abbr", "acronym", "address", "cite", "samp", "var",
21868         'div','span'
21869     ],
21870     
21871     onRender : function(ct, position)
21872     {
21873        // Roo.log("Call onRender: " + this.xtype);
21874         
21875        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21876        Roo.log(this.el);
21877        this.el.dom.style.marginBottom = '0';
21878        var _this = this;
21879        var editorcore = this.editorcore;
21880        var editor= this.editor;
21881        
21882        var children = [];
21883        var btn = function(id,cmd , toggle, handler){
21884        
21885             var  event = toggle ? 'toggle' : 'click';
21886        
21887             var a = {
21888                 size : 'sm',
21889                 xtype: 'Button',
21890                 xns: Roo.bootstrap,
21891                 glyphicon : id,
21892                 cmd : id || cmd,
21893                 enableToggle:toggle !== false,
21894                 //html : 'submit'
21895                 pressed : toggle ? false : null,
21896                 listeners : {}
21897             };
21898             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21899                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21900             };
21901             children.push(a);
21902             return a;
21903        }
21904         
21905         var style = {
21906                 xtype: 'Button',
21907                 size : 'sm',
21908                 xns: Roo.bootstrap,
21909                 glyphicon : 'font',
21910                 //html : 'submit'
21911                 menu : {
21912                     xtype: 'Menu',
21913                     xns: Roo.bootstrap,
21914                     items:  []
21915                 }
21916         };
21917         Roo.each(this.formats, function(f) {
21918             style.menu.items.push({
21919                 xtype :'MenuItem',
21920                 xns: Roo.bootstrap,
21921                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21922                 tagname : f,
21923                 listeners : {
21924                     click : function()
21925                     {
21926                         editorcore.insertTag(this.tagname);
21927                         editor.focus();
21928                     }
21929                 }
21930                 
21931             });
21932         });
21933          children.push(style);   
21934             
21935             
21936         btn('bold',false,true);
21937         btn('italic',false,true);
21938         btn('align-left', 'justifyleft',true);
21939         btn('align-center', 'justifycenter',true);
21940         btn('align-right' , 'justifyright',true);
21941         btn('link', false, false, function(btn) {
21942             //Roo.log("create link?");
21943             var url = prompt(this.createLinkText, this.defaultLinkValue);
21944             if(url && url != 'http:/'+'/'){
21945                 this.editorcore.relayCmd('createlink', url);
21946             }
21947         }),
21948         btn('list','insertunorderedlist',true);
21949         btn('pencil', false,true, function(btn){
21950                 Roo.log(this);
21951                 
21952                 this.toggleSourceEdit(btn.pressed);
21953         });
21954         /*
21955         var cog = {
21956                 xtype: 'Button',
21957                 size : 'sm',
21958                 xns: Roo.bootstrap,
21959                 glyphicon : 'cog',
21960                 //html : 'submit'
21961                 menu : {
21962                     xtype: 'Menu',
21963                     xns: Roo.bootstrap,
21964                     items:  []
21965                 }
21966         };
21967         
21968         cog.menu.items.push({
21969             xtype :'MenuItem',
21970             xns: Roo.bootstrap,
21971             html : Clean styles,
21972             tagname : f,
21973             listeners : {
21974                 click : function()
21975                 {
21976                     editorcore.insertTag(this.tagname);
21977                     editor.focus();
21978                 }
21979             }
21980             
21981         });
21982        */
21983         
21984          
21985        this.xtype = 'NavSimplebar';
21986         
21987         for(var i=0;i< children.length;i++) {
21988             
21989             this.buttons.add(this.addxtypeChild(children[i]));
21990             
21991         }
21992         
21993         editor.on('editorevent', this.updateToolbar, this);
21994     },
21995     onBtnClick : function(id)
21996     {
21997        this.editorcore.relayCmd(id);
21998        this.editorcore.focus();
21999     },
22000     
22001     /**
22002      * Protected method that will not generally be called directly. It triggers
22003      * a toolbar update by reading the markup state of the current selection in the editor.
22004      */
22005     updateToolbar: function(){
22006
22007         if(!this.editorcore.activated){
22008             this.editor.onFirstFocus(); // is this neeed?
22009             return;
22010         }
22011
22012         var btns = this.buttons; 
22013         var doc = this.editorcore.doc;
22014         btns.get('bold').setActive(doc.queryCommandState('bold'));
22015         btns.get('italic').setActive(doc.queryCommandState('italic'));
22016         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22017         
22018         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22019         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22020         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22021         
22022         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22023         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22024          /*
22025         
22026         var ans = this.editorcore.getAllAncestors();
22027         if (this.formatCombo) {
22028             
22029             
22030             var store = this.formatCombo.store;
22031             this.formatCombo.setValue("");
22032             for (var i =0; i < ans.length;i++) {
22033                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22034                     // select it..
22035                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22036                     break;
22037                 }
22038             }
22039         }
22040         
22041         
22042         
22043         // hides menus... - so this cant be on a menu...
22044         Roo.bootstrap.MenuMgr.hideAll();
22045         */
22046         Roo.bootstrap.MenuMgr.hideAll();
22047         //this.editorsyncValue();
22048     },
22049     onFirstFocus: function() {
22050         this.buttons.each(function(item){
22051            item.enable();
22052         });
22053     },
22054     toggleSourceEdit : function(sourceEditMode){
22055         
22056           
22057         if(sourceEditMode){
22058             Roo.log("disabling buttons");
22059            this.buttons.each( function(item){
22060                 if(item.cmd != 'pencil'){
22061                     item.disable();
22062                 }
22063             });
22064           
22065         }else{
22066             Roo.log("enabling buttons");
22067             if(this.editorcore.initialized){
22068                 this.buttons.each( function(item){
22069                     item.enable();
22070                 });
22071             }
22072             
22073         }
22074         Roo.log("calling toggole on editor");
22075         // tell the editor that it's been pressed..
22076         this.editor.toggleSourceEdit(sourceEditMode);
22077        
22078     }
22079 });
22080
22081
22082
22083
22084
22085 /**
22086  * @class Roo.bootstrap.Table.AbstractSelectionModel
22087  * @extends Roo.util.Observable
22088  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22089  * implemented by descendant classes.  This class should not be directly instantiated.
22090  * @constructor
22091  */
22092 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22093     this.locked = false;
22094     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22095 };
22096
22097
22098 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22099     /** @ignore Called by the grid automatically. Do not call directly. */
22100     init : function(grid){
22101         this.grid = grid;
22102         this.initEvents();
22103     },
22104
22105     /**
22106      * Locks the selections.
22107      */
22108     lock : function(){
22109         this.locked = true;
22110     },
22111
22112     /**
22113      * Unlocks the selections.
22114      */
22115     unlock : function(){
22116         this.locked = false;
22117     },
22118
22119     /**
22120      * Returns true if the selections are locked.
22121      * @return {Boolean}
22122      */
22123     isLocked : function(){
22124         return this.locked;
22125     }
22126 });
22127 /**
22128  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22129  * @class Roo.bootstrap.Table.RowSelectionModel
22130  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22131  * It supports multiple selections and keyboard selection/navigation. 
22132  * @constructor
22133  * @param {Object} config
22134  */
22135
22136 Roo.bootstrap.Table.RowSelectionModel = function(config){
22137     Roo.apply(this, config);
22138     this.selections = new Roo.util.MixedCollection(false, function(o){
22139         return o.id;
22140     });
22141
22142     this.last = false;
22143     this.lastActive = false;
22144
22145     this.addEvents({
22146         /**
22147              * @event selectionchange
22148              * Fires when the selection changes
22149              * @param {SelectionModel} this
22150              */
22151             "selectionchange" : true,
22152         /**
22153              * @event afterselectionchange
22154              * Fires after the selection changes (eg. by key press or clicking)
22155              * @param {SelectionModel} this
22156              */
22157             "afterselectionchange" : true,
22158         /**
22159              * @event beforerowselect
22160              * Fires when a row is selected being selected, return false to cancel.
22161              * @param {SelectionModel} this
22162              * @param {Number} rowIndex The selected index
22163              * @param {Boolean} keepExisting False if other selections will be cleared
22164              */
22165             "beforerowselect" : true,
22166         /**
22167              * @event rowselect
22168              * Fires when a row is selected.
22169              * @param {SelectionModel} this
22170              * @param {Number} rowIndex The selected index
22171              * @param {Roo.data.Record} r The record
22172              */
22173             "rowselect" : true,
22174         /**
22175              * @event rowdeselect
22176              * Fires when a row is deselected.
22177              * @param {SelectionModel} this
22178              * @param {Number} rowIndex The selected index
22179              */
22180         "rowdeselect" : true
22181     });
22182     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22183     this.locked = false;
22184 };
22185
22186 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22187     /**
22188      * @cfg {Boolean} singleSelect
22189      * True to allow selection of only one row at a time (defaults to false)
22190      */
22191     singleSelect : false,
22192
22193     // private
22194     initEvents : function(){
22195
22196         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22197             this.grid.on("mousedown", this.handleMouseDown, this);
22198         }else{ // allow click to work like normal
22199             this.grid.on("rowclick", this.handleDragableRowClick, this);
22200         }
22201
22202         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22203             "up" : function(e){
22204                 if(!e.shiftKey){
22205                     this.selectPrevious(e.shiftKey);
22206                 }else if(this.last !== false && this.lastActive !== false){
22207                     var last = this.last;
22208                     this.selectRange(this.last,  this.lastActive-1);
22209                     this.grid.getView().focusRow(this.lastActive);
22210                     if(last !== false){
22211                         this.last = last;
22212                     }
22213                 }else{
22214                     this.selectFirstRow();
22215                 }
22216                 this.fireEvent("afterselectionchange", this);
22217             },
22218             "down" : function(e){
22219                 if(!e.shiftKey){
22220                     this.selectNext(e.shiftKey);
22221                 }else if(this.last !== false && this.lastActive !== false){
22222                     var last = this.last;
22223                     this.selectRange(this.last,  this.lastActive+1);
22224                     this.grid.getView().focusRow(this.lastActive);
22225                     if(last !== false){
22226                         this.last = last;
22227                     }
22228                 }else{
22229                     this.selectFirstRow();
22230                 }
22231                 this.fireEvent("afterselectionchange", this);
22232             },
22233             scope: this
22234         });
22235
22236         var view = this.grid.view;
22237         view.on("refresh", this.onRefresh, this);
22238         view.on("rowupdated", this.onRowUpdated, this);
22239         view.on("rowremoved", this.onRemove, this);
22240     },
22241
22242     // private
22243     onRefresh : function(){
22244         var ds = this.grid.dataSource, i, v = this.grid.view;
22245         var s = this.selections;
22246         s.each(function(r){
22247             if((i = ds.indexOfId(r.id)) != -1){
22248                 v.onRowSelect(i);
22249             }else{
22250                 s.remove(r);
22251             }
22252         });
22253     },
22254
22255     // private
22256     onRemove : function(v, index, r){
22257         this.selections.remove(r);
22258     },
22259
22260     // private
22261     onRowUpdated : function(v, index, r){
22262         if(this.isSelected(r)){
22263             v.onRowSelect(index);
22264         }
22265     },
22266
22267     /**
22268      * Select records.
22269      * @param {Array} records The records to select
22270      * @param {Boolean} keepExisting (optional) True to keep existing selections
22271      */
22272     selectRecords : function(records, keepExisting){
22273         if(!keepExisting){
22274             this.clearSelections();
22275         }
22276         var ds = this.grid.dataSource;
22277         for(var i = 0, len = records.length; i < len; i++){
22278             this.selectRow(ds.indexOf(records[i]), true);
22279         }
22280     },
22281
22282     /**
22283      * Gets the number of selected rows.
22284      * @return {Number}
22285      */
22286     getCount : function(){
22287         return this.selections.length;
22288     },
22289
22290     /**
22291      * Selects the first row in the grid.
22292      */
22293     selectFirstRow : function(){
22294         this.selectRow(0);
22295     },
22296
22297     /**
22298      * Select the last row.
22299      * @param {Boolean} keepExisting (optional) True to keep existing selections
22300      */
22301     selectLastRow : function(keepExisting){
22302         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22303     },
22304
22305     /**
22306      * Selects the row immediately following the last selected row.
22307      * @param {Boolean} keepExisting (optional) True to keep existing selections
22308      */
22309     selectNext : function(keepExisting){
22310         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22311             this.selectRow(this.last+1, keepExisting);
22312             this.grid.getView().focusRow(this.last);
22313         }
22314     },
22315
22316     /**
22317      * Selects the row that precedes the last selected row.
22318      * @param {Boolean} keepExisting (optional) True to keep existing selections
22319      */
22320     selectPrevious : function(keepExisting){
22321         if(this.last){
22322             this.selectRow(this.last-1, keepExisting);
22323             this.grid.getView().focusRow(this.last);
22324         }
22325     },
22326
22327     /**
22328      * Returns the selected records
22329      * @return {Array} Array of selected records
22330      */
22331     getSelections : function(){
22332         return [].concat(this.selections.items);
22333     },
22334
22335     /**
22336      * Returns the first selected record.
22337      * @return {Record}
22338      */
22339     getSelected : function(){
22340         return this.selections.itemAt(0);
22341     },
22342
22343
22344     /**
22345      * Clears all selections.
22346      */
22347     clearSelections : function(fast){
22348         if(this.locked) {
22349             return;
22350         }
22351         if(fast !== true){
22352             var ds = this.grid.dataSource;
22353             var s = this.selections;
22354             s.each(function(r){
22355                 this.deselectRow(ds.indexOfId(r.id));
22356             }, this);
22357             s.clear();
22358         }else{
22359             this.selections.clear();
22360         }
22361         this.last = false;
22362     },
22363
22364
22365     /**
22366      * Selects all rows.
22367      */
22368     selectAll : function(){
22369         if(this.locked) {
22370             return;
22371         }
22372         this.selections.clear();
22373         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22374             this.selectRow(i, true);
22375         }
22376     },
22377
22378     /**
22379      * Returns True if there is a selection.
22380      * @return {Boolean}
22381      */
22382     hasSelection : function(){
22383         return this.selections.length > 0;
22384     },
22385
22386     /**
22387      * Returns True if the specified row is selected.
22388      * @param {Number/Record} record The record or index of the record to check
22389      * @return {Boolean}
22390      */
22391     isSelected : function(index){
22392         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22393         return (r && this.selections.key(r.id) ? true : false);
22394     },
22395
22396     /**
22397      * Returns True if the specified record id is selected.
22398      * @param {String} id The id of record to check
22399      * @return {Boolean}
22400      */
22401     isIdSelected : function(id){
22402         return (this.selections.key(id) ? true : false);
22403     },
22404
22405     // private
22406     handleMouseDown : function(e, t){
22407         var view = this.grid.getView(), rowIndex;
22408         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22409             return;
22410         };
22411         if(e.shiftKey && this.last !== false){
22412             var last = this.last;
22413             this.selectRange(last, rowIndex, e.ctrlKey);
22414             this.last = last; // reset the last
22415             view.focusRow(rowIndex);
22416         }else{
22417             var isSelected = this.isSelected(rowIndex);
22418             if(e.button !== 0 && isSelected){
22419                 view.focusRow(rowIndex);
22420             }else if(e.ctrlKey && isSelected){
22421                 this.deselectRow(rowIndex);
22422             }else if(!isSelected){
22423                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22424                 view.focusRow(rowIndex);
22425             }
22426         }
22427         this.fireEvent("afterselectionchange", this);
22428     },
22429     // private
22430     handleDragableRowClick :  function(grid, rowIndex, e) 
22431     {
22432         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22433             this.selectRow(rowIndex, false);
22434             grid.view.focusRow(rowIndex);
22435              this.fireEvent("afterselectionchange", this);
22436         }
22437     },
22438     
22439     /**
22440      * Selects multiple rows.
22441      * @param {Array} rows Array of the indexes of the row to select
22442      * @param {Boolean} keepExisting (optional) True to keep existing selections
22443      */
22444     selectRows : function(rows, keepExisting){
22445         if(!keepExisting){
22446             this.clearSelections();
22447         }
22448         for(var i = 0, len = rows.length; i < len; i++){
22449             this.selectRow(rows[i], true);
22450         }
22451     },
22452
22453     /**
22454      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22455      * @param {Number} startRow The index of the first row in the range
22456      * @param {Number} endRow The index of the last row in the range
22457      * @param {Boolean} keepExisting (optional) True to retain existing selections
22458      */
22459     selectRange : function(startRow, endRow, keepExisting){
22460         if(this.locked) {
22461             return;
22462         }
22463         if(!keepExisting){
22464             this.clearSelections();
22465         }
22466         if(startRow <= endRow){
22467             for(var i = startRow; i <= endRow; i++){
22468                 this.selectRow(i, true);
22469             }
22470         }else{
22471             for(var i = startRow; i >= endRow; i--){
22472                 this.selectRow(i, true);
22473             }
22474         }
22475     },
22476
22477     /**
22478      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22479      * @param {Number} startRow The index of the first row in the range
22480      * @param {Number} endRow The index of the last row in the range
22481      */
22482     deselectRange : function(startRow, endRow, preventViewNotify){
22483         if(this.locked) {
22484             return;
22485         }
22486         for(var i = startRow; i <= endRow; i++){
22487             this.deselectRow(i, preventViewNotify);
22488         }
22489     },
22490
22491     /**
22492      * Selects a row.
22493      * @param {Number} row The index of the row to select
22494      * @param {Boolean} keepExisting (optional) True to keep existing selections
22495      */
22496     selectRow : function(index, keepExisting, preventViewNotify){
22497         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22498             return;
22499         }
22500         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22501             if(!keepExisting || this.singleSelect){
22502                 this.clearSelections();
22503             }
22504             var r = this.grid.dataSource.getAt(index);
22505             this.selections.add(r);
22506             this.last = this.lastActive = index;
22507             if(!preventViewNotify){
22508                 this.grid.getView().onRowSelect(index);
22509             }
22510             this.fireEvent("rowselect", this, index, r);
22511             this.fireEvent("selectionchange", this);
22512         }
22513     },
22514
22515     /**
22516      * Deselects a row.
22517      * @param {Number} row The index of the row to deselect
22518      */
22519     deselectRow : function(index, preventViewNotify){
22520         if(this.locked) {
22521             return;
22522         }
22523         if(this.last == index){
22524             this.last = false;
22525         }
22526         if(this.lastActive == index){
22527             this.lastActive = false;
22528         }
22529         var r = this.grid.dataSource.getAt(index);
22530         this.selections.remove(r);
22531         if(!preventViewNotify){
22532             this.grid.getView().onRowDeselect(index);
22533         }
22534         this.fireEvent("rowdeselect", this, index);
22535         this.fireEvent("selectionchange", this);
22536     },
22537
22538     // private
22539     restoreLast : function(){
22540         if(this._last){
22541             this.last = this._last;
22542         }
22543     },
22544
22545     // private
22546     acceptsNav : function(row, col, cm){
22547         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22548     },
22549
22550     // private
22551     onEditorKey : function(field, e){
22552         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22553         if(k == e.TAB){
22554             e.stopEvent();
22555             ed.completeEdit();
22556             if(e.shiftKey){
22557                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22558             }else{
22559                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22560             }
22561         }else if(k == e.ENTER && !e.ctrlKey){
22562             e.stopEvent();
22563             ed.completeEdit();
22564             if(e.shiftKey){
22565                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22566             }else{
22567                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22568             }
22569         }else if(k == e.ESC){
22570             ed.cancelEdit();
22571         }
22572         if(newCell){
22573             g.startEditing(newCell[0], newCell[1]);
22574         }
22575     }
22576 });/*
22577  * Based on:
22578  * Ext JS Library 1.1.1
22579  * Copyright(c) 2006-2007, Ext JS, LLC.
22580  *
22581  * Originally Released Under LGPL - original licence link has changed is not relivant.
22582  *
22583  * Fork - LGPL
22584  * <script type="text/javascript">
22585  */
22586  
22587 /**
22588  * @class Roo.bootstrap.PagingToolbar
22589  * @extends Roo.bootstrap.NavSimplebar
22590  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22591  * @constructor
22592  * Create a new PagingToolbar
22593  * @param {Object} config The config object
22594  * @param {Roo.data.Store} store
22595  */
22596 Roo.bootstrap.PagingToolbar = function(config)
22597 {
22598     // old args format still supported... - xtype is prefered..
22599         // created from xtype...
22600     
22601     this.ds = config.dataSource;
22602     
22603     if (config.store && !this.ds) {
22604         this.store= Roo.factory(config.store, Roo.data);
22605         this.ds = this.store;
22606         this.ds.xmodule = this.xmodule || false;
22607     }
22608     
22609     this.toolbarItems = [];
22610     if (config.items) {
22611         this.toolbarItems = config.items;
22612     }
22613     
22614     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22615     
22616     this.cursor = 0;
22617     
22618     if (this.ds) { 
22619         this.bind(this.ds);
22620     }
22621     
22622     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22623     
22624 };
22625
22626 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22627     /**
22628      * @cfg {Roo.data.Store} dataSource
22629      * The underlying data store providing the paged data
22630      */
22631     /**
22632      * @cfg {String/HTMLElement/Element} container
22633      * container The id or element that will contain the toolbar
22634      */
22635     /**
22636      * @cfg {Boolean} displayInfo
22637      * True to display the displayMsg (defaults to false)
22638      */
22639     /**
22640      * @cfg {Number} pageSize
22641      * The number of records to display per page (defaults to 20)
22642      */
22643     pageSize: 20,
22644     /**
22645      * @cfg {String} displayMsg
22646      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22647      */
22648     displayMsg : 'Displaying {0} - {1} of {2}',
22649     /**
22650      * @cfg {String} emptyMsg
22651      * The message to display when no records are found (defaults to "No data to display")
22652      */
22653     emptyMsg : 'No data to display',
22654     /**
22655      * Customizable piece of the default paging text (defaults to "Page")
22656      * @type String
22657      */
22658     beforePageText : "Page",
22659     /**
22660      * Customizable piece of the default paging text (defaults to "of %0")
22661      * @type String
22662      */
22663     afterPageText : "of {0}",
22664     /**
22665      * Customizable piece of the default paging text (defaults to "First Page")
22666      * @type String
22667      */
22668     firstText : "First Page",
22669     /**
22670      * Customizable piece of the default paging text (defaults to "Previous Page")
22671      * @type String
22672      */
22673     prevText : "Previous Page",
22674     /**
22675      * Customizable piece of the default paging text (defaults to "Next Page")
22676      * @type String
22677      */
22678     nextText : "Next Page",
22679     /**
22680      * Customizable piece of the default paging text (defaults to "Last Page")
22681      * @type String
22682      */
22683     lastText : "Last Page",
22684     /**
22685      * Customizable piece of the default paging text (defaults to "Refresh")
22686      * @type String
22687      */
22688     refreshText : "Refresh",
22689
22690     buttons : false,
22691     // private
22692     onRender : function(ct, position) 
22693     {
22694         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22695         this.navgroup.parentId = this.id;
22696         this.navgroup.onRender(this.el, null);
22697         // add the buttons to the navgroup
22698         
22699         if(this.displayInfo){
22700             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22701             this.displayEl = this.el.select('.x-paging-info', true).first();
22702 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22703 //            this.displayEl = navel.el.select('span',true).first();
22704         }
22705         
22706         var _this = this;
22707         
22708         if(this.buttons){
22709             Roo.each(_this.buttons, function(e){ // this might need to use render????
22710                Roo.factory(e).onRender(_this.el, null);
22711             });
22712         }
22713             
22714         Roo.each(_this.toolbarItems, function(e) {
22715             _this.navgroup.addItem(e);
22716         });
22717         
22718         
22719         this.first = this.navgroup.addItem({
22720             tooltip: this.firstText,
22721             cls: "prev",
22722             icon : 'fa fa-backward',
22723             disabled: true,
22724             preventDefault: true,
22725             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22726         });
22727         
22728         this.prev =  this.navgroup.addItem({
22729             tooltip: this.prevText,
22730             cls: "prev",
22731             icon : 'fa fa-step-backward',
22732             disabled: true,
22733             preventDefault: true,
22734             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22735         });
22736     //this.addSeparator();
22737         
22738         
22739         var field = this.navgroup.addItem( {
22740             tagtype : 'span',
22741             cls : 'x-paging-position',
22742             
22743             html : this.beforePageText  +
22744                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22745                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22746          } ); //?? escaped?
22747         
22748         this.field = field.el.select('input', true).first();
22749         this.field.on("keydown", this.onPagingKeydown, this);
22750         this.field.on("focus", function(){this.dom.select();});
22751     
22752     
22753         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22754         //this.field.setHeight(18);
22755         //this.addSeparator();
22756         this.next = this.navgroup.addItem({
22757             tooltip: this.nextText,
22758             cls: "next",
22759             html : ' <i class="fa fa-step-forward">',
22760             disabled: true,
22761             preventDefault: true,
22762             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22763         });
22764         this.last = this.navgroup.addItem({
22765             tooltip: this.lastText,
22766             icon : 'fa fa-forward',
22767             cls: "next",
22768             disabled: true,
22769             preventDefault: true,
22770             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22771         });
22772     //this.addSeparator();
22773         this.loading = this.navgroup.addItem({
22774             tooltip: this.refreshText,
22775             icon: 'fa fa-refresh',
22776             preventDefault: true,
22777             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22778         });
22779         
22780     },
22781
22782     // private
22783     updateInfo : function(){
22784         if(this.displayEl){
22785             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22786             var msg = count == 0 ?
22787                 this.emptyMsg :
22788                 String.format(
22789                     this.displayMsg,
22790                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22791                 );
22792             this.displayEl.update(msg);
22793         }
22794     },
22795
22796     // private
22797     onLoad : function(ds, r, o){
22798        this.cursor = o.params ? o.params.start : 0;
22799        var d = this.getPageData(),
22800             ap = d.activePage,
22801             ps = d.pages;
22802         
22803        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22804        this.field.dom.value = ap;
22805        this.first.setDisabled(ap == 1);
22806        this.prev.setDisabled(ap == 1);
22807        this.next.setDisabled(ap == ps);
22808        this.last.setDisabled(ap == ps);
22809        this.loading.enable();
22810        this.updateInfo();
22811     },
22812
22813     // private
22814     getPageData : function(){
22815         var total = this.ds.getTotalCount();
22816         return {
22817             total : total,
22818             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22819             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22820         };
22821     },
22822
22823     // private
22824     onLoadError : function(){
22825         this.loading.enable();
22826     },
22827
22828     // private
22829     onPagingKeydown : function(e){
22830         var k = e.getKey();
22831         var d = this.getPageData();
22832         if(k == e.RETURN){
22833             var v = this.field.dom.value, pageNum;
22834             if(!v || isNaN(pageNum = parseInt(v, 10))){
22835                 this.field.dom.value = d.activePage;
22836                 return;
22837             }
22838             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22839             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22840             e.stopEvent();
22841         }
22842         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))
22843         {
22844           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22845           this.field.dom.value = pageNum;
22846           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22847           e.stopEvent();
22848         }
22849         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22850         {
22851           var v = this.field.dom.value, pageNum; 
22852           var increment = (e.shiftKey) ? 10 : 1;
22853           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22854                 increment *= -1;
22855           }
22856           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22857             this.field.dom.value = d.activePage;
22858             return;
22859           }
22860           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22861           {
22862             this.field.dom.value = parseInt(v, 10) + increment;
22863             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22864             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22865           }
22866           e.stopEvent();
22867         }
22868     },
22869
22870     // private
22871     beforeLoad : function(){
22872         if(this.loading){
22873             this.loading.disable();
22874         }
22875     },
22876
22877     // private
22878     onClick : function(which){
22879         
22880         var ds = this.ds;
22881         if (!ds) {
22882             return;
22883         }
22884         
22885         switch(which){
22886             case "first":
22887                 ds.load({params:{start: 0, limit: this.pageSize}});
22888             break;
22889             case "prev":
22890                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22891             break;
22892             case "next":
22893                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22894             break;
22895             case "last":
22896                 var total = ds.getTotalCount();
22897                 var extra = total % this.pageSize;
22898                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22899                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22900             break;
22901             case "refresh":
22902                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22903             break;
22904         }
22905     },
22906
22907     /**
22908      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22909      * @param {Roo.data.Store} store The data store to unbind
22910      */
22911     unbind : function(ds){
22912         ds.un("beforeload", this.beforeLoad, this);
22913         ds.un("load", this.onLoad, this);
22914         ds.un("loadexception", this.onLoadError, this);
22915         ds.un("remove", this.updateInfo, this);
22916         ds.un("add", this.updateInfo, this);
22917         this.ds = undefined;
22918     },
22919
22920     /**
22921      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22922      * @param {Roo.data.Store} store The data store to bind
22923      */
22924     bind : function(ds){
22925         ds.on("beforeload", this.beforeLoad, this);
22926         ds.on("load", this.onLoad, this);
22927         ds.on("loadexception", this.onLoadError, this);
22928         ds.on("remove", this.updateInfo, this);
22929         ds.on("add", this.updateInfo, this);
22930         this.ds = ds;
22931     }
22932 });/*
22933  * - LGPL
22934  *
22935  * element
22936  * 
22937  */
22938
22939 /**
22940  * @class Roo.bootstrap.MessageBar
22941  * @extends Roo.bootstrap.Component
22942  * Bootstrap MessageBar class
22943  * @cfg {String} html contents of the MessageBar
22944  * @cfg {String} weight (info | success | warning | danger) default info
22945  * @cfg {String} beforeClass insert the bar before the given class
22946  * @cfg {Boolean} closable (true | false) default false
22947  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22948  * 
22949  * @constructor
22950  * Create a new Element
22951  * @param {Object} config The config object
22952  */
22953
22954 Roo.bootstrap.MessageBar = function(config){
22955     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22956 };
22957
22958 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22959     
22960     html: '',
22961     weight: 'info',
22962     closable: false,
22963     fixed: false,
22964     beforeClass: 'bootstrap-sticky-wrap',
22965     
22966     getAutoCreate : function(){
22967         
22968         var cfg = {
22969             tag: 'div',
22970             cls: 'alert alert-dismissable alert-' + this.weight,
22971             cn: [
22972                 {
22973                     tag: 'span',
22974                     cls: 'message',
22975                     html: this.html || ''
22976                 }
22977             ]
22978         };
22979         
22980         if(this.fixed){
22981             cfg.cls += ' alert-messages-fixed';
22982         }
22983         
22984         if(this.closable){
22985             cfg.cn.push({
22986                 tag: 'button',
22987                 cls: 'close',
22988                 html: 'x'
22989             });
22990         }
22991         
22992         return cfg;
22993     },
22994     
22995     onRender : function(ct, position)
22996     {
22997         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22998         
22999         if(!this.el){
23000             var cfg = Roo.apply({},  this.getAutoCreate());
23001             cfg.id = Roo.id();
23002             
23003             if (this.cls) {
23004                 cfg.cls += ' ' + this.cls;
23005             }
23006             if (this.style) {
23007                 cfg.style = this.style;
23008             }
23009             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23010             
23011             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23012         }
23013         
23014         this.el.select('>button.close').on('click', this.hide, this);
23015         
23016     },
23017     
23018     show : function()
23019     {
23020         if (!this.rendered) {
23021             this.render();
23022         }
23023         
23024         this.el.show();
23025         
23026         this.fireEvent('show', this);
23027         
23028     },
23029     
23030     hide : function()
23031     {
23032         if (!this.rendered) {
23033             this.render();
23034         }
23035         
23036         this.el.hide();
23037         
23038         this.fireEvent('hide', this);
23039     },
23040     
23041     update : function()
23042     {
23043 //        var e = this.el.dom.firstChild;
23044 //        
23045 //        if(this.closable){
23046 //            e = e.nextSibling;
23047 //        }
23048 //        
23049 //        e.data = this.html || '';
23050
23051         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23052     }
23053    
23054 });
23055
23056  
23057
23058      /*
23059  * - LGPL
23060  *
23061  * Graph
23062  * 
23063  */
23064
23065
23066 /**
23067  * @class Roo.bootstrap.Graph
23068  * @extends Roo.bootstrap.Component
23069  * Bootstrap Graph class
23070 > Prameters
23071  -sm {number} sm 4
23072  -md {number} md 5
23073  @cfg {String} graphtype  bar | vbar | pie
23074  @cfg {number} g_x coodinator | centre x (pie)
23075  @cfg {number} g_y coodinator | centre y (pie)
23076  @cfg {number} g_r radius (pie)
23077  @cfg {number} g_height height of the chart (respected by all elements in the set)
23078  @cfg {number} g_width width of the chart (respected by all elements in the set)
23079  @cfg {Object} title The title of the chart
23080     
23081  -{Array}  values
23082  -opts (object) options for the chart 
23083      o {
23084      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23085      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23086      o vgutter (number)
23087      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.
23088      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23089      o to
23090      o stretch (boolean)
23091      o }
23092  -opts (object) options for the pie
23093      o{
23094      o cut
23095      o startAngle (number)
23096      o endAngle (number)
23097      } 
23098  *
23099  * @constructor
23100  * Create a new Input
23101  * @param {Object} config The config object
23102  */
23103
23104 Roo.bootstrap.Graph = function(config){
23105     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23106     
23107     this.addEvents({
23108         // img events
23109         /**
23110          * @event click
23111          * The img click event for the img.
23112          * @param {Roo.EventObject} e
23113          */
23114         "click" : true
23115     });
23116 };
23117
23118 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23119     
23120     sm: 4,
23121     md: 5,
23122     graphtype: 'bar',
23123     g_height: 250,
23124     g_width: 400,
23125     g_x: 50,
23126     g_y: 50,
23127     g_r: 30,
23128     opts:{
23129         //g_colors: this.colors,
23130         g_type: 'soft',
23131         g_gutter: '20%'
23132
23133     },
23134     title : false,
23135
23136     getAutoCreate : function(){
23137         
23138         var cfg = {
23139             tag: 'div',
23140             html : null
23141         };
23142         
23143         
23144         return  cfg;
23145     },
23146
23147     onRender : function(ct,position){
23148         
23149         
23150         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23151         
23152         if (typeof(Raphael) == 'undefined') {
23153             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23154             return;
23155         }
23156         
23157         this.raphael = Raphael(this.el.dom);
23158         
23159                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23160                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23161                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23162                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23163                 /*
23164                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23165                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23166                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23167                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23168                 
23169                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23170                 r.barchart(330, 10, 300, 220, data1);
23171                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23172                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23173                 */
23174                 
23175                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23176                 // r.barchart(30, 30, 560, 250,  xdata, {
23177                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23178                 //     axis : "0 0 1 1",
23179                 //     axisxlabels :  xdata
23180                 //     //yvalues : cols,
23181                    
23182                 // });
23183 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23184 //        
23185 //        this.load(null,xdata,{
23186 //                axis : "0 0 1 1",
23187 //                axisxlabels :  xdata
23188 //                });
23189
23190     },
23191
23192     load : function(graphtype,xdata,opts)
23193     {
23194         this.raphael.clear();
23195         if(!graphtype) {
23196             graphtype = this.graphtype;
23197         }
23198         if(!opts){
23199             opts = this.opts;
23200         }
23201         var r = this.raphael,
23202             fin = function () {
23203                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23204             },
23205             fout = function () {
23206                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23207             },
23208             pfin = function() {
23209                 this.sector.stop();
23210                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23211
23212                 if (this.label) {
23213                     this.label[0].stop();
23214                     this.label[0].attr({ r: 7.5 });
23215                     this.label[1].attr({ "font-weight": 800 });
23216                 }
23217             },
23218             pfout = function() {
23219                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23220
23221                 if (this.label) {
23222                     this.label[0].animate({ r: 5 }, 500, "bounce");
23223                     this.label[1].attr({ "font-weight": 400 });
23224                 }
23225             };
23226
23227         switch(graphtype){
23228             case 'bar':
23229                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23230                 break;
23231             case 'hbar':
23232                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23233                 break;
23234             case 'pie':
23235 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23236 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23237 //            
23238                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23239                 
23240                 break;
23241
23242         }
23243         
23244         if(this.title){
23245             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23246         }
23247         
23248     },
23249     
23250     setTitle: function(o)
23251     {
23252         this.title = o;
23253     },
23254     
23255     initEvents: function() {
23256         
23257         if(!this.href){
23258             this.el.on('click', this.onClick, this);
23259         }
23260     },
23261     
23262     onClick : function(e)
23263     {
23264         Roo.log('img onclick');
23265         this.fireEvent('click', this, e);
23266     }
23267    
23268 });
23269
23270  
23271 /*
23272  * - LGPL
23273  *
23274  * numberBox
23275  * 
23276  */
23277 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23278
23279 /**
23280  * @class Roo.bootstrap.dash.NumberBox
23281  * @extends Roo.bootstrap.Component
23282  * Bootstrap NumberBox class
23283  * @cfg {String} headline Box headline
23284  * @cfg {String} content Box content
23285  * @cfg {String} icon Box icon
23286  * @cfg {String} footer Footer text
23287  * @cfg {String} fhref Footer href
23288  * 
23289  * @constructor
23290  * Create a new NumberBox
23291  * @param {Object} config The config object
23292  */
23293
23294
23295 Roo.bootstrap.dash.NumberBox = function(config){
23296     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23297     
23298 };
23299
23300 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23301     
23302     headline : '',
23303     content : '',
23304     icon : '',
23305     footer : '',
23306     fhref : '',
23307     ficon : '',
23308     
23309     getAutoCreate : function(){
23310         
23311         var cfg = {
23312             tag : 'div',
23313             cls : 'small-box ',
23314             cn : [
23315                 {
23316                     tag : 'div',
23317                     cls : 'inner',
23318                     cn :[
23319                         {
23320                             tag : 'h3',
23321                             cls : 'roo-headline',
23322                             html : this.headline
23323                         },
23324                         {
23325                             tag : 'p',
23326                             cls : 'roo-content',
23327                             html : this.content
23328                         }
23329                     ]
23330                 }
23331             ]
23332         };
23333         
23334         if(this.icon){
23335             cfg.cn.push({
23336                 tag : 'div',
23337                 cls : 'icon',
23338                 cn :[
23339                     {
23340                         tag : 'i',
23341                         cls : 'ion ' + this.icon
23342                     }
23343                 ]
23344             });
23345         }
23346         
23347         if(this.footer){
23348             var footer = {
23349                 tag : 'a',
23350                 cls : 'small-box-footer',
23351                 href : this.fhref || '#',
23352                 html : this.footer
23353             };
23354             
23355             cfg.cn.push(footer);
23356             
23357         }
23358         
23359         return  cfg;
23360     },
23361
23362     onRender : function(ct,position){
23363         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23364
23365
23366        
23367                 
23368     },
23369
23370     setHeadline: function (value)
23371     {
23372         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23373     },
23374     
23375     setFooter: function (value, href)
23376     {
23377         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23378         
23379         if(href){
23380             this.el.select('a.small-box-footer',true).first().attr('href', href);
23381         }
23382         
23383     },
23384
23385     setContent: function (value)
23386     {
23387         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23388     },
23389
23390     initEvents: function() 
23391     {   
23392         
23393     }
23394     
23395 });
23396
23397  
23398 /*
23399  * - LGPL
23400  *
23401  * TabBox
23402  * 
23403  */
23404 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23405
23406 /**
23407  * @class Roo.bootstrap.dash.TabBox
23408  * @extends Roo.bootstrap.Component
23409  * Bootstrap TabBox class
23410  * @cfg {String} title Title of the TabBox
23411  * @cfg {String} icon Icon of the TabBox
23412  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23413  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23414  * 
23415  * @constructor
23416  * Create a new TabBox
23417  * @param {Object} config The config object
23418  */
23419
23420
23421 Roo.bootstrap.dash.TabBox = function(config){
23422     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23423     this.addEvents({
23424         // raw events
23425         /**
23426          * @event addpane
23427          * When a pane is added
23428          * @param {Roo.bootstrap.dash.TabPane} pane
23429          */
23430         "addpane" : true,
23431         /**
23432          * @event activatepane
23433          * When a pane is activated
23434          * @param {Roo.bootstrap.dash.TabPane} pane
23435          */
23436         "activatepane" : true
23437         
23438          
23439     });
23440     
23441     this.panes = [];
23442 };
23443
23444 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23445
23446     title : '',
23447     icon : false,
23448     showtabs : true,
23449     tabScrollable : false,
23450     
23451     getChildContainer : function()
23452     {
23453         return this.el.select('.tab-content', true).first();
23454     },
23455     
23456     getAutoCreate : function(){
23457         
23458         var header = {
23459             tag: 'li',
23460             cls: 'pull-left header',
23461             html: this.title,
23462             cn : []
23463         };
23464         
23465         if(this.icon){
23466             header.cn.push({
23467                 tag: 'i',
23468                 cls: 'fa ' + this.icon
23469             });
23470         }
23471         
23472         var h = {
23473             tag: 'ul',
23474             cls: 'nav nav-tabs pull-right',
23475             cn: [
23476                 header
23477             ]
23478         };
23479         
23480         if(this.tabScrollable){
23481             h = {
23482                 tag: 'div',
23483                 cls: 'tab-header',
23484                 cn: [
23485                     {
23486                         tag: 'ul',
23487                         cls: 'nav nav-tabs pull-right',
23488                         cn: [
23489                             header
23490                         ]
23491                     }
23492                 ]
23493             };
23494         }
23495         
23496         var cfg = {
23497             tag: 'div',
23498             cls: 'nav-tabs-custom',
23499             cn: [
23500                 h,
23501                 {
23502                     tag: 'div',
23503                     cls: 'tab-content no-padding',
23504                     cn: []
23505                 }
23506             ]
23507         };
23508
23509         return  cfg;
23510     },
23511     initEvents : function()
23512     {
23513         //Roo.log('add add pane handler');
23514         this.on('addpane', this.onAddPane, this);
23515     },
23516      /**
23517      * Updates the box title
23518      * @param {String} html to set the title to.
23519      */
23520     setTitle : function(value)
23521     {
23522         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23523     },
23524     onAddPane : function(pane)
23525     {
23526         this.panes.push(pane);
23527         //Roo.log('addpane');
23528         //Roo.log(pane);
23529         // tabs are rendere left to right..
23530         if(!this.showtabs){
23531             return;
23532         }
23533         
23534         var ctr = this.el.select('.nav-tabs', true).first();
23535          
23536          
23537         var existing = ctr.select('.nav-tab',true);
23538         var qty = existing.getCount();;
23539         
23540         
23541         var tab = ctr.createChild({
23542             tag : 'li',
23543             cls : 'nav-tab' + (qty ? '' : ' active'),
23544             cn : [
23545                 {
23546                     tag : 'a',
23547                     href:'#',
23548                     html : pane.title
23549                 }
23550             ]
23551         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23552         pane.tab = tab;
23553         
23554         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23555         if (!qty) {
23556             pane.el.addClass('active');
23557         }
23558         
23559                 
23560     },
23561     onTabClick : function(ev,un,ob,pane)
23562     {
23563         //Roo.log('tab - prev default');
23564         ev.preventDefault();
23565         
23566         
23567         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23568         pane.tab.addClass('active');
23569         //Roo.log(pane.title);
23570         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23571         // technically we should have a deactivate event.. but maybe add later.
23572         // and it should not de-activate the selected tab...
23573         this.fireEvent('activatepane', pane);
23574         pane.el.addClass('active');
23575         pane.fireEvent('activate');
23576         
23577         
23578     },
23579     
23580     getActivePane : function()
23581     {
23582         var r = false;
23583         Roo.each(this.panes, function(p) {
23584             if(p.el.hasClass('active')){
23585                 r = p;
23586                 return false;
23587             }
23588             
23589             return;
23590         });
23591         
23592         return r;
23593     }
23594     
23595     
23596 });
23597
23598  
23599 /*
23600  * - LGPL
23601  *
23602  * Tab pane
23603  * 
23604  */
23605 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23606 /**
23607  * @class Roo.bootstrap.TabPane
23608  * @extends Roo.bootstrap.Component
23609  * Bootstrap TabPane class
23610  * @cfg {Boolean} active (false | true) Default false
23611  * @cfg {String} title title of panel
23612
23613  * 
23614  * @constructor
23615  * Create a new TabPane
23616  * @param {Object} config The config object
23617  */
23618
23619 Roo.bootstrap.dash.TabPane = function(config){
23620     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23621     
23622     this.addEvents({
23623         // raw events
23624         /**
23625          * @event activate
23626          * When a pane is activated
23627          * @param {Roo.bootstrap.dash.TabPane} pane
23628          */
23629         "activate" : true
23630          
23631     });
23632 };
23633
23634 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23635     
23636     active : false,
23637     title : '',
23638     
23639     // the tabBox that this is attached to.
23640     tab : false,
23641      
23642     getAutoCreate : function() 
23643     {
23644         var cfg = {
23645             tag: 'div',
23646             cls: 'tab-pane'
23647         };
23648         
23649         if(this.active){
23650             cfg.cls += ' active';
23651         }
23652         
23653         return cfg;
23654     },
23655     initEvents  : function()
23656     {
23657         //Roo.log('trigger add pane handler');
23658         this.parent().fireEvent('addpane', this)
23659     },
23660     
23661      /**
23662      * Updates the tab title 
23663      * @param {String} html to set the title to.
23664      */
23665     setTitle: function(str)
23666     {
23667         if (!this.tab) {
23668             return;
23669         }
23670         this.title = str;
23671         this.tab.select('a', true).first().dom.innerHTML = str;
23672         
23673     }
23674     
23675     
23676     
23677 });
23678
23679  
23680
23681
23682  /*
23683  * - LGPL
23684  *
23685  * menu
23686  * 
23687  */
23688 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23689
23690 /**
23691  * @class Roo.bootstrap.menu.Menu
23692  * @extends Roo.bootstrap.Component
23693  * Bootstrap Menu class - container for Menu
23694  * @cfg {String} html Text of the menu
23695  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23696  * @cfg {String} icon Font awesome icon
23697  * @cfg {String} pos Menu align to (top | bottom) default bottom
23698  * 
23699  * 
23700  * @constructor
23701  * Create a new Menu
23702  * @param {Object} config The config object
23703  */
23704
23705
23706 Roo.bootstrap.menu.Menu = function(config){
23707     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23708     
23709     this.addEvents({
23710         /**
23711          * @event beforeshow
23712          * Fires before this menu is displayed
23713          * @param {Roo.bootstrap.menu.Menu} this
23714          */
23715         beforeshow : true,
23716         /**
23717          * @event beforehide
23718          * Fires before this menu is hidden
23719          * @param {Roo.bootstrap.menu.Menu} this
23720          */
23721         beforehide : true,
23722         /**
23723          * @event show
23724          * Fires after this menu is displayed
23725          * @param {Roo.bootstrap.menu.Menu} this
23726          */
23727         show : true,
23728         /**
23729          * @event hide
23730          * Fires after this menu is hidden
23731          * @param {Roo.bootstrap.menu.Menu} this
23732          */
23733         hide : true,
23734         /**
23735          * @event click
23736          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23737          * @param {Roo.bootstrap.menu.Menu} this
23738          * @param {Roo.EventObject} e
23739          */
23740         click : true
23741     });
23742     
23743 };
23744
23745 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23746     
23747     submenu : false,
23748     html : '',
23749     weight : 'default',
23750     icon : false,
23751     pos : 'bottom',
23752     
23753     
23754     getChildContainer : function() {
23755         if(this.isSubMenu){
23756             return this.el;
23757         }
23758         
23759         return this.el.select('ul.dropdown-menu', true).first();  
23760     },
23761     
23762     getAutoCreate : function()
23763     {
23764         var text = [
23765             {
23766                 tag : 'span',
23767                 cls : 'roo-menu-text',
23768                 html : this.html
23769             }
23770         ];
23771         
23772         if(this.icon){
23773             text.unshift({
23774                 tag : 'i',
23775                 cls : 'fa ' + this.icon
23776             })
23777         }
23778         
23779         
23780         var cfg = {
23781             tag : 'div',
23782             cls : 'btn-group',
23783             cn : [
23784                 {
23785                     tag : 'button',
23786                     cls : 'dropdown-button btn btn-' + this.weight,
23787                     cn : text
23788                 },
23789                 {
23790                     tag : 'button',
23791                     cls : 'dropdown-toggle btn btn-' + this.weight,
23792                     cn : [
23793                         {
23794                             tag : 'span',
23795                             cls : 'caret'
23796                         }
23797                     ]
23798                 },
23799                 {
23800                     tag : 'ul',
23801                     cls : 'dropdown-menu'
23802                 }
23803             ]
23804             
23805         };
23806         
23807         if(this.pos == 'top'){
23808             cfg.cls += ' dropup';
23809         }
23810         
23811         if(this.isSubMenu){
23812             cfg = {
23813                 tag : 'ul',
23814                 cls : 'dropdown-menu'
23815             }
23816         }
23817         
23818         return cfg;
23819     },
23820     
23821     onRender : function(ct, position)
23822     {
23823         this.isSubMenu = ct.hasClass('dropdown-submenu');
23824         
23825         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23826     },
23827     
23828     initEvents : function() 
23829     {
23830         if(this.isSubMenu){
23831             return;
23832         }
23833         
23834         this.hidden = true;
23835         
23836         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23837         this.triggerEl.on('click', this.onTriggerPress, this);
23838         
23839         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23840         this.buttonEl.on('click', this.onClick, this);
23841         
23842     },
23843     
23844     list : function()
23845     {
23846         if(this.isSubMenu){
23847             return this.el;
23848         }
23849         
23850         return this.el.select('ul.dropdown-menu', true).first();
23851     },
23852     
23853     onClick : function(e)
23854     {
23855         this.fireEvent("click", this, e);
23856     },
23857     
23858     onTriggerPress  : function(e)
23859     {   
23860         if (this.isVisible()) {
23861             this.hide();
23862         } else {
23863             this.show();
23864         }
23865     },
23866     
23867     isVisible : function(){
23868         return !this.hidden;
23869     },
23870     
23871     show : function()
23872     {
23873         this.fireEvent("beforeshow", this);
23874         
23875         this.hidden = false;
23876         this.el.addClass('open');
23877         
23878         Roo.get(document).on("mouseup", this.onMouseUp, this);
23879         
23880         this.fireEvent("show", this);
23881         
23882         
23883     },
23884     
23885     hide : function()
23886     {
23887         this.fireEvent("beforehide", this);
23888         
23889         this.hidden = true;
23890         this.el.removeClass('open');
23891         
23892         Roo.get(document).un("mouseup", this.onMouseUp);
23893         
23894         this.fireEvent("hide", this);
23895     },
23896     
23897     onMouseUp : function()
23898     {
23899         this.hide();
23900     }
23901     
23902 });
23903
23904  
23905  /*
23906  * - LGPL
23907  *
23908  * menu item
23909  * 
23910  */
23911 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23912
23913 /**
23914  * @class Roo.bootstrap.menu.Item
23915  * @extends Roo.bootstrap.Component
23916  * Bootstrap MenuItem class
23917  * @cfg {Boolean} submenu (true | false) default false
23918  * @cfg {String} html text of the item
23919  * @cfg {String} href the link
23920  * @cfg {Boolean} disable (true | false) default false
23921  * @cfg {Boolean} preventDefault (true | false) default true
23922  * @cfg {String} icon Font awesome icon
23923  * @cfg {String} pos Submenu align to (left | right) default right 
23924  * 
23925  * 
23926  * @constructor
23927  * Create a new Item
23928  * @param {Object} config The config object
23929  */
23930
23931
23932 Roo.bootstrap.menu.Item = function(config){
23933     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23934     this.addEvents({
23935         /**
23936          * @event mouseover
23937          * Fires when the mouse is hovering over this menu
23938          * @param {Roo.bootstrap.menu.Item} this
23939          * @param {Roo.EventObject} e
23940          */
23941         mouseover : true,
23942         /**
23943          * @event mouseout
23944          * Fires when the mouse exits this menu
23945          * @param {Roo.bootstrap.menu.Item} this
23946          * @param {Roo.EventObject} e
23947          */
23948         mouseout : true,
23949         // raw events
23950         /**
23951          * @event click
23952          * The raw click event for the entire grid.
23953          * @param {Roo.EventObject} e
23954          */
23955         click : true
23956     });
23957 };
23958
23959 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23960     
23961     submenu : false,
23962     href : '',
23963     html : '',
23964     preventDefault: true,
23965     disable : false,
23966     icon : false,
23967     pos : 'right',
23968     
23969     getAutoCreate : function()
23970     {
23971         var text = [
23972             {
23973                 tag : 'span',
23974                 cls : 'roo-menu-item-text',
23975                 html : this.html
23976             }
23977         ];
23978         
23979         if(this.icon){
23980             text.unshift({
23981                 tag : 'i',
23982                 cls : 'fa ' + this.icon
23983             })
23984         }
23985         
23986         var cfg = {
23987             tag : 'li',
23988             cn : [
23989                 {
23990                     tag : 'a',
23991                     href : this.href || '#',
23992                     cn : text
23993                 }
23994             ]
23995         };
23996         
23997         if(this.disable){
23998             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23999         }
24000         
24001         if(this.submenu){
24002             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24003             
24004             if(this.pos == 'left'){
24005                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24006             }
24007         }
24008         
24009         return cfg;
24010     },
24011     
24012     initEvents : function() 
24013     {
24014         this.el.on('mouseover', this.onMouseOver, this);
24015         this.el.on('mouseout', this.onMouseOut, this);
24016         
24017         this.el.select('a', true).first().on('click', this.onClick, this);
24018         
24019     },
24020     
24021     onClick : function(e)
24022     {
24023         if(this.preventDefault){
24024             e.preventDefault();
24025         }
24026         
24027         this.fireEvent("click", this, e);
24028     },
24029     
24030     onMouseOver : function(e)
24031     {
24032         if(this.submenu && this.pos == 'left'){
24033             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24034         }
24035         
24036         this.fireEvent("mouseover", this, e);
24037     },
24038     
24039     onMouseOut : function(e)
24040     {
24041         this.fireEvent("mouseout", this, e);
24042     }
24043 });
24044
24045  
24046
24047  /*
24048  * - LGPL
24049  *
24050  * menu separator
24051  * 
24052  */
24053 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24054
24055 /**
24056  * @class Roo.bootstrap.menu.Separator
24057  * @extends Roo.bootstrap.Component
24058  * Bootstrap Separator class
24059  * 
24060  * @constructor
24061  * Create a new Separator
24062  * @param {Object} config The config object
24063  */
24064
24065
24066 Roo.bootstrap.menu.Separator = function(config){
24067     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24068 };
24069
24070 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24071     
24072     getAutoCreate : function(){
24073         var cfg = {
24074             tag : 'li',
24075             cls: 'divider'
24076         };
24077         
24078         return cfg;
24079     }
24080    
24081 });
24082
24083  
24084
24085  /*
24086  * - LGPL
24087  *
24088  * Tooltip
24089  * 
24090  */
24091
24092 /**
24093  * @class Roo.bootstrap.Tooltip
24094  * Bootstrap Tooltip class
24095  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24096  * to determine which dom element triggers the tooltip.
24097  * 
24098  * It needs to add support for additional attributes like tooltip-position
24099  * 
24100  * @constructor
24101  * Create a new Toolti
24102  * @param {Object} config The config object
24103  */
24104
24105 Roo.bootstrap.Tooltip = function(config){
24106     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24107 };
24108
24109 Roo.apply(Roo.bootstrap.Tooltip, {
24110     /**
24111      * @function init initialize tooltip monitoring.
24112      * @static
24113      */
24114     currentEl : false,
24115     currentTip : false,
24116     currentRegion : false,
24117     
24118     //  init : delay?
24119     
24120     init : function()
24121     {
24122         Roo.get(document).on('mouseover', this.enter ,this);
24123         Roo.get(document).on('mouseout', this.leave, this);
24124          
24125         
24126         this.currentTip = new Roo.bootstrap.Tooltip();
24127     },
24128     
24129     enter : function(ev)
24130     {
24131         var dom = ev.getTarget();
24132         
24133         //Roo.log(['enter',dom]);
24134         var el = Roo.fly(dom);
24135         if (this.currentEl) {
24136             //Roo.log(dom);
24137             //Roo.log(this.currentEl);
24138             //Roo.log(this.currentEl.contains(dom));
24139             if (this.currentEl == el) {
24140                 return;
24141             }
24142             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24143                 return;
24144             }
24145
24146         }
24147         
24148         if (this.currentTip.el) {
24149             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24150         }    
24151         //Roo.log(ev);
24152         var bindEl = el;
24153         
24154         // you can not look for children, as if el is the body.. then everythign is the child..
24155         if (!el.attr('tooltip')) { //
24156             if (!el.select("[tooltip]").elements.length) {
24157                 return;
24158             }
24159             // is the mouse over this child...?
24160             bindEl = el.select("[tooltip]").first();
24161             var xy = ev.getXY();
24162             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24163                 //Roo.log("not in region.");
24164                 return;
24165             }
24166             //Roo.log("child element over..");
24167             
24168         }
24169         this.currentEl = bindEl;
24170         this.currentTip.bind(bindEl);
24171         this.currentRegion = Roo.lib.Region.getRegion(dom);
24172         this.currentTip.enter();
24173         
24174     },
24175     leave : function(ev)
24176     {
24177         var dom = ev.getTarget();
24178         //Roo.log(['leave',dom]);
24179         if (!this.currentEl) {
24180             return;
24181         }
24182         
24183         
24184         if (dom != this.currentEl.dom) {
24185             return;
24186         }
24187         var xy = ev.getXY();
24188         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24189             return;
24190         }
24191         // only activate leave if mouse cursor is outside... bounding box..
24192         
24193         
24194         
24195         
24196         if (this.currentTip) {
24197             this.currentTip.leave();
24198         }
24199         //Roo.log('clear currentEl');
24200         this.currentEl = false;
24201         
24202         
24203     },
24204     alignment : {
24205         'left' : ['r-l', [-2,0], 'right'],
24206         'right' : ['l-r', [2,0], 'left'],
24207         'bottom' : ['t-b', [0,2], 'top'],
24208         'top' : [ 'b-t', [0,-2], 'bottom']
24209     }
24210     
24211 });
24212
24213
24214 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24215     
24216     
24217     bindEl : false,
24218     
24219     delay : null, // can be { show : 300 , hide: 500}
24220     
24221     timeout : null,
24222     
24223     hoverState : null, //???
24224     
24225     placement : 'bottom', 
24226     
24227     getAutoCreate : function(){
24228     
24229         var cfg = {
24230            cls : 'tooltip',
24231            role : 'tooltip',
24232            cn : [
24233                 {
24234                     cls : 'tooltip-arrow'
24235                 },
24236                 {
24237                     cls : 'tooltip-inner'
24238                 }
24239            ]
24240         };
24241         
24242         return cfg;
24243     },
24244     bind : function(el)
24245     {
24246         this.bindEl = el;
24247     },
24248       
24249     
24250     enter : function () {
24251        
24252         if (this.timeout != null) {
24253             clearTimeout(this.timeout);
24254         }
24255         
24256         this.hoverState = 'in';
24257          //Roo.log("enter - show");
24258         if (!this.delay || !this.delay.show) {
24259             this.show();
24260             return;
24261         }
24262         var _t = this;
24263         this.timeout = setTimeout(function () {
24264             if (_t.hoverState == 'in') {
24265                 _t.show();
24266             }
24267         }, this.delay.show);
24268     },
24269     leave : function()
24270     {
24271         clearTimeout(this.timeout);
24272     
24273         this.hoverState = 'out';
24274          if (!this.delay || !this.delay.hide) {
24275             this.hide();
24276             return;
24277         }
24278        
24279         var _t = this;
24280         this.timeout = setTimeout(function () {
24281             //Roo.log("leave - timeout");
24282             
24283             if (_t.hoverState == 'out') {
24284                 _t.hide();
24285                 Roo.bootstrap.Tooltip.currentEl = false;
24286             }
24287         }, delay);
24288     },
24289     
24290     show : function ()
24291     {
24292         if (!this.el) {
24293             this.render(document.body);
24294         }
24295         // set content.
24296         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24297         
24298         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24299         
24300         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24301         
24302         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24303         
24304         var placement = typeof this.placement == 'function' ?
24305             this.placement.call(this, this.el, on_el) :
24306             this.placement;
24307             
24308         var autoToken = /\s?auto?\s?/i;
24309         var autoPlace = autoToken.test(placement);
24310         if (autoPlace) {
24311             placement = placement.replace(autoToken, '') || 'top';
24312         }
24313         
24314         //this.el.detach()
24315         //this.el.setXY([0,0]);
24316         this.el.show();
24317         //this.el.dom.style.display='block';
24318         
24319         //this.el.appendTo(on_el);
24320         
24321         var p = this.getPosition();
24322         var box = this.el.getBox();
24323         
24324         if (autoPlace) {
24325             // fixme..
24326         }
24327         
24328         var align = Roo.bootstrap.Tooltip.alignment[placement];
24329         
24330         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24331         
24332         if(placement == 'top' || placement == 'bottom'){
24333             if(xy[0] < 0){
24334                 placement = 'right';
24335             }
24336             
24337             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24338                 placement = 'left';
24339             }
24340         }
24341         
24342         align = Roo.bootstrap.Tooltip.alignment[placement];
24343         
24344         this.el.alignTo(this.bindEl, align[0],align[1]);
24345         //var arrow = this.el.select('.arrow',true).first();
24346         //arrow.set(align[2], 
24347         
24348         this.el.addClass(placement);
24349         
24350         this.el.addClass('in fade');
24351         
24352         this.hoverState = null;
24353         
24354         if (this.el.hasClass('fade')) {
24355             // fade it?
24356         }
24357         
24358     },
24359     hide : function()
24360     {
24361          
24362         if (!this.el) {
24363             return;
24364         }
24365         //this.el.setXY([0,0]);
24366         this.el.removeClass('in');
24367         //this.el.hide();
24368         
24369     }
24370     
24371 });
24372  
24373
24374  /*
24375  * - LGPL
24376  *
24377  * Location Picker
24378  * 
24379  */
24380
24381 /**
24382  * @class Roo.bootstrap.LocationPicker
24383  * @extends Roo.bootstrap.Component
24384  * Bootstrap LocationPicker class
24385  * @cfg {Number} latitude Position when init default 0
24386  * @cfg {Number} longitude Position when init default 0
24387  * @cfg {Number} zoom default 15
24388  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24389  * @cfg {Boolean} mapTypeControl default false
24390  * @cfg {Boolean} disableDoubleClickZoom default false
24391  * @cfg {Boolean} scrollwheel default true
24392  * @cfg {Boolean} streetViewControl default false
24393  * @cfg {Number} radius default 0
24394  * @cfg {String} locationName
24395  * @cfg {Boolean} draggable default true
24396  * @cfg {Boolean} enableAutocomplete default false
24397  * @cfg {Boolean} enableReverseGeocode default true
24398  * @cfg {String} markerTitle
24399  * 
24400  * @constructor
24401  * Create a new LocationPicker
24402  * @param {Object} config The config object
24403  */
24404
24405
24406 Roo.bootstrap.LocationPicker = function(config){
24407     
24408     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24409     
24410     this.addEvents({
24411         /**
24412          * @event initial
24413          * Fires when the picker initialized.
24414          * @param {Roo.bootstrap.LocationPicker} this
24415          * @param {Google Location} location
24416          */
24417         initial : true,
24418         /**
24419          * @event positionchanged
24420          * Fires when the picker position changed.
24421          * @param {Roo.bootstrap.LocationPicker} this
24422          * @param {Google Location} location
24423          */
24424         positionchanged : true,
24425         /**
24426          * @event resize
24427          * Fires when the map resize.
24428          * @param {Roo.bootstrap.LocationPicker} this
24429          */
24430         resize : true,
24431         /**
24432          * @event show
24433          * Fires when the map show.
24434          * @param {Roo.bootstrap.LocationPicker} this
24435          */
24436         show : true,
24437         /**
24438          * @event hide
24439          * Fires when the map hide.
24440          * @param {Roo.bootstrap.LocationPicker} this
24441          */
24442         hide : true,
24443         /**
24444          * @event mapClick
24445          * Fires when click the map.
24446          * @param {Roo.bootstrap.LocationPicker} this
24447          * @param {Map event} e
24448          */
24449         mapClick : true,
24450         /**
24451          * @event mapRightClick
24452          * Fires when right click the map.
24453          * @param {Roo.bootstrap.LocationPicker} this
24454          * @param {Map event} e
24455          */
24456         mapRightClick : true,
24457         /**
24458          * @event markerClick
24459          * Fires when click the marker.
24460          * @param {Roo.bootstrap.LocationPicker} this
24461          * @param {Map event} e
24462          */
24463         markerClick : true,
24464         /**
24465          * @event markerRightClick
24466          * Fires when right click the marker.
24467          * @param {Roo.bootstrap.LocationPicker} this
24468          * @param {Map event} e
24469          */
24470         markerRightClick : true,
24471         /**
24472          * @event OverlayViewDraw
24473          * Fires when OverlayView Draw
24474          * @param {Roo.bootstrap.LocationPicker} this
24475          */
24476         OverlayViewDraw : true,
24477         /**
24478          * @event OverlayViewOnAdd
24479          * Fires when OverlayView Draw
24480          * @param {Roo.bootstrap.LocationPicker} this
24481          */
24482         OverlayViewOnAdd : true,
24483         /**
24484          * @event OverlayViewOnRemove
24485          * Fires when OverlayView Draw
24486          * @param {Roo.bootstrap.LocationPicker} this
24487          */
24488         OverlayViewOnRemove : true,
24489         /**
24490          * @event OverlayViewShow
24491          * Fires when OverlayView Draw
24492          * @param {Roo.bootstrap.LocationPicker} this
24493          * @param {Pixel} cpx
24494          */
24495         OverlayViewShow : true,
24496         /**
24497          * @event OverlayViewHide
24498          * Fires when OverlayView Draw
24499          * @param {Roo.bootstrap.LocationPicker} this
24500          */
24501         OverlayViewHide : true,
24502         /**
24503          * @event loadexception
24504          * Fires when load google lib failed.
24505          * @param {Roo.bootstrap.LocationPicker} this
24506          */
24507         loadexception : true
24508     });
24509         
24510 };
24511
24512 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24513     
24514     gMapContext: false,
24515     
24516     latitude: 0,
24517     longitude: 0,
24518     zoom: 15,
24519     mapTypeId: false,
24520     mapTypeControl: false,
24521     disableDoubleClickZoom: false,
24522     scrollwheel: true,
24523     streetViewControl: false,
24524     radius: 0,
24525     locationName: '',
24526     draggable: true,
24527     enableAutocomplete: false,
24528     enableReverseGeocode: true,
24529     markerTitle: '',
24530     
24531     getAutoCreate: function()
24532     {
24533
24534         var cfg = {
24535             tag: 'div',
24536             cls: 'roo-location-picker'
24537         };
24538         
24539         return cfg
24540     },
24541     
24542     initEvents: function(ct, position)
24543     {       
24544         if(!this.el.getWidth() || this.isApplied()){
24545             return;
24546         }
24547         
24548         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24549         
24550         this.initial();
24551     },
24552     
24553     initial: function()
24554     {
24555         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24556             this.fireEvent('loadexception', this);
24557             return;
24558         }
24559         
24560         if(!this.mapTypeId){
24561             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24562         }
24563         
24564         this.gMapContext = this.GMapContext();
24565         
24566         this.initOverlayView();
24567         
24568         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24569         
24570         var _this = this;
24571                 
24572         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24573             _this.setPosition(_this.gMapContext.marker.position);
24574         });
24575         
24576         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24577             _this.fireEvent('mapClick', this, event);
24578             
24579         });
24580
24581         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24582             _this.fireEvent('mapRightClick', this, event);
24583             
24584         });
24585         
24586         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24587             _this.fireEvent('markerClick', this, event);
24588             
24589         });
24590
24591         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24592             _this.fireEvent('markerRightClick', this, event);
24593             
24594         });
24595         
24596         this.setPosition(this.gMapContext.location);
24597         
24598         this.fireEvent('initial', this, this.gMapContext.location);
24599     },
24600     
24601     initOverlayView: function()
24602     {
24603         var _this = this;
24604         
24605         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24606             
24607             draw: function()
24608             {
24609                 _this.fireEvent('OverlayViewDraw', _this);
24610             },
24611             
24612             onAdd: function()
24613             {
24614                 _this.fireEvent('OverlayViewOnAdd', _this);
24615             },
24616             
24617             onRemove: function()
24618             {
24619                 _this.fireEvent('OverlayViewOnRemove', _this);
24620             },
24621             
24622             show: function(cpx)
24623             {
24624                 _this.fireEvent('OverlayViewShow', _this, cpx);
24625             },
24626             
24627             hide: function()
24628             {
24629                 _this.fireEvent('OverlayViewHide', _this);
24630             }
24631             
24632         });
24633     },
24634     
24635     fromLatLngToContainerPixel: function(event)
24636     {
24637         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24638     },
24639     
24640     isApplied: function() 
24641     {
24642         return this.getGmapContext() == false ? false : true;
24643     },
24644     
24645     getGmapContext: function() 
24646     {
24647         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24648     },
24649     
24650     GMapContext: function() 
24651     {
24652         var position = new google.maps.LatLng(this.latitude, this.longitude);
24653         
24654         var _map = new google.maps.Map(this.el.dom, {
24655             center: position,
24656             zoom: this.zoom,
24657             mapTypeId: this.mapTypeId,
24658             mapTypeControl: this.mapTypeControl,
24659             disableDoubleClickZoom: this.disableDoubleClickZoom,
24660             scrollwheel: this.scrollwheel,
24661             streetViewControl: this.streetViewControl,
24662             locationName: this.locationName,
24663             draggable: this.draggable,
24664             enableAutocomplete: this.enableAutocomplete,
24665             enableReverseGeocode: this.enableReverseGeocode
24666         });
24667         
24668         var _marker = new google.maps.Marker({
24669             position: position,
24670             map: _map,
24671             title: this.markerTitle,
24672             draggable: this.draggable
24673         });
24674         
24675         return {
24676             map: _map,
24677             marker: _marker,
24678             circle: null,
24679             location: position,
24680             radius: this.radius,
24681             locationName: this.locationName,
24682             addressComponents: {
24683                 formatted_address: null,
24684                 addressLine1: null,
24685                 addressLine2: null,
24686                 streetName: null,
24687                 streetNumber: null,
24688                 city: null,
24689                 district: null,
24690                 state: null,
24691                 stateOrProvince: null
24692             },
24693             settings: this,
24694             domContainer: this.el.dom,
24695             geodecoder: new google.maps.Geocoder()
24696         };
24697     },
24698     
24699     drawCircle: function(center, radius, options) 
24700     {
24701         if (this.gMapContext.circle != null) {
24702             this.gMapContext.circle.setMap(null);
24703         }
24704         if (radius > 0) {
24705             radius *= 1;
24706             options = Roo.apply({}, options, {
24707                 strokeColor: "#0000FF",
24708                 strokeOpacity: .35,
24709                 strokeWeight: 2,
24710                 fillColor: "#0000FF",
24711                 fillOpacity: .2
24712             });
24713             
24714             options.map = this.gMapContext.map;
24715             options.radius = radius;
24716             options.center = center;
24717             this.gMapContext.circle = new google.maps.Circle(options);
24718             return this.gMapContext.circle;
24719         }
24720         
24721         return null;
24722     },
24723     
24724     setPosition: function(location) 
24725     {
24726         this.gMapContext.location = location;
24727         this.gMapContext.marker.setPosition(location);
24728         this.gMapContext.map.panTo(location);
24729         this.drawCircle(location, this.gMapContext.radius, {});
24730         
24731         var _this = this;
24732         
24733         if (this.gMapContext.settings.enableReverseGeocode) {
24734             this.gMapContext.geodecoder.geocode({
24735                 latLng: this.gMapContext.location
24736             }, function(results, status) {
24737                 
24738                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24739                     _this.gMapContext.locationName = results[0].formatted_address;
24740                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24741                     
24742                     _this.fireEvent('positionchanged', this, location);
24743                 }
24744             });
24745             
24746             return;
24747         }
24748         
24749         this.fireEvent('positionchanged', this, location);
24750     },
24751     
24752     resize: function()
24753     {
24754         google.maps.event.trigger(this.gMapContext.map, "resize");
24755         
24756         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24757         
24758         this.fireEvent('resize', this);
24759     },
24760     
24761     setPositionByLatLng: function(latitude, longitude)
24762     {
24763         this.setPosition(new google.maps.LatLng(latitude, longitude));
24764     },
24765     
24766     getCurrentPosition: function() 
24767     {
24768         return {
24769             latitude: this.gMapContext.location.lat(),
24770             longitude: this.gMapContext.location.lng()
24771         };
24772     },
24773     
24774     getAddressName: function() 
24775     {
24776         return this.gMapContext.locationName;
24777     },
24778     
24779     getAddressComponents: function() 
24780     {
24781         return this.gMapContext.addressComponents;
24782     },
24783     
24784     address_component_from_google_geocode: function(address_components) 
24785     {
24786         var result = {};
24787         
24788         for (var i = 0; i < address_components.length; i++) {
24789             var component = address_components[i];
24790             if (component.types.indexOf("postal_code") >= 0) {
24791                 result.postalCode = component.short_name;
24792             } else if (component.types.indexOf("street_number") >= 0) {
24793                 result.streetNumber = component.short_name;
24794             } else if (component.types.indexOf("route") >= 0) {
24795                 result.streetName = component.short_name;
24796             } else if (component.types.indexOf("neighborhood") >= 0) {
24797                 result.city = component.short_name;
24798             } else if (component.types.indexOf("locality") >= 0) {
24799                 result.city = component.short_name;
24800             } else if (component.types.indexOf("sublocality") >= 0) {
24801                 result.district = component.short_name;
24802             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24803                 result.stateOrProvince = component.short_name;
24804             } else if (component.types.indexOf("country") >= 0) {
24805                 result.country = component.short_name;
24806             }
24807         }
24808         
24809         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24810         result.addressLine2 = "";
24811         return result;
24812     },
24813     
24814     setZoomLevel: function(zoom)
24815     {
24816         this.gMapContext.map.setZoom(zoom);
24817     },
24818     
24819     show: function()
24820     {
24821         if(!this.el){
24822             return;
24823         }
24824         
24825         this.el.show();
24826         
24827         this.resize();
24828         
24829         this.fireEvent('show', this);
24830     },
24831     
24832     hide: function()
24833     {
24834         if(!this.el){
24835             return;
24836         }
24837         
24838         this.el.hide();
24839         
24840         this.fireEvent('hide', this);
24841     }
24842     
24843 });
24844
24845 Roo.apply(Roo.bootstrap.LocationPicker, {
24846     
24847     OverlayView : function(map, options)
24848     {
24849         options = options || {};
24850         
24851         this.setMap(map);
24852     }
24853     
24854     
24855 });/*
24856  * - LGPL
24857  *
24858  * Alert
24859  * 
24860  */
24861
24862 /**
24863  * @class Roo.bootstrap.Alert
24864  * @extends Roo.bootstrap.Component
24865  * Bootstrap Alert class
24866  * @cfg {String} title The title of alert
24867  * @cfg {String} html The content of alert
24868  * @cfg {String} weight (  success | info | warning | danger )
24869  * @cfg {String} faicon font-awesomeicon
24870  * 
24871  * @constructor
24872  * Create a new alert
24873  * @param {Object} config The config object
24874  */
24875
24876
24877 Roo.bootstrap.Alert = function(config){
24878     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24879     
24880 };
24881
24882 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24883     
24884     title: '',
24885     html: '',
24886     weight: false,
24887     faicon: false,
24888     
24889     getAutoCreate : function()
24890     {
24891         
24892         var cfg = {
24893             tag : 'div',
24894             cls : 'alert',
24895             cn : [
24896                 {
24897                     tag : 'i',
24898                     cls : 'roo-alert-icon'
24899                     
24900                 },
24901                 {
24902                     tag : 'b',
24903                     cls : 'roo-alert-title',
24904                     html : this.title
24905                 },
24906                 {
24907                     tag : 'span',
24908                     cls : 'roo-alert-text',
24909                     html : this.html
24910                 }
24911             ]
24912         };
24913         
24914         if(this.faicon){
24915             cfg.cn[0].cls += ' fa ' + this.faicon;
24916         }
24917         
24918         if(this.weight){
24919             cfg.cls += ' alert-' + this.weight;
24920         }
24921         
24922         return cfg;
24923     },
24924     
24925     initEvents: function() 
24926     {
24927         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24928     },
24929     
24930     setTitle : function(str)
24931     {
24932         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24933     },
24934     
24935     setText : function(str)
24936     {
24937         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24938     },
24939     
24940     setWeight : function(weight)
24941     {
24942         if(this.weight){
24943             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24944         }
24945         
24946         this.weight = weight;
24947         
24948         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24949     },
24950     
24951     setIcon : function(icon)
24952     {
24953         if(this.faicon){
24954             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24955         }
24956         
24957         this.faicon = icon;
24958         
24959         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24960     },
24961     
24962     hide: function() 
24963     {
24964         this.el.hide();   
24965     },
24966     
24967     show: function() 
24968     {  
24969         this.el.show();   
24970     }
24971     
24972 });
24973
24974  
24975 /*
24976 * Licence: LGPL
24977 */
24978
24979 /**
24980  * @class Roo.bootstrap.UploadCropbox
24981  * @extends Roo.bootstrap.Component
24982  * Bootstrap UploadCropbox class
24983  * @cfg {String} emptyText show when image has been loaded
24984  * @cfg {String} rotateNotify show when image too small to rotate
24985  * @cfg {Number} errorTimeout default 3000
24986  * @cfg {Number} minWidth default 300
24987  * @cfg {Number} minHeight default 300
24988  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24989  * @cfg {Boolean} isDocument (true|false) default false
24990  * @cfg {String} url action url
24991  * @cfg {String} paramName default 'imageUpload'
24992  * @cfg {String} method default POST
24993  * @cfg {Boolean} loadMask (true|false) default true
24994  * @cfg {Boolean} loadingText default 'Loading...'
24995  * 
24996  * @constructor
24997  * Create a new UploadCropbox
24998  * @param {Object} config The config object
24999  */
25000
25001 Roo.bootstrap.UploadCropbox = function(config){
25002     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25003     
25004     this.addEvents({
25005         /**
25006          * @event beforeselectfile
25007          * Fire before select file
25008          * @param {Roo.bootstrap.UploadCropbox} this
25009          */
25010         "beforeselectfile" : true,
25011         /**
25012          * @event initial
25013          * Fire after initEvent
25014          * @param {Roo.bootstrap.UploadCropbox} this
25015          */
25016         "initial" : true,
25017         /**
25018          * @event crop
25019          * Fire after initEvent
25020          * @param {Roo.bootstrap.UploadCropbox} this
25021          * @param {String} data
25022          */
25023         "crop" : true,
25024         /**
25025          * @event prepare
25026          * Fire when preparing the file data
25027          * @param {Roo.bootstrap.UploadCropbox} this
25028          * @param {Object} file
25029          */
25030         "prepare" : true,
25031         /**
25032          * @event exception
25033          * Fire when get exception
25034          * @param {Roo.bootstrap.UploadCropbox} this
25035          * @param {XMLHttpRequest} xhr
25036          */
25037         "exception" : true,
25038         /**
25039          * @event beforeloadcanvas
25040          * Fire before load the canvas
25041          * @param {Roo.bootstrap.UploadCropbox} this
25042          * @param {String} src
25043          */
25044         "beforeloadcanvas" : true,
25045         /**
25046          * @event trash
25047          * Fire when trash image
25048          * @param {Roo.bootstrap.UploadCropbox} this
25049          */
25050         "trash" : true,
25051         /**
25052          * @event download
25053          * Fire when download the image
25054          * @param {Roo.bootstrap.UploadCropbox} this
25055          */
25056         "download" : true,
25057         /**
25058          * @event footerbuttonclick
25059          * Fire when footerbuttonclick
25060          * @param {Roo.bootstrap.UploadCropbox} this
25061          * @param {String} type
25062          */
25063         "footerbuttonclick" : true,
25064         /**
25065          * @event resize
25066          * Fire when resize
25067          * @param {Roo.bootstrap.UploadCropbox} this
25068          */
25069         "resize" : true,
25070         /**
25071          * @event rotate
25072          * Fire when rotate the image
25073          * @param {Roo.bootstrap.UploadCropbox} this
25074          * @param {String} pos
25075          */
25076         "rotate" : true,
25077         /**
25078          * @event inspect
25079          * Fire when inspect the file
25080          * @param {Roo.bootstrap.UploadCropbox} this
25081          * @param {Object} file
25082          */
25083         "inspect" : true,
25084         /**
25085          * @event upload
25086          * Fire when xhr upload the file
25087          * @param {Roo.bootstrap.UploadCropbox} this
25088          * @param {Object} data
25089          */
25090         "upload" : true,
25091         /**
25092          * @event arrange
25093          * Fire when arrange the file data
25094          * @param {Roo.bootstrap.UploadCropbox} this
25095          * @param {Object} formData
25096          */
25097         "arrange" : true
25098     });
25099     
25100     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25101 };
25102
25103 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25104     
25105     emptyText : 'Click to upload image',
25106     rotateNotify : 'Image is too small to rotate',
25107     errorTimeout : 3000,
25108     scale : 0,
25109     baseScale : 1,
25110     rotate : 0,
25111     dragable : false,
25112     pinching : false,
25113     mouseX : 0,
25114     mouseY : 0,
25115     cropData : false,
25116     minWidth : 300,
25117     minHeight : 300,
25118     file : false,
25119     exif : {},
25120     baseRotate : 1,
25121     cropType : 'image/jpeg',
25122     buttons : false,
25123     canvasLoaded : false,
25124     isDocument : false,
25125     method : 'POST',
25126     paramName : 'imageUpload',
25127     loadMask : true,
25128     loadingText : 'Loading...',
25129     maskEl : false,
25130     
25131     getAutoCreate : function()
25132     {
25133         var cfg = {
25134             tag : 'div',
25135             cls : 'roo-upload-cropbox',
25136             cn : [
25137                 {
25138                     tag : 'input',
25139                     cls : 'roo-upload-cropbox-selector',
25140                     type : 'file'
25141                 },
25142                 {
25143                     tag : 'div',
25144                     cls : 'roo-upload-cropbox-body',
25145                     style : 'cursor:pointer',
25146                     cn : [
25147                         {
25148                             tag : 'div',
25149                             cls : 'roo-upload-cropbox-preview'
25150                         },
25151                         {
25152                             tag : 'div',
25153                             cls : 'roo-upload-cropbox-thumb'
25154                         },
25155                         {
25156                             tag : 'div',
25157                             cls : 'roo-upload-cropbox-empty-notify',
25158                             html : this.emptyText
25159                         },
25160                         {
25161                             tag : 'div',
25162                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25163                             html : this.rotateNotify
25164                         }
25165                     ]
25166                 },
25167                 {
25168                     tag : 'div',
25169                     cls : 'roo-upload-cropbox-footer',
25170                     cn : {
25171                         tag : 'div',
25172                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25173                         cn : []
25174                     }
25175                 }
25176             ]
25177         };
25178         
25179         return cfg;
25180     },
25181     
25182     onRender : function(ct, position)
25183     {
25184         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25185         
25186         if (this.buttons.length) {
25187             
25188             Roo.each(this.buttons, function(bb) {
25189                 
25190                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25191                 
25192                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25193                 
25194             }, this);
25195         }
25196         
25197         if(this.loadMask){
25198             this.maskEl = this.el;
25199         }
25200     },
25201     
25202     initEvents : function()
25203     {
25204         this.urlAPI = (window.createObjectURL && window) || 
25205                                 (window.URL && URL.revokeObjectURL && URL) || 
25206                                 (window.webkitURL && webkitURL);
25207                         
25208         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25209         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25210         
25211         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25212         this.selectorEl.hide();
25213         
25214         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25215         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25216         
25217         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25218         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25219         this.thumbEl.hide();
25220         
25221         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25222         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25223         
25224         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25225         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25226         this.errorEl.hide();
25227         
25228         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25229         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25230         this.footerEl.hide();
25231         
25232         this.setThumbBoxSize();
25233         
25234         this.bind();
25235         
25236         this.resize();
25237         
25238         this.fireEvent('initial', this);
25239     },
25240
25241     bind : function()
25242     {
25243         var _this = this;
25244         
25245         window.addEventListener("resize", function() { _this.resize(); } );
25246         
25247         this.bodyEl.on('click', this.beforeSelectFile, this);
25248         
25249         if(Roo.isTouch){
25250             this.bodyEl.on('touchstart', this.onTouchStart, this);
25251             this.bodyEl.on('touchmove', this.onTouchMove, this);
25252             this.bodyEl.on('touchend', this.onTouchEnd, this);
25253         }
25254         
25255         if(!Roo.isTouch){
25256             this.bodyEl.on('mousedown', this.onMouseDown, this);
25257             this.bodyEl.on('mousemove', this.onMouseMove, this);
25258             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25259             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25260             Roo.get(document).on('mouseup', this.onMouseUp, this);
25261         }
25262         
25263         this.selectorEl.on('change', this.onFileSelected, this);
25264     },
25265     
25266     reset : function()
25267     {    
25268         this.scale = 0;
25269         this.baseScale = 1;
25270         this.rotate = 0;
25271         this.baseRotate = 1;
25272         this.dragable = false;
25273         this.pinching = false;
25274         this.mouseX = 0;
25275         this.mouseY = 0;
25276         this.cropData = false;
25277         this.notifyEl.dom.innerHTML = this.emptyText;
25278         
25279         this.selectorEl.dom.value = '';
25280         
25281     },
25282     
25283     resize : function()
25284     {
25285         if(this.fireEvent('resize', this) != false){
25286             this.setThumbBoxPosition();
25287             this.setCanvasPosition();
25288         }
25289     },
25290     
25291     onFooterButtonClick : function(e, el, o, type)
25292     {
25293         switch (type) {
25294             case 'rotate-left' :
25295                 this.onRotateLeft(e);
25296                 break;
25297             case 'rotate-right' :
25298                 this.onRotateRight(e);
25299                 break;
25300             case 'picture' :
25301                 this.beforeSelectFile(e);
25302                 break;
25303             case 'trash' :
25304                 this.trash(e);
25305                 break;
25306             case 'crop' :
25307                 this.crop(e);
25308                 break;
25309             case 'download' :
25310                 this.download(e);
25311                 break;
25312             default :
25313                 break;
25314         }
25315         
25316         this.fireEvent('footerbuttonclick', this, type);
25317     },
25318     
25319     beforeSelectFile : function(e)
25320     {
25321         e.preventDefault();
25322         
25323         if(this.fireEvent('beforeselectfile', this) != false){
25324             this.selectorEl.dom.click();
25325         }
25326     },
25327     
25328     onFileSelected : function(e)
25329     {
25330         e.preventDefault();
25331         
25332         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25333             return;
25334         }
25335         
25336         var file = this.selectorEl.dom.files[0];
25337         
25338         if(this.fireEvent('inspect', this, file) != false){
25339             this.prepare(file);
25340         }
25341         
25342     },
25343     
25344     trash : function(e)
25345     {
25346         this.fireEvent('trash', this);
25347     },
25348     
25349     download : function(e)
25350     {
25351         this.fireEvent('download', this);
25352     },
25353     
25354     loadCanvas : function(src)
25355     {   
25356         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25357             
25358             this.reset();
25359             
25360             this.imageEl = document.createElement('img');
25361             
25362             var _this = this;
25363             
25364             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25365             
25366             this.imageEl.src = src;
25367         }
25368     },
25369     
25370     onLoadCanvas : function()
25371     {   
25372         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25373         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25374         
25375         this.bodyEl.un('click', this.beforeSelectFile, this);
25376         
25377         this.notifyEl.hide();
25378         this.thumbEl.show();
25379         this.footerEl.show();
25380         
25381         this.baseRotateLevel();
25382         
25383         if(this.isDocument){
25384             this.setThumbBoxSize();
25385         }
25386         
25387         this.setThumbBoxPosition();
25388         
25389         this.baseScaleLevel();
25390         
25391         this.draw();
25392         
25393         this.resize();
25394         
25395         this.canvasLoaded = true;
25396         
25397         if(this.loadMask){
25398             this.maskEl.unmask();
25399         }
25400         
25401     },
25402     
25403     setCanvasPosition : function()
25404     {   
25405         if(!this.canvasEl){
25406             return;
25407         }
25408         
25409         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25410         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25411         
25412         this.previewEl.setLeft(pw);
25413         this.previewEl.setTop(ph);
25414         
25415     },
25416     
25417     onMouseDown : function(e)
25418     {   
25419         e.stopEvent();
25420         
25421         this.dragable = true;
25422         this.pinching = false;
25423         
25424         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25425             this.dragable = false;
25426             return;
25427         }
25428         
25429         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25430         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25431         
25432     },
25433     
25434     onMouseMove : function(e)
25435     {   
25436         e.stopEvent();
25437         
25438         if(!this.canvasLoaded){
25439             return;
25440         }
25441         
25442         if (!this.dragable){
25443             return;
25444         }
25445         
25446         var minX = Math.ceil(this.thumbEl.getLeft(true));
25447         var minY = Math.ceil(this.thumbEl.getTop(true));
25448         
25449         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25450         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25451         
25452         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25453         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25454         
25455         x = x - this.mouseX;
25456         y = y - this.mouseY;
25457         
25458         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25459         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25460         
25461         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25462         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25463         
25464         this.previewEl.setLeft(bgX);
25465         this.previewEl.setTop(bgY);
25466         
25467         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25468         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25469     },
25470     
25471     onMouseUp : function(e)
25472     {   
25473         e.stopEvent();
25474         
25475         this.dragable = false;
25476     },
25477     
25478     onMouseWheel : function(e)
25479     {   
25480         e.stopEvent();
25481         
25482         this.startScale = this.scale;
25483         
25484         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25485         
25486         if(!this.zoomable()){
25487             this.scale = this.startScale;
25488             return;
25489         }
25490         
25491         this.draw();
25492         
25493         return;
25494     },
25495     
25496     zoomable : function()
25497     {
25498         var minScale = this.thumbEl.getWidth() / this.minWidth;
25499         
25500         if(this.minWidth < this.minHeight){
25501             minScale = this.thumbEl.getHeight() / this.minHeight;
25502         }
25503         
25504         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25505         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25506         
25507         if(
25508                 this.isDocument &&
25509                 (this.rotate == 0 || this.rotate == 180) && 
25510                 (
25511                     width > this.imageEl.OriginWidth || 
25512                     height > this.imageEl.OriginHeight ||
25513                     (width < this.minWidth && height < this.minHeight)
25514                 )
25515         ){
25516             return false;
25517         }
25518         
25519         if(
25520                 this.isDocument &&
25521                 (this.rotate == 90 || this.rotate == 270) && 
25522                 (
25523                     width > this.imageEl.OriginWidth || 
25524                     height > this.imageEl.OriginHeight ||
25525                     (width < this.minHeight && height < this.minWidth)
25526                 )
25527         ){
25528             return false;
25529         }
25530         
25531         if(
25532                 !this.isDocument &&
25533                 (this.rotate == 0 || this.rotate == 180) && 
25534                 (
25535                     width < this.minWidth || 
25536                     width > this.imageEl.OriginWidth || 
25537                     height < this.minHeight || 
25538                     height > this.imageEl.OriginHeight
25539                 )
25540         ){
25541             return false;
25542         }
25543         
25544         if(
25545                 !this.isDocument &&
25546                 (this.rotate == 90 || this.rotate == 270) && 
25547                 (
25548                     width < this.minHeight || 
25549                     width > this.imageEl.OriginWidth || 
25550                     height < this.minWidth || 
25551                     height > this.imageEl.OriginHeight
25552                 )
25553         ){
25554             return false;
25555         }
25556         
25557         return true;
25558         
25559     },
25560     
25561     onRotateLeft : function(e)
25562     {   
25563         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25564             
25565             var minScale = this.thumbEl.getWidth() / this.minWidth;
25566             
25567             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25568             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25569             
25570             this.startScale = this.scale;
25571             
25572             while (this.getScaleLevel() < minScale){
25573             
25574                 this.scale = this.scale + 1;
25575                 
25576                 if(!this.zoomable()){
25577                     break;
25578                 }
25579                 
25580                 if(
25581                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25582                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25583                 ){
25584                     continue;
25585                 }
25586                 
25587                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25588
25589                 this.draw();
25590                 
25591                 return;
25592             }
25593             
25594             this.scale = this.startScale;
25595             
25596             this.onRotateFail();
25597             
25598             return false;
25599         }
25600         
25601         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25602
25603         if(this.isDocument){
25604             this.setThumbBoxSize();
25605             this.setThumbBoxPosition();
25606             this.setCanvasPosition();
25607         }
25608         
25609         this.draw();
25610         
25611         this.fireEvent('rotate', this, 'left');
25612         
25613     },
25614     
25615     onRotateRight : function(e)
25616     {
25617         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25618             
25619             var minScale = this.thumbEl.getWidth() / this.minWidth;
25620         
25621             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25622             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25623             
25624             this.startScale = this.scale;
25625             
25626             while (this.getScaleLevel() < minScale){
25627             
25628                 this.scale = this.scale + 1;
25629                 
25630                 if(!this.zoomable()){
25631                     break;
25632                 }
25633                 
25634                 if(
25635                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25636                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25637                 ){
25638                     continue;
25639                 }
25640                 
25641                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25642
25643                 this.draw();
25644                 
25645                 return;
25646             }
25647             
25648             this.scale = this.startScale;
25649             
25650             this.onRotateFail();
25651             
25652             return false;
25653         }
25654         
25655         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25656
25657         if(this.isDocument){
25658             this.setThumbBoxSize();
25659             this.setThumbBoxPosition();
25660             this.setCanvasPosition();
25661         }
25662         
25663         this.draw();
25664         
25665         this.fireEvent('rotate', this, 'right');
25666     },
25667     
25668     onRotateFail : function()
25669     {
25670         this.errorEl.show(true);
25671         
25672         var _this = this;
25673         
25674         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25675     },
25676     
25677     draw : function()
25678     {
25679         this.previewEl.dom.innerHTML = '';
25680         
25681         var canvasEl = document.createElement("canvas");
25682         
25683         var contextEl = canvasEl.getContext("2d");
25684         
25685         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25686         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25687         var center = this.imageEl.OriginWidth / 2;
25688         
25689         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25690             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25691             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25692             center = this.imageEl.OriginHeight / 2;
25693         }
25694         
25695         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25696         
25697         contextEl.translate(center, center);
25698         contextEl.rotate(this.rotate * Math.PI / 180);
25699
25700         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25701         
25702         this.canvasEl = document.createElement("canvas");
25703         
25704         this.contextEl = this.canvasEl.getContext("2d");
25705         
25706         switch (this.rotate) {
25707             case 0 :
25708                 
25709                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25710                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25711                 
25712                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25713                 
25714                 break;
25715             case 90 : 
25716                 
25717                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25718                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25719                 
25720                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25721                     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);
25722                     break;
25723                 }
25724                 
25725                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25726                 
25727                 break;
25728             case 180 :
25729                 
25730                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25731                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25732                 
25733                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25734                     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);
25735                     break;
25736                 }
25737                 
25738                 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);
25739                 
25740                 break;
25741             case 270 :
25742                 
25743                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25744                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25745         
25746                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25747                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25748                     break;
25749                 }
25750                 
25751                 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);
25752                 
25753                 break;
25754             default : 
25755                 break;
25756         }
25757         
25758         this.previewEl.appendChild(this.canvasEl);
25759         
25760         this.setCanvasPosition();
25761     },
25762     
25763     crop : function()
25764     {
25765         if(!this.canvasLoaded){
25766             return;
25767         }
25768         
25769         var imageCanvas = document.createElement("canvas");
25770         
25771         var imageContext = imageCanvas.getContext("2d");
25772         
25773         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25774         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25775         
25776         var center = imageCanvas.width / 2;
25777         
25778         imageContext.translate(center, center);
25779         
25780         imageContext.rotate(this.rotate * Math.PI / 180);
25781         
25782         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25783         
25784         var canvas = document.createElement("canvas");
25785         
25786         var context = canvas.getContext("2d");
25787                 
25788         canvas.width = this.minWidth;
25789         canvas.height = this.minHeight;
25790
25791         switch (this.rotate) {
25792             case 0 :
25793                 
25794                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25795                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25796                 
25797                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25798                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25799                 
25800                 var targetWidth = this.minWidth - 2 * x;
25801                 var targetHeight = this.minHeight - 2 * y;
25802                 
25803                 var scale = 1;
25804                 
25805                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25806                     scale = targetWidth / width;
25807                 }
25808                 
25809                 if(x > 0 && y == 0){
25810                     scale = targetHeight / height;
25811                 }
25812                 
25813                 if(x > 0 && y > 0){
25814                     scale = targetWidth / width;
25815                     
25816                     if(width < height){
25817                         scale = targetHeight / height;
25818                     }
25819                 }
25820                 
25821                 context.scale(scale, scale);
25822                 
25823                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25824                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25825
25826                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25827                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25828
25829                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25830                 
25831                 break;
25832             case 90 : 
25833                 
25834                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25835                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25836                 
25837                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25838                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25839                 
25840                 var targetWidth = this.minWidth - 2 * x;
25841                 var targetHeight = this.minHeight - 2 * y;
25842                 
25843                 var scale = 1;
25844                 
25845                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25846                     scale = targetWidth / width;
25847                 }
25848                 
25849                 if(x > 0 && y == 0){
25850                     scale = targetHeight / height;
25851                 }
25852                 
25853                 if(x > 0 && y > 0){
25854                     scale = targetWidth / width;
25855                     
25856                     if(width < height){
25857                         scale = targetHeight / height;
25858                     }
25859                 }
25860                 
25861                 context.scale(scale, scale);
25862                 
25863                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25864                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25865
25866                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25867                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25868                 
25869                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25870                 
25871                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25872                 
25873                 break;
25874             case 180 :
25875                 
25876                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25877                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25878                 
25879                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25880                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25881                 
25882                 var targetWidth = this.minWidth - 2 * x;
25883                 var targetHeight = this.minHeight - 2 * y;
25884                 
25885                 var scale = 1;
25886                 
25887                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25888                     scale = targetWidth / width;
25889                 }
25890                 
25891                 if(x > 0 && y == 0){
25892                     scale = targetHeight / height;
25893                 }
25894                 
25895                 if(x > 0 && y > 0){
25896                     scale = targetWidth / width;
25897                     
25898                     if(width < height){
25899                         scale = targetHeight / height;
25900                     }
25901                 }
25902                 
25903                 context.scale(scale, scale);
25904                 
25905                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25906                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25907
25908                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25909                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25910
25911                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25912                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25913                 
25914                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25915                 
25916                 break;
25917             case 270 :
25918                 
25919                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25920                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25921                 
25922                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25923                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25924                 
25925                 var targetWidth = this.minWidth - 2 * x;
25926                 var targetHeight = this.minHeight - 2 * y;
25927                 
25928                 var scale = 1;
25929                 
25930                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25931                     scale = targetWidth / width;
25932                 }
25933                 
25934                 if(x > 0 && y == 0){
25935                     scale = targetHeight / height;
25936                 }
25937                 
25938                 if(x > 0 && y > 0){
25939                     scale = targetWidth / width;
25940                     
25941                     if(width < height){
25942                         scale = targetHeight / height;
25943                     }
25944                 }
25945                 
25946                 context.scale(scale, scale);
25947                 
25948                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25949                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25950
25951                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25952                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25953                 
25954                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25955                 
25956                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25957                 
25958                 break;
25959             default : 
25960                 break;
25961         }
25962         
25963         this.cropData = canvas.toDataURL(this.cropType);
25964         
25965         if(this.fireEvent('crop', this, this.cropData) !== false){
25966             this.process(this.file, this.cropData);
25967         }
25968         
25969         return;
25970         
25971     },
25972     
25973     setThumbBoxSize : function()
25974     {
25975         var width, height;
25976         
25977         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25978             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25979             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25980             
25981             this.minWidth = width;
25982             this.minHeight = height;
25983             
25984             if(this.rotate == 90 || this.rotate == 270){
25985                 this.minWidth = height;
25986                 this.minHeight = width;
25987             }
25988         }
25989         
25990         height = 300;
25991         width = Math.ceil(this.minWidth * height / this.minHeight);
25992         
25993         if(this.minWidth > this.minHeight){
25994             width = 300;
25995             height = Math.ceil(this.minHeight * width / this.minWidth);
25996         }
25997         
25998         this.thumbEl.setStyle({
25999             width : width + 'px',
26000             height : height + 'px'
26001         });
26002
26003         return;
26004             
26005     },
26006     
26007     setThumbBoxPosition : function()
26008     {
26009         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26010         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26011         
26012         this.thumbEl.setLeft(x);
26013         this.thumbEl.setTop(y);
26014         
26015     },
26016     
26017     baseRotateLevel : function()
26018     {
26019         this.baseRotate = 1;
26020         
26021         if(
26022                 typeof(this.exif) != 'undefined' &&
26023                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26024                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26025         ){
26026             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26027         }
26028         
26029         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26030         
26031     },
26032     
26033     baseScaleLevel : function()
26034     {
26035         var width, height;
26036         
26037         if(this.isDocument){
26038             
26039             if(this.baseRotate == 6 || this.baseRotate == 8){
26040             
26041                 height = this.thumbEl.getHeight();
26042                 this.baseScale = height / this.imageEl.OriginWidth;
26043
26044                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26045                     width = this.thumbEl.getWidth();
26046                     this.baseScale = width / this.imageEl.OriginHeight;
26047                 }
26048
26049                 return;
26050             }
26051
26052             height = this.thumbEl.getHeight();
26053             this.baseScale = height / this.imageEl.OriginHeight;
26054
26055             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26056                 width = this.thumbEl.getWidth();
26057                 this.baseScale = width / this.imageEl.OriginWidth;
26058             }
26059
26060             return;
26061         }
26062         
26063         if(this.baseRotate == 6 || this.baseRotate == 8){
26064             
26065             width = this.thumbEl.getHeight();
26066             this.baseScale = width / this.imageEl.OriginHeight;
26067             
26068             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26069                 height = this.thumbEl.getWidth();
26070                 this.baseScale = height / this.imageEl.OriginHeight;
26071             }
26072             
26073             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26074                 height = this.thumbEl.getWidth();
26075                 this.baseScale = height / this.imageEl.OriginHeight;
26076                 
26077                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26078                     width = this.thumbEl.getHeight();
26079                     this.baseScale = width / this.imageEl.OriginWidth;
26080                 }
26081             }
26082             
26083             return;
26084         }
26085         
26086         width = this.thumbEl.getWidth();
26087         this.baseScale = width / this.imageEl.OriginWidth;
26088         
26089         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26090             height = this.thumbEl.getHeight();
26091             this.baseScale = height / this.imageEl.OriginHeight;
26092         }
26093         
26094         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26095             
26096             height = this.thumbEl.getHeight();
26097             this.baseScale = height / this.imageEl.OriginHeight;
26098             
26099             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26100                 width = this.thumbEl.getWidth();
26101                 this.baseScale = width / this.imageEl.OriginWidth;
26102             }
26103             
26104         }
26105         
26106         return;
26107     },
26108     
26109     getScaleLevel : function()
26110     {
26111         return this.baseScale * Math.pow(1.1, this.scale);
26112     },
26113     
26114     onTouchStart : function(e)
26115     {
26116         if(!this.canvasLoaded){
26117             this.beforeSelectFile(e);
26118             return;
26119         }
26120         
26121         var touches = e.browserEvent.touches;
26122         
26123         if(!touches){
26124             return;
26125         }
26126         
26127         if(touches.length == 1){
26128             this.onMouseDown(e);
26129             return;
26130         }
26131         
26132         if(touches.length != 2){
26133             return;
26134         }
26135         
26136         var coords = [];
26137         
26138         for(var i = 0, finger; finger = touches[i]; i++){
26139             coords.push(finger.pageX, finger.pageY);
26140         }
26141         
26142         var x = Math.pow(coords[0] - coords[2], 2);
26143         var y = Math.pow(coords[1] - coords[3], 2);
26144         
26145         this.startDistance = Math.sqrt(x + y);
26146         
26147         this.startScale = this.scale;
26148         
26149         this.pinching = true;
26150         this.dragable = false;
26151         
26152     },
26153     
26154     onTouchMove : function(e)
26155     {
26156         if(!this.pinching && !this.dragable){
26157             return;
26158         }
26159         
26160         var touches = e.browserEvent.touches;
26161         
26162         if(!touches){
26163             return;
26164         }
26165         
26166         if(this.dragable){
26167             this.onMouseMove(e);
26168             return;
26169         }
26170         
26171         var coords = [];
26172         
26173         for(var i = 0, finger; finger = touches[i]; i++){
26174             coords.push(finger.pageX, finger.pageY);
26175         }
26176         
26177         var x = Math.pow(coords[0] - coords[2], 2);
26178         var y = Math.pow(coords[1] - coords[3], 2);
26179         
26180         this.endDistance = Math.sqrt(x + y);
26181         
26182         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26183         
26184         if(!this.zoomable()){
26185             this.scale = this.startScale;
26186             return;
26187         }
26188         
26189         this.draw();
26190         
26191     },
26192     
26193     onTouchEnd : function(e)
26194     {
26195         this.pinching = false;
26196         this.dragable = false;
26197         
26198     },
26199     
26200     process : function(file, crop)
26201     {
26202         if(this.loadMask){
26203             this.maskEl.mask(this.loadingText);
26204         }
26205         
26206         this.xhr = new XMLHttpRequest();
26207         
26208         file.xhr = this.xhr;
26209
26210         this.xhr.open(this.method, this.url, true);
26211         
26212         var headers = {
26213             "Accept": "application/json",
26214             "Cache-Control": "no-cache",
26215             "X-Requested-With": "XMLHttpRequest"
26216         };
26217         
26218         for (var headerName in headers) {
26219             var headerValue = headers[headerName];
26220             if (headerValue) {
26221                 this.xhr.setRequestHeader(headerName, headerValue);
26222             }
26223         }
26224         
26225         var _this = this;
26226         
26227         this.xhr.onload = function()
26228         {
26229             _this.xhrOnLoad(_this.xhr);
26230         }
26231         
26232         this.xhr.onerror = function()
26233         {
26234             _this.xhrOnError(_this.xhr);
26235         }
26236         
26237         var formData = new FormData();
26238
26239         formData.append('returnHTML', 'NO');
26240         
26241         if(crop){
26242             formData.append('crop', crop);
26243         }
26244         
26245         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26246             formData.append(this.paramName, file, file.name);
26247         }
26248         
26249         if(typeof(file.filename) != 'undefined'){
26250             formData.append('filename', file.filename);
26251         }
26252         
26253         if(typeof(file.mimetype) != 'undefined'){
26254             formData.append('mimetype', file.mimetype);
26255         }
26256         
26257         if(this.fireEvent('arrange', this, formData) != false){
26258             this.xhr.send(formData);
26259         };
26260     },
26261     
26262     xhrOnLoad : function(xhr)
26263     {
26264         if(this.loadMask){
26265             this.maskEl.unmask();
26266         }
26267         
26268         if (xhr.readyState !== 4) {
26269             this.fireEvent('exception', this, xhr);
26270             return;
26271         }
26272
26273         var response = Roo.decode(xhr.responseText);
26274         
26275         if(!response.success){
26276             this.fireEvent('exception', this, xhr);
26277             return;
26278         }
26279         
26280         var response = Roo.decode(xhr.responseText);
26281         
26282         this.fireEvent('upload', this, response);
26283         
26284     },
26285     
26286     xhrOnError : function()
26287     {
26288         if(this.loadMask){
26289             this.maskEl.unmask();
26290         }
26291         
26292         Roo.log('xhr on error');
26293         
26294         var response = Roo.decode(xhr.responseText);
26295           
26296         Roo.log(response);
26297         
26298     },
26299     
26300     prepare : function(file)
26301     {   
26302         if(this.loadMask){
26303             this.maskEl.mask(this.loadingText);
26304         }
26305         
26306         this.file = false;
26307         this.exif = {};
26308         
26309         if(typeof(file) === 'string'){
26310             this.loadCanvas(file);
26311             return;
26312         }
26313         
26314         if(!file || !this.urlAPI){
26315             return;
26316         }
26317         
26318         this.file = file;
26319         this.cropType = file.type;
26320         
26321         var _this = this;
26322         
26323         if(this.fireEvent('prepare', this, this.file) != false){
26324             
26325             var reader = new FileReader();
26326             
26327             reader.onload = function (e) {
26328                 if (e.target.error) {
26329                     Roo.log(e.target.error);
26330                     return;
26331                 }
26332                 
26333                 var buffer = e.target.result,
26334                     dataView = new DataView(buffer),
26335                     offset = 2,
26336                     maxOffset = dataView.byteLength - 4,
26337                     markerBytes,
26338                     markerLength;
26339                 
26340                 if (dataView.getUint16(0) === 0xffd8) {
26341                     while (offset < maxOffset) {
26342                         markerBytes = dataView.getUint16(offset);
26343                         
26344                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26345                             markerLength = dataView.getUint16(offset + 2) + 2;
26346                             if (offset + markerLength > dataView.byteLength) {
26347                                 Roo.log('Invalid meta data: Invalid segment size.');
26348                                 break;
26349                             }
26350                             
26351                             if(markerBytes == 0xffe1){
26352                                 _this.parseExifData(
26353                                     dataView,
26354                                     offset,
26355                                     markerLength
26356                                 );
26357                             }
26358                             
26359                             offset += markerLength;
26360                             
26361                             continue;
26362                         }
26363                         
26364                         break;
26365                     }
26366                     
26367                 }
26368                 
26369                 var url = _this.urlAPI.createObjectURL(_this.file);
26370                 
26371                 _this.loadCanvas(url);
26372                 
26373                 return;
26374             }
26375             
26376             reader.readAsArrayBuffer(this.file);
26377             
26378         }
26379         
26380     },
26381     
26382     parseExifData : function(dataView, offset, length)
26383     {
26384         var tiffOffset = offset + 10,
26385             littleEndian,
26386             dirOffset;
26387     
26388         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26389             // No Exif data, might be XMP data instead
26390             return;
26391         }
26392         
26393         // Check for the ASCII code for "Exif" (0x45786966):
26394         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26395             // No Exif data, might be XMP data instead
26396             return;
26397         }
26398         if (tiffOffset + 8 > dataView.byteLength) {
26399             Roo.log('Invalid Exif data: Invalid segment size.');
26400             return;
26401         }
26402         // Check for the two null bytes:
26403         if (dataView.getUint16(offset + 8) !== 0x0000) {
26404             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26405             return;
26406         }
26407         // Check the byte alignment:
26408         switch (dataView.getUint16(tiffOffset)) {
26409         case 0x4949:
26410             littleEndian = true;
26411             break;
26412         case 0x4D4D:
26413             littleEndian = false;
26414             break;
26415         default:
26416             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26417             return;
26418         }
26419         // Check for the TIFF tag marker (0x002A):
26420         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26421             Roo.log('Invalid Exif data: Missing TIFF marker.');
26422             return;
26423         }
26424         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26425         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26426         
26427         this.parseExifTags(
26428             dataView,
26429             tiffOffset,
26430             tiffOffset + dirOffset,
26431             littleEndian
26432         );
26433     },
26434     
26435     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26436     {
26437         var tagsNumber,
26438             dirEndOffset,
26439             i;
26440         if (dirOffset + 6 > dataView.byteLength) {
26441             Roo.log('Invalid Exif data: Invalid directory offset.');
26442             return;
26443         }
26444         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26445         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26446         if (dirEndOffset + 4 > dataView.byteLength) {
26447             Roo.log('Invalid Exif data: Invalid directory size.');
26448             return;
26449         }
26450         for (i = 0; i < tagsNumber; i += 1) {
26451             this.parseExifTag(
26452                 dataView,
26453                 tiffOffset,
26454                 dirOffset + 2 + 12 * i, // tag offset
26455                 littleEndian
26456             );
26457         }
26458         // Return the offset to the next directory:
26459         return dataView.getUint32(dirEndOffset, littleEndian);
26460     },
26461     
26462     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26463     {
26464         var tag = dataView.getUint16(offset, littleEndian);
26465         
26466         this.exif[tag] = this.getExifValue(
26467             dataView,
26468             tiffOffset,
26469             offset,
26470             dataView.getUint16(offset + 2, littleEndian), // tag type
26471             dataView.getUint32(offset + 4, littleEndian), // tag length
26472             littleEndian
26473         );
26474     },
26475     
26476     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26477     {
26478         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26479             tagSize,
26480             dataOffset,
26481             values,
26482             i,
26483             str,
26484             c;
26485     
26486         if (!tagType) {
26487             Roo.log('Invalid Exif data: Invalid tag type.');
26488             return;
26489         }
26490         
26491         tagSize = tagType.size * length;
26492         // Determine if the value is contained in the dataOffset bytes,
26493         // or if the value at the dataOffset is a pointer to the actual data:
26494         dataOffset = tagSize > 4 ?
26495                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26496         if (dataOffset + tagSize > dataView.byteLength) {
26497             Roo.log('Invalid Exif data: Invalid data offset.');
26498             return;
26499         }
26500         if (length === 1) {
26501             return tagType.getValue(dataView, dataOffset, littleEndian);
26502         }
26503         values = [];
26504         for (i = 0; i < length; i += 1) {
26505             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26506         }
26507         
26508         if (tagType.ascii) {
26509             str = '';
26510             // Concatenate the chars:
26511             for (i = 0; i < values.length; i += 1) {
26512                 c = values[i];
26513                 // Ignore the terminating NULL byte(s):
26514                 if (c === '\u0000') {
26515                     break;
26516                 }
26517                 str += c;
26518             }
26519             return str;
26520         }
26521         return values;
26522     }
26523     
26524 });
26525
26526 Roo.apply(Roo.bootstrap.UploadCropbox, {
26527     tags : {
26528         'Orientation': 0x0112
26529     },
26530     
26531     Orientation: {
26532             1: 0, //'top-left',
26533 //            2: 'top-right',
26534             3: 180, //'bottom-right',
26535 //            4: 'bottom-left',
26536 //            5: 'left-top',
26537             6: 90, //'right-top',
26538 //            7: 'right-bottom',
26539             8: 270 //'left-bottom'
26540     },
26541     
26542     exifTagTypes : {
26543         // byte, 8-bit unsigned int:
26544         1: {
26545             getValue: function (dataView, dataOffset) {
26546                 return dataView.getUint8(dataOffset);
26547             },
26548             size: 1
26549         },
26550         // ascii, 8-bit byte:
26551         2: {
26552             getValue: function (dataView, dataOffset) {
26553                 return String.fromCharCode(dataView.getUint8(dataOffset));
26554             },
26555             size: 1,
26556             ascii: true
26557         },
26558         // short, 16 bit int:
26559         3: {
26560             getValue: function (dataView, dataOffset, littleEndian) {
26561                 return dataView.getUint16(dataOffset, littleEndian);
26562             },
26563             size: 2
26564         },
26565         // long, 32 bit int:
26566         4: {
26567             getValue: function (dataView, dataOffset, littleEndian) {
26568                 return dataView.getUint32(dataOffset, littleEndian);
26569             },
26570             size: 4
26571         },
26572         // rational = two long values, first is numerator, second is denominator:
26573         5: {
26574             getValue: function (dataView, dataOffset, littleEndian) {
26575                 return dataView.getUint32(dataOffset, littleEndian) /
26576                     dataView.getUint32(dataOffset + 4, littleEndian);
26577             },
26578             size: 8
26579         },
26580         // slong, 32 bit signed int:
26581         9: {
26582             getValue: function (dataView, dataOffset, littleEndian) {
26583                 return dataView.getInt32(dataOffset, littleEndian);
26584             },
26585             size: 4
26586         },
26587         // srational, two slongs, first is numerator, second is denominator:
26588         10: {
26589             getValue: function (dataView, dataOffset, littleEndian) {
26590                 return dataView.getInt32(dataOffset, littleEndian) /
26591                     dataView.getInt32(dataOffset + 4, littleEndian);
26592             },
26593             size: 8
26594         }
26595     },
26596     
26597     footer : {
26598         STANDARD : [
26599             {
26600                 tag : 'div',
26601                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26602                 action : 'rotate-left',
26603                 cn : [
26604                     {
26605                         tag : 'button',
26606                         cls : 'btn btn-default',
26607                         html : '<i class="fa fa-undo"></i>'
26608                     }
26609                 ]
26610             },
26611             {
26612                 tag : 'div',
26613                 cls : 'btn-group roo-upload-cropbox-picture',
26614                 action : 'picture',
26615                 cn : [
26616                     {
26617                         tag : 'button',
26618                         cls : 'btn btn-default',
26619                         html : '<i class="fa fa-picture-o"></i>'
26620                     }
26621                 ]
26622             },
26623             {
26624                 tag : 'div',
26625                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26626                 action : 'rotate-right',
26627                 cn : [
26628                     {
26629                         tag : 'button',
26630                         cls : 'btn btn-default',
26631                         html : '<i class="fa fa-repeat"></i>'
26632                     }
26633                 ]
26634             }
26635         ],
26636         DOCUMENT : [
26637             {
26638                 tag : 'div',
26639                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26640                 action : 'rotate-left',
26641                 cn : [
26642                     {
26643                         tag : 'button',
26644                         cls : 'btn btn-default',
26645                         html : '<i class="fa fa-undo"></i>'
26646                     }
26647                 ]
26648             },
26649             {
26650                 tag : 'div',
26651                 cls : 'btn-group roo-upload-cropbox-download',
26652                 action : 'download',
26653                 cn : [
26654                     {
26655                         tag : 'button',
26656                         cls : 'btn btn-default',
26657                         html : '<i class="fa fa-download"></i>'
26658                     }
26659                 ]
26660             },
26661             {
26662                 tag : 'div',
26663                 cls : 'btn-group roo-upload-cropbox-crop',
26664                 action : 'crop',
26665                 cn : [
26666                     {
26667                         tag : 'button',
26668                         cls : 'btn btn-default',
26669                         html : '<i class="fa fa-crop"></i>'
26670                     }
26671                 ]
26672             },
26673             {
26674                 tag : 'div',
26675                 cls : 'btn-group roo-upload-cropbox-trash',
26676                 action : 'trash',
26677                 cn : [
26678                     {
26679                         tag : 'button',
26680                         cls : 'btn btn-default',
26681                         html : '<i class="fa fa-trash"></i>'
26682                     }
26683                 ]
26684             },
26685             {
26686                 tag : 'div',
26687                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26688                 action : 'rotate-right',
26689                 cn : [
26690                     {
26691                         tag : 'button',
26692                         cls : 'btn btn-default',
26693                         html : '<i class="fa fa-repeat"></i>'
26694                     }
26695                 ]
26696             }
26697         ],
26698         ROTATOR : [
26699             {
26700                 tag : 'div',
26701                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26702                 action : 'rotate-left',
26703                 cn : [
26704                     {
26705                         tag : 'button',
26706                         cls : 'btn btn-default',
26707                         html : '<i class="fa fa-undo"></i>'
26708                     }
26709                 ]
26710             },
26711             {
26712                 tag : 'div',
26713                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26714                 action : 'rotate-right',
26715                 cn : [
26716                     {
26717                         tag : 'button',
26718                         cls : 'btn btn-default',
26719                         html : '<i class="fa fa-repeat"></i>'
26720                     }
26721                 ]
26722             }
26723         ]
26724     }
26725 });
26726
26727 /*
26728 * Licence: LGPL
26729 */
26730
26731 /**
26732  * @class Roo.bootstrap.DocumentManager
26733  * @extends Roo.bootstrap.Component
26734  * Bootstrap DocumentManager class
26735  * @cfg {String} paramName default 'imageUpload'
26736  * @cfg {String} method default POST
26737  * @cfg {String} url action url
26738  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26739  * @cfg {Boolean} multiple multiple upload default true
26740  * @cfg {Number} thumbSize default 300
26741  * @cfg {String} fieldLabel
26742  * @cfg {Number} labelWidth default 4
26743  * @cfg {String} labelAlign (left|top) default left
26744  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26745  * 
26746  * @constructor
26747  * Create a new DocumentManager
26748  * @param {Object} config The config object
26749  */
26750
26751 Roo.bootstrap.DocumentManager = function(config){
26752     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26753     
26754     this.addEvents({
26755         /**
26756          * @event initial
26757          * Fire when initial the DocumentManager
26758          * @param {Roo.bootstrap.DocumentManager} this
26759          */
26760         "initial" : true,
26761         /**
26762          * @event inspect
26763          * inspect selected file
26764          * @param {Roo.bootstrap.DocumentManager} this
26765          * @param {File} file
26766          */
26767         "inspect" : true,
26768         /**
26769          * @event exception
26770          * Fire when xhr load exception
26771          * @param {Roo.bootstrap.DocumentManager} this
26772          * @param {XMLHttpRequest} xhr
26773          */
26774         "exception" : true,
26775         /**
26776          * @event prepare
26777          * prepare the form data
26778          * @param {Roo.bootstrap.DocumentManager} this
26779          * @param {Object} formData
26780          */
26781         "prepare" : true,
26782         /**
26783          * @event remove
26784          * Fire when remove the file
26785          * @param {Roo.bootstrap.DocumentManager} this
26786          * @param {Object} file
26787          */
26788         "remove" : true,
26789         /**
26790          * @event refresh
26791          * Fire after refresh the file
26792          * @param {Roo.bootstrap.DocumentManager} this
26793          */
26794         "refresh" : true,
26795         /**
26796          * @event click
26797          * Fire after click the image
26798          * @param {Roo.bootstrap.DocumentManager} this
26799          * @param {Object} file
26800          */
26801         "click" : true,
26802         /**
26803          * @event edit
26804          * Fire when upload a image and editable set to true
26805          * @param {Roo.bootstrap.DocumentManager} this
26806          * @param {Object} file
26807          */
26808         "edit" : true,
26809         /**
26810          * @event beforeselectfile
26811          * Fire before select file
26812          * @param {Roo.bootstrap.DocumentManager} this
26813          */
26814         "beforeselectfile" : true,
26815         /**
26816          * @event process
26817          * Fire before process file
26818          * @param {Roo.bootstrap.DocumentManager} this
26819          * @param {Object} file
26820          */
26821         "process" : true
26822         
26823     });
26824 };
26825
26826 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26827     
26828     boxes : 0,
26829     inputName : '',
26830     thumbSize : 300,
26831     multiple : true,
26832     files : [],
26833     method : 'POST',
26834     url : '',
26835     paramName : 'imageUpload',
26836     fieldLabel : '',
26837     labelWidth : 4,
26838     labelAlign : 'left',
26839     editable : true,
26840     delegates : [],
26841     
26842     
26843     xhr : false, 
26844     
26845     getAutoCreate : function()
26846     {   
26847         var managerWidget = {
26848             tag : 'div',
26849             cls : 'roo-document-manager',
26850             cn : [
26851                 {
26852                     tag : 'input',
26853                     cls : 'roo-document-manager-selector',
26854                     type : 'file'
26855                 },
26856                 {
26857                     tag : 'div',
26858                     cls : 'roo-document-manager-uploader',
26859                     cn : [
26860                         {
26861                             tag : 'div',
26862                             cls : 'roo-document-manager-upload-btn',
26863                             html : '<i class="fa fa-plus"></i>'
26864                         }
26865                     ]
26866                     
26867                 }
26868             ]
26869         };
26870         
26871         var content = [
26872             {
26873                 tag : 'div',
26874                 cls : 'column col-md-12',
26875                 cn : managerWidget
26876             }
26877         ];
26878         
26879         if(this.fieldLabel.length){
26880             
26881             content = [
26882                 {
26883                     tag : 'div',
26884                     cls : 'column col-md-12',
26885                     html : this.fieldLabel
26886                 },
26887                 {
26888                     tag : 'div',
26889                     cls : 'column col-md-12',
26890                     cn : managerWidget
26891                 }
26892             ];
26893
26894             if(this.labelAlign == 'left'){
26895                 content = [
26896                     {
26897                         tag : 'div',
26898                         cls : 'column col-md-' + this.labelWidth,
26899                         html : this.fieldLabel
26900                     },
26901                     {
26902                         tag : 'div',
26903                         cls : 'column col-md-' + (12 - this.labelWidth),
26904                         cn : managerWidget
26905                     }
26906                 ];
26907                 
26908             }
26909         }
26910         
26911         var cfg = {
26912             tag : 'div',
26913             cls : 'row clearfix',
26914             cn : content
26915         };
26916         
26917         return cfg;
26918         
26919     },
26920     
26921     initEvents : function()
26922     {
26923         this.managerEl = this.el.select('.roo-document-manager', true).first();
26924         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26925         
26926         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26927         this.selectorEl.hide();
26928         
26929         if(this.multiple){
26930             this.selectorEl.attr('multiple', 'multiple');
26931         }
26932         
26933         this.selectorEl.on('change', this.onFileSelected, this);
26934         
26935         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26936         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26937         
26938         this.uploader.on('click', this.onUploaderClick, this);
26939         
26940         this.renderProgressDialog();
26941         
26942         var _this = this;
26943         
26944         window.addEventListener("resize", function() { _this.refresh(); } );
26945         
26946         this.fireEvent('initial', this);
26947     },
26948     
26949     renderProgressDialog : function()
26950     {
26951         var _this = this;
26952         
26953         this.progressDialog = new Roo.bootstrap.Modal({
26954             cls : 'roo-document-manager-progress-dialog',
26955             allow_close : false,
26956             title : '',
26957             buttons : [
26958                 {
26959                     name  :'cancel',
26960                     weight : 'danger',
26961                     html : 'Cancel'
26962                 }
26963             ], 
26964             listeners : { 
26965                 btnclick : function() {
26966                     _this.uploadCancel();
26967                     this.hide();
26968                 }
26969             }
26970         });
26971          
26972         this.progressDialog.render(Roo.get(document.body));
26973          
26974         this.progress = new Roo.bootstrap.Progress({
26975             cls : 'roo-document-manager-progress',
26976             active : true,
26977             striped : true
26978         });
26979         
26980         this.progress.render(this.progressDialog.getChildContainer());
26981         
26982         this.progressBar = new Roo.bootstrap.ProgressBar({
26983             cls : 'roo-document-manager-progress-bar',
26984             aria_valuenow : 0,
26985             aria_valuemin : 0,
26986             aria_valuemax : 12,
26987             panel : 'success'
26988         });
26989         
26990         this.progressBar.render(this.progress.getChildContainer());
26991     },
26992     
26993     onUploaderClick : function(e)
26994     {
26995         e.preventDefault();
26996      
26997         if(this.fireEvent('beforeselectfile', this) != false){
26998             this.selectorEl.dom.click();
26999         }
27000         
27001     },
27002     
27003     onFileSelected : function(e)
27004     {
27005         e.preventDefault();
27006         
27007         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27008             return;
27009         }
27010         
27011         Roo.each(this.selectorEl.dom.files, function(file){
27012             if(this.fireEvent('inspect', this, file) != false){
27013                 this.files.push(file);
27014             }
27015         }, this);
27016         
27017         this.queue();
27018         
27019     },
27020     
27021     queue : function()
27022     {
27023         this.selectorEl.dom.value = '';
27024         
27025         if(!this.files.length){
27026             return;
27027         }
27028         
27029         if(this.boxes > 0 && this.files.length > this.boxes){
27030             this.files = this.files.slice(0, this.boxes);
27031         }
27032         
27033         this.uploader.show();
27034         
27035         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27036             this.uploader.hide();
27037         }
27038         
27039         var _this = this;
27040         
27041         var files = [];
27042         
27043         var docs = [];
27044         
27045         Roo.each(this.files, function(file){
27046             
27047             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27048                 var f = this.renderPreview(file);
27049                 files.push(f);
27050                 return;
27051             }
27052             
27053             if(file.type.indexOf('image') != -1){
27054                 this.delegates.push(
27055                     (function(){
27056                         _this.process(file);
27057                     }).createDelegate(this)
27058                 );
27059         
27060                 return;
27061             }
27062             
27063             docs.push(
27064                 (function(){
27065                     _this.process(file);
27066                 }).createDelegate(this)
27067             );
27068             
27069         }, this);
27070         
27071         this.files = files;
27072         
27073         this.delegates = this.delegates.concat(docs);
27074         
27075         if(!this.delegates.length){
27076             this.refresh();
27077             return;
27078         }
27079         
27080         this.progressBar.aria_valuemax = this.delegates.length;
27081         
27082         this.arrange();
27083         
27084         return;
27085     },
27086     
27087     arrange : function()
27088     {
27089         if(!this.delegates.length){
27090             this.progressDialog.hide();
27091             this.refresh();
27092             return;
27093         }
27094         
27095         var delegate = this.delegates.shift();
27096         
27097         this.progressDialog.show();
27098         
27099         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27100         
27101         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27102         
27103         delegate();
27104     },
27105     
27106     refresh : function()
27107     {
27108         this.uploader.show();
27109         
27110         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27111             this.uploader.hide();
27112         }
27113         
27114         Roo.isTouch ? this.closable(false) : this.closable(true);
27115         
27116         this.fireEvent('refresh', this);
27117     },
27118     
27119     onRemove : function(e, el, o)
27120     {
27121         e.preventDefault();
27122         
27123         this.fireEvent('remove', this, o);
27124         
27125     },
27126     
27127     remove : function(o)
27128     {
27129         var files = [];
27130         
27131         Roo.each(this.files, function(file){
27132             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27133                 files.push(file);
27134                 return;
27135             }
27136
27137             o.target.remove();
27138
27139         }, this);
27140         
27141         this.files = files;
27142         
27143         this.refresh();
27144     },
27145     
27146     clear : function()
27147     {
27148         Roo.each(this.files, function(file){
27149             if(!file.target){
27150                 return;
27151             }
27152             
27153             file.target.remove();
27154
27155         }, this);
27156         
27157         this.files = [];
27158         
27159         this.refresh();
27160     },
27161     
27162     onClick : function(e, el, o)
27163     {
27164         e.preventDefault();
27165         
27166         this.fireEvent('click', this, o);
27167         
27168     },
27169     
27170     closable : function(closable)
27171     {
27172         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27173             
27174             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27175             
27176             if(closable){
27177                 el.show();
27178                 return;
27179             }
27180             
27181             el.hide();
27182             
27183         }, this);
27184     },
27185     
27186     xhrOnLoad : function(xhr)
27187     {
27188         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27189             el.remove();
27190         }, this);
27191         
27192         if (xhr.readyState !== 4) {
27193             this.arrange();
27194             this.fireEvent('exception', this, xhr);
27195             return;
27196         }
27197
27198         var response = Roo.decode(xhr.responseText);
27199         
27200         if(!response.success){
27201             this.arrange();
27202             this.fireEvent('exception', this, xhr);
27203             return;
27204         }
27205         
27206         var file = this.renderPreview(response.data);
27207         
27208         this.files.push(file);
27209         
27210         this.arrange();
27211         
27212     },
27213     
27214     xhrOnError : function(xhr)
27215     {
27216         Roo.log('xhr on error');
27217         
27218         var response = Roo.decode(xhr.responseText);
27219           
27220         Roo.log(response);
27221         
27222         this.arrange();
27223     },
27224     
27225     process : function(file)
27226     {
27227         if(this.fireEvent('process', this, file) !== false){
27228             if(this.editable && file.type.indexOf('image') != -1){
27229                 this.fireEvent('edit', this, file);
27230                 return;
27231             }
27232
27233             this.uploadStart(file, false);
27234
27235             return;
27236         }
27237         
27238     },
27239     
27240     uploadStart : function(file, crop)
27241     {
27242         this.xhr = new XMLHttpRequest();
27243         
27244         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27245             this.arrange();
27246             return;
27247         }
27248         
27249         file.xhr = this.xhr;
27250             
27251         this.managerEl.createChild({
27252             tag : 'div',
27253             cls : 'roo-document-manager-loading',
27254             cn : [
27255                 {
27256                     tag : 'div',
27257                     tooltip : file.name,
27258                     cls : 'roo-document-manager-thumb',
27259                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27260                 }
27261             ]
27262
27263         });
27264
27265         this.xhr.open(this.method, this.url, true);
27266         
27267         var headers = {
27268             "Accept": "application/json",
27269             "Cache-Control": "no-cache",
27270             "X-Requested-With": "XMLHttpRequest"
27271         };
27272         
27273         for (var headerName in headers) {
27274             var headerValue = headers[headerName];
27275             if (headerValue) {
27276                 this.xhr.setRequestHeader(headerName, headerValue);
27277             }
27278         }
27279         
27280         var _this = this;
27281         
27282         this.xhr.onload = function()
27283         {
27284             _this.xhrOnLoad(_this.xhr);
27285         }
27286         
27287         this.xhr.onerror = function()
27288         {
27289             _this.xhrOnError(_this.xhr);
27290         }
27291         
27292         var formData = new FormData();
27293
27294         formData.append('returnHTML', 'NO');
27295         
27296         if(crop){
27297             formData.append('crop', crop);
27298         }
27299         
27300         formData.append(this.paramName, file, file.name);
27301         
27302         if(this.fireEvent('prepare', this, formData) != false){
27303             this.xhr.send(formData);
27304         };
27305     },
27306     
27307     uploadCancel : function()
27308     {
27309         if (this.xhr) {
27310             this.xhr.abort();
27311         }
27312         
27313         
27314         this.delegates = [];
27315         
27316         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27317             el.remove();
27318         }, this);
27319         
27320         this.arrange();
27321     },
27322     
27323     renderPreview : function(file)
27324     {
27325         if(typeof(file.target) != 'undefined' && file.target){
27326             return file;
27327         }
27328         
27329         var previewEl = this.managerEl.createChild({
27330             tag : 'div',
27331             cls : 'roo-document-manager-preview',
27332             cn : [
27333                 {
27334                     tag : 'div',
27335                     tooltip : file.filename,
27336                     cls : 'roo-document-manager-thumb',
27337                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27338                 },
27339                 {
27340                     tag : 'button',
27341                     cls : 'close',
27342                     html : '<i class="fa fa-times-circle"></i>'
27343                 }
27344             ]
27345         });
27346
27347         var close = previewEl.select('button.close', true).first();
27348
27349         close.on('click', this.onRemove, this, file);
27350
27351         file.target = previewEl;
27352
27353         var image = previewEl.select('img', true).first();
27354         
27355         var _this = this;
27356         
27357         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27358         
27359         image.on('click', this.onClick, this, file);
27360         
27361         return file;
27362         
27363     },
27364     
27365     onPreviewLoad : function(file, image)
27366     {
27367         if(typeof(file.target) == 'undefined' || !file.target){
27368             return;
27369         }
27370         
27371         var width = image.dom.naturalWidth || image.dom.width;
27372         var height = image.dom.naturalHeight || image.dom.height;
27373         
27374         if(width > height){
27375             file.target.addClass('wide');
27376             return;
27377         }
27378         
27379         file.target.addClass('tall');
27380         return;
27381         
27382     },
27383     
27384     uploadFromSource : function(file, crop)
27385     {
27386         this.xhr = new XMLHttpRequest();
27387         
27388         this.managerEl.createChild({
27389             tag : 'div',
27390             cls : 'roo-document-manager-loading',
27391             cn : [
27392                 {
27393                     tag : 'div',
27394                     tooltip : file.name,
27395                     cls : 'roo-document-manager-thumb',
27396                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27397                 }
27398             ]
27399
27400         });
27401
27402         this.xhr.open(this.method, this.url, true);
27403         
27404         var headers = {
27405             "Accept": "application/json",
27406             "Cache-Control": "no-cache",
27407             "X-Requested-With": "XMLHttpRequest"
27408         };
27409         
27410         for (var headerName in headers) {
27411             var headerValue = headers[headerName];
27412             if (headerValue) {
27413                 this.xhr.setRequestHeader(headerName, headerValue);
27414             }
27415         }
27416         
27417         var _this = this;
27418         
27419         this.xhr.onload = function()
27420         {
27421             _this.xhrOnLoad(_this.xhr);
27422         }
27423         
27424         this.xhr.onerror = function()
27425         {
27426             _this.xhrOnError(_this.xhr);
27427         }
27428         
27429         var formData = new FormData();
27430
27431         formData.append('returnHTML', 'NO');
27432         
27433         formData.append('crop', crop);
27434         
27435         if(typeof(file.filename) != 'undefined'){
27436             formData.append('filename', file.filename);
27437         }
27438         
27439         if(typeof(file.mimetype) != 'undefined'){
27440             formData.append('mimetype', file.mimetype);
27441         }
27442         
27443         if(this.fireEvent('prepare', this, formData) != false){
27444             this.xhr.send(formData);
27445         };
27446     }
27447 });
27448
27449 /*
27450 * Licence: LGPL
27451 */
27452
27453 /**
27454  * @class Roo.bootstrap.DocumentViewer
27455  * @extends Roo.bootstrap.Component
27456  * Bootstrap DocumentViewer class
27457  * 
27458  * @constructor
27459  * Create a new DocumentViewer
27460  * @param {Object} config The config object
27461  */
27462
27463 Roo.bootstrap.DocumentViewer = function(config){
27464     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27465     
27466     this.addEvents({
27467         /**
27468          * @event initial
27469          * Fire after initEvent
27470          * @param {Roo.bootstrap.DocumentViewer} this
27471          */
27472         "initial" : true,
27473         /**
27474          * @event click
27475          * Fire after click
27476          * @param {Roo.bootstrap.DocumentViewer} this
27477          */
27478         "click" : true,
27479         /**
27480          * @event trash
27481          * Fire after trash button
27482          * @param {Roo.bootstrap.DocumentViewer} this
27483          */
27484         "trash" : true
27485         
27486     });
27487 };
27488
27489 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27490     
27491     getAutoCreate : function()
27492     {
27493         var cfg = {
27494             tag : 'div',
27495             cls : 'roo-document-viewer',
27496             cn : [
27497                 {
27498                     tag : 'div',
27499                     cls : 'roo-document-viewer-body',
27500                     cn : [
27501                         {
27502                             tag : 'div',
27503                             cls : 'roo-document-viewer-thumb',
27504                             cn : [
27505                                 {
27506                                     tag : 'img',
27507                                     cls : 'roo-document-viewer-image'
27508                                 }
27509                             ]
27510                         }
27511                     ]
27512                 },
27513                 {
27514                     tag : 'div',
27515                     cls : 'roo-document-viewer-footer',
27516                     cn : {
27517                         tag : 'div',
27518                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27519                         cn : [
27520                             {
27521                                 tag : 'div',
27522                                 cls : 'btn-group',
27523                                 cn : [
27524                                     {
27525                                         tag : 'button',
27526                                         cls : 'btn btn-default roo-document-viewer-trash',
27527                                         html : '<i class="fa fa-trash"></i>'
27528                                     }
27529                                 ]
27530                             }
27531                         ]
27532                     }
27533                 }
27534             ]
27535         };
27536         
27537         return cfg;
27538     },
27539     
27540     initEvents : function()
27541     {
27542         
27543         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27544         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27545         
27546         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27547         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27548         
27549         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27550         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27551         
27552         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27553         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27554         
27555         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27556         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27557         
27558         this.bodyEl.on('click', this.onClick, this);
27559         
27560         this.trashBtn.on('click', this.onTrash, this);
27561         
27562     },
27563     
27564     initial : function()
27565     {
27566 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27567         
27568         
27569         this.fireEvent('initial', this);
27570         
27571     },
27572     
27573     onClick : function(e)
27574     {
27575         e.preventDefault();
27576         
27577         this.fireEvent('click', this);
27578     },
27579     
27580     onTrash : function(e)
27581     {
27582         e.preventDefault();
27583         
27584         this.fireEvent('trash', this);
27585     }
27586     
27587 });
27588 /*
27589  * - LGPL
27590  *
27591  * nav progress bar
27592  * 
27593  */
27594
27595 /**
27596  * @class Roo.bootstrap.NavProgressBar
27597  * @extends Roo.bootstrap.Component
27598  * Bootstrap NavProgressBar class
27599  * 
27600  * @constructor
27601  * Create a new nav progress bar
27602  * @param {Object} config The config object
27603  */
27604
27605 Roo.bootstrap.NavProgressBar = function(config){
27606     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27607
27608     this.bullets = this.bullets || [];
27609    
27610 //    Roo.bootstrap.NavProgressBar.register(this);
27611      this.addEvents({
27612         /**
27613              * @event changed
27614              * Fires when the active item changes
27615              * @param {Roo.bootstrap.NavProgressBar} this
27616              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27617              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27618          */
27619         'changed': true
27620      });
27621     
27622 };
27623
27624 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27625     
27626     bullets : [],
27627     barItems : [],
27628     
27629     getAutoCreate : function()
27630     {
27631         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27632         
27633         cfg = {
27634             tag : 'div',
27635             cls : 'roo-navigation-bar-group',
27636             cn : [
27637                 {
27638                     tag : 'div',
27639                     cls : 'roo-navigation-top-bar'
27640                 },
27641                 {
27642                     tag : 'div',
27643                     cls : 'roo-navigation-bullets-bar',
27644                     cn : [
27645                         {
27646                             tag : 'ul',
27647                             cls : 'roo-navigation-bar'
27648                         }
27649                     ]
27650                 },
27651                 
27652                 {
27653                     tag : 'div',
27654                     cls : 'roo-navigation-bottom-bar'
27655                 }
27656             ]
27657             
27658         };
27659         
27660         return cfg;
27661         
27662     },
27663     
27664     initEvents: function() 
27665     {
27666         
27667     },
27668     
27669     onRender : function(ct, position) 
27670     {
27671         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27672         
27673         if(this.bullets.length){
27674             Roo.each(this.bullets, function(b){
27675                this.addItem(b);
27676             }, this);
27677         }
27678         
27679         this.format();
27680         
27681     },
27682     
27683     addItem : function(cfg)
27684     {
27685         var item = new Roo.bootstrap.NavProgressItem(cfg);
27686         
27687         item.parentId = this.id;
27688         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27689         
27690         if(cfg.html){
27691             var top = new Roo.bootstrap.Element({
27692                 tag : 'div',
27693                 cls : 'roo-navigation-bar-text'
27694             });
27695             
27696             var bottom = new Roo.bootstrap.Element({
27697                 tag : 'div',
27698                 cls : 'roo-navigation-bar-text'
27699             });
27700             
27701             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27702             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27703             
27704             var topText = new Roo.bootstrap.Element({
27705                 tag : 'span',
27706                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27707             });
27708             
27709             var bottomText = new Roo.bootstrap.Element({
27710                 tag : 'span',
27711                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27712             });
27713             
27714             topText.onRender(top.el, null);
27715             bottomText.onRender(bottom.el, null);
27716             
27717             item.topEl = top;
27718             item.bottomEl = bottom;
27719         }
27720         
27721         this.barItems.push(item);
27722         
27723         return item;
27724     },
27725     
27726     getActive : function()
27727     {
27728         var active = false;
27729         
27730         Roo.each(this.barItems, function(v){
27731             
27732             if (!v.isActive()) {
27733                 return;
27734             }
27735             
27736             active = v;
27737             return false;
27738             
27739         });
27740         
27741         return active;
27742     },
27743     
27744     setActiveItem : function(item)
27745     {
27746         var prev = false;
27747         
27748         Roo.each(this.barItems, function(v){
27749             if (v.rid == item.rid) {
27750                 return ;
27751             }
27752             
27753             if (v.isActive()) {
27754                 v.setActive(false);
27755                 prev = v;
27756             }
27757         });
27758
27759         item.setActive(true);
27760         
27761         this.fireEvent('changed', this, item, prev);
27762     },
27763     
27764     getBarItem: function(rid)
27765     {
27766         var ret = false;
27767         
27768         Roo.each(this.barItems, function(e) {
27769             if (e.rid != rid) {
27770                 return;
27771             }
27772             
27773             ret =  e;
27774             return false;
27775         });
27776         
27777         return ret;
27778     },
27779     
27780     indexOfItem : function(item)
27781     {
27782         var index = false;
27783         
27784         Roo.each(this.barItems, function(v, i){
27785             
27786             if (v.rid != item.rid) {
27787                 return;
27788             }
27789             
27790             index = i;
27791             return false
27792         });
27793         
27794         return index;
27795     },
27796     
27797     setActiveNext : function()
27798     {
27799         var i = this.indexOfItem(this.getActive());
27800         
27801         if (i > this.barItems.length) {
27802             return;
27803         }
27804         
27805         this.setActiveItem(this.barItems[i+1]);
27806     },
27807     
27808     setActivePrev : function()
27809     {
27810         var i = this.indexOfItem(this.getActive());
27811         
27812         if (i  < 1) {
27813             return;
27814         }
27815         
27816         this.setActiveItem(this.barItems[i-1]);
27817     },
27818     
27819     format : function()
27820     {
27821         if(!this.barItems.length){
27822             return;
27823         }
27824      
27825         var width = 100 / this.barItems.length;
27826         
27827         Roo.each(this.barItems, function(i){
27828             i.el.setStyle('width', width + '%');
27829             i.topEl.el.setStyle('width', width + '%');
27830             i.bottomEl.el.setStyle('width', width + '%');
27831         }, this);
27832         
27833     }
27834     
27835 });
27836 /*
27837  * - LGPL
27838  *
27839  * Nav Progress Item
27840  * 
27841  */
27842
27843 /**
27844  * @class Roo.bootstrap.NavProgressItem
27845  * @extends Roo.bootstrap.Component
27846  * Bootstrap NavProgressItem class
27847  * @cfg {String} rid the reference id
27848  * @cfg {Boolean} active (true|false) Is item active default false
27849  * @cfg {Boolean} disabled (true|false) Is item active default false
27850  * @cfg {String} html
27851  * @cfg {String} position (top|bottom) text position default bottom
27852  * @cfg {String} icon show icon instead of number
27853  * 
27854  * @constructor
27855  * Create a new NavProgressItem
27856  * @param {Object} config The config object
27857  */
27858 Roo.bootstrap.NavProgressItem = function(config){
27859     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27860     this.addEvents({
27861         // raw events
27862         /**
27863          * @event click
27864          * The raw click event for the entire grid.
27865          * @param {Roo.bootstrap.NavProgressItem} this
27866          * @param {Roo.EventObject} e
27867          */
27868         "click" : true
27869     });
27870    
27871 };
27872
27873 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27874     
27875     rid : '',
27876     active : false,
27877     disabled : false,
27878     html : '',
27879     position : 'bottom',
27880     icon : false,
27881     
27882     getAutoCreate : function()
27883     {
27884         var iconCls = 'roo-navigation-bar-item-icon';
27885         
27886         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27887         
27888         var cfg = {
27889             tag: 'li',
27890             cls: 'roo-navigation-bar-item',
27891             cn : [
27892                 {
27893                     tag : 'i',
27894                     cls : iconCls
27895                 }
27896             ]
27897         };
27898         
27899         if(this.active){
27900             cfg.cls += ' active';
27901         }
27902         if(this.disabled){
27903             cfg.cls += ' disabled';
27904         }
27905         
27906         return cfg;
27907     },
27908     
27909     disable : function()
27910     {
27911         this.setDisabled(true);
27912     },
27913     
27914     enable : function()
27915     {
27916         this.setDisabled(false);
27917     },
27918     
27919     initEvents: function() 
27920     {
27921         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27922         
27923         this.iconEl.on('click', this.onClick, this);
27924     },
27925     
27926     onClick : function(e)
27927     {
27928         e.preventDefault();
27929         
27930         if(this.disabled){
27931             return;
27932         }
27933         
27934         if(this.fireEvent('click', this, e) === false){
27935             return;
27936         };
27937         
27938         this.parent().setActiveItem(this);
27939     },
27940     
27941     isActive: function () 
27942     {
27943         return this.active;
27944     },
27945     
27946     setActive : function(state)
27947     {
27948         if(this.active == state){
27949             return;
27950         }
27951         
27952         this.active = state;
27953         
27954         if (state) {
27955             this.el.addClass('active');
27956             return;
27957         }
27958         
27959         this.el.removeClass('active');
27960         
27961         return;
27962     },
27963     
27964     setDisabled : function(state)
27965     {
27966         if(this.disabled == state){
27967             return;
27968         }
27969         
27970         this.disabled = state;
27971         
27972         if (state) {
27973             this.el.addClass('disabled');
27974             return;
27975         }
27976         
27977         this.el.removeClass('disabled');
27978     },
27979     
27980     tooltipEl : function()
27981     {
27982         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27983     }
27984 });
27985  
27986
27987  /*
27988  * - LGPL
27989  *
27990  * FieldLabel
27991  * 
27992  */
27993
27994 /**
27995  * @class Roo.bootstrap.FieldLabel
27996  * @extends Roo.bootstrap.Component
27997  * Bootstrap FieldLabel class
27998  * @cfg {String} html contents of the element
27999  * @cfg {String} tag tag of the element default label
28000  * @cfg {String} cls class of the element
28001  * @cfg {String} target label target 
28002  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28003  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28004  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28005  * @cfg {String} iconTooltip default "This field is required"
28006  * 
28007  * @constructor
28008  * Create a new FieldLabel
28009  * @param {Object} config The config object
28010  */
28011
28012 Roo.bootstrap.FieldLabel = function(config){
28013     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28014     
28015     this.addEvents({
28016             /**
28017              * @event invalid
28018              * Fires after the field has been marked as invalid.
28019              * @param {Roo.form.FieldLabel} this
28020              * @param {String} msg The validation message
28021              */
28022             invalid : true,
28023             /**
28024              * @event valid
28025              * Fires after the field has been validated with no errors.
28026              * @param {Roo.form.FieldLabel} this
28027              */
28028             valid : true
28029         });
28030 };
28031
28032 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28033     
28034     tag: 'label',
28035     cls: '',
28036     html: '',
28037     target: '',
28038     allowBlank : true,
28039     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28040     validClass : 'text-success fa fa-lg fa-check',
28041     iconTooltip : 'This field is required',
28042     
28043     getAutoCreate : function(){
28044         
28045         var cfg = {
28046             tag : this.tag,
28047             cls : 'roo-bootstrap-field-label ' + this.cls,
28048             for : this.target,
28049             cn : [
28050                 {
28051                     tag : 'i',
28052                     cls : '',
28053                     tooltip : this.iconTooltip
28054                 },
28055                 {
28056                     tag : 'span',
28057                     html : this.html
28058                 }
28059             ] 
28060         };
28061         
28062         return cfg;
28063     },
28064     
28065     initEvents: function() 
28066     {
28067         Roo.bootstrap.Element.superclass.initEvents.call(this);
28068         
28069         this.iconEl = this.el.select('i', true).first();
28070         
28071         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28072         
28073         Roo.bootstrap.FieldLabel.register(this);
28074     },
28075     
28076     /**
28077      * Mark this field as valid
28078      */
28079     markValid : function()
28080     {
28081         this.iconEl.show();
28082         
28083         this.iconEl.removeClass(this.invalidClass);
28084         
28085         this.iconEl.addClass(this.validClass);
28086         
28087         this.fireEvent('valid', this);
28088     },
28089     
28090     /**
28091      * Mark this field as invalid
28092      * @param {String} msg The validation message
28093      */
28094     markInvalid : function(msg)
28095     {
28096         this.iconEl.show();
28097         
28098         this.iconEl.removeClass(this.validClass);
28099         
28100         this.iconEl.addClass(this.invalidClass);
28101         
28102         this.fireEvent('invalid', this, msg);
28103     }
28104     
28105    
28106 });
28107
28108 Roo.apply(Roo.bootstrap.FieldLabel, {
28109     
28110     groups: {},
28111     
28112      /**
28113     * register a FieldLabel Group
28114     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28115     */
28116     register : function(label)
28117     {
28118         if(this.groups.hasOwnProperty(label.target)){
28119             return;
28120         }
28121      
28122         this.groups[label.target] = label;
28123         
28124     },
28125     /**
28126     * fetch a FieldLabel Group based on the target
28127     * @param {string} target
28128     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28129     */
28130     get: function(target) {
28131         if (typeof(this.groups[target]) == 'undefined') {
28132             return false;
28133         }
28134         
28135         return this.groups[target] ;
28136     }
28137 });
28138
28139  
28140
28141  /*
28142  * - LGPL
28143  *
28144  * page DateSplitField.
28145  * 
28146  */
28147
28148
28149 /**
28150  * @class Roo.bootstrap.DateSplitField
28151  * @extends Roo.bootstrap.Component
28152  * Bootstrap DateSplitField class
28153  * @cfg {string} fieldLabel - the label associated
28154  * @cfg {Number} labelWidth set the width of label (0-12)
28155  * @cfg {String} labelAlign (top|left)
28156  * @cfg {Boolean} dayAllowBlank (true|false) default false
28157  * @cfg {Boolean} monthAllowBlank (true|false) default false
28158  * @cfg {Boolean} yearAllowBlank (true|false) default false
28159  * @cfg {string} dayPlaceholder 
28160  * @cfg {string} monthPlaceholder
28161  * @cfg {string} yearPlaceholder
28162  * @cfg {string} dayFormat default 'd'
28163  * @cfg {string} monthFormat default 'm'
28164  * @cfg {string} yearFormat default 'Y'
28165
28166  *     
28167  * @constructor
28168  * Create a new DateSplitField
28169  * @param {Object} config The config object
28170  */
28171
28172 Roo.bootstrap.DateSplitField = function(config){
28173     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28174     
28175     this.addEvents({
28176         // raw events
28177          /**
28178          * @event years
28179          * getting the data of years
28180          * @param {Roo.bootstrap.DateSplitField} this
28181          * @param {Object} years
28182          */
28183         "years" : true,
28184         /**
28185          * @event days
28186          * getting the data of days
28187          * @param {Roo.bootstrap.DateSplitField} this
28188          * @param {Object} days
28189          */
28190         "days" : true,
28191         /**
28192          * @event invalid
28193          * Fires after the field has been marked as invalid.
28194          * @param {Roo.form.Field} this
28195          * @param {String} msg The validation message
28196          */
28197         invalid : true,
28198        /**
28199          * @event valid
28200          * Fires after the field has been validated with no errors.
28201          * @param {Roo.form.Field} this
28202          */
28203         valid : true
28204     });
28205 };
28206
28207 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28208     
28209     fieldLabel : '',
28210     labelAlign : 'top',
28211     labelWidth : 3,
28212     dayAllowBlank : false,
28213     monthAllowBlank : false,
28214     yearAllowBlank : false,
28215     dayPlaceholder : '',
28216     monthPlaceholder : '',
28217     yearPlaceholder : '',
28218     dayFormat : 'd',
28219     monthFormat : 'm',
28220     yearFormat : 'Y',
28221     isFormField : true,
28222     
28223     getAutoCreate : function()
28224     {
28225         var cfg = {
28226             tag : 'div',
28227             cls : 'row roo-date-split-field-group',
28228             cn : [
28229                 {
28230                     tag : 'input',
28231                     type : 'hidden',
28232                     cls : 'form-hidden-field roo-date-split-field-group-value',
28233                     name : this.name
28234                 }
28235             ]
28236         };
28237         
28238         if(this.fieldLabel){
28239             cfg.cn.push({
28240                 tag : 'div',
28241                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28242                 cn : [
28243                     {
28244                         tag : 'label',
28245                         html : this.fieldLabel
28246                     }
28247                 ]
28248             });
28249         }
28250         
28251         Roo.each(['day', 'month', 'year'], function(t){
28252             cfg.cn.push({
28253                 tag : 'div',
28254                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28255             });
28256         }, this);
28257         
28258         return cfg;
28259     },
28260     
28261     inputEl: function ()
28262     {
28263         return this.el.select('.roo-date-split-field-group-value', true).first();
28264     },
28265     
28266     onRender : function(ct, position) 
28267     {
28268         var _this = this;
28269         
28270         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28271         
28272         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28273         
28274         this.dayField = new Roo.bootstrap.ComboBox({
28275             allowBlank : this.dayAllowBlank,
28276             alwaysQuery : true,
28277             displayField : 'value',
28278             editable : false,
28279             fieldLabel : '',
28280             forceSelection : true,
28281             mode : 'local',
28282             placeholder : this.dayPlaceholder,
28283             selectOnFocus : true,
28284             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28285             triggerAction : 'all',
28286             typeAhead : true,
28287             valueField : 'value',
28288             store : new Roo.data.SimpleStore({
28289                 data : (function() {    
28290                     var days = [];
28291                     _this.fireEvent('days', _this, days);
28292                     return days;
28293                 })(),
28294                 fields : [ 'value' ]
28295             }),
28296             listeners : {
28297                 select : function (_self, record, index)
28298                 {
28299                     _this.setValue(_this.getValue());
28300                 }
28301             }
28302         });
28303
28304         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28305         
28306         this.monthField = new Roo.bootstrap.MonthField({
28307             after : '<i class=\"fa fa-calendar\"></i>',
28308             allowBlank : this.monthAllowBlank,
28309             placeholder : this.monthPlaceholder,
28310             readOnly : true,
28311             listeners : {
28312                 render : function (_self)
28313                 {
28314                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28315                         e.preventDefault();
28316                         _self.focus();
28317                     });
28318                 },
28319                 select : function (_self, oldvalue, newvalue)
28320                 {
28321                     _this.setValue(_this.getValue());
28322                 }
28323             }
28324         });
28325         
28326         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28327         
28328         this.yearField = new Roo.bootstrap.ComboBox({
28329             allowBlank : this.yearAllowBlank,
28330             alwaysQuery : true,
28331             displayField : 'value',
28332             editable : false,
28333             fieldLabel : '',
28334             forceSelection : true,
28335             mode : 'local',
28336             placeholder : this.yearPlaceholder,
28337             selectOnFocus : true,
28338             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28339             triggerAction : 'all',
28340             typeAhead : true,
28341             valueField : 'value',
28342             store : new Roo.data.SimpleStore({
28343                 data : (function() {
28344                     var years = [];
28345                     _this.fireEvent('years', _this, years);
28346                     return years;
28347                 })(),
28348                 fields : [ 'value' ]
28349             }),
28350             listeners : {
28351                 select : function (_self, record, index)
28352                 {
28353                     _this.setValue(_this.getValue());
28354                 }
28355             }
28356         });
28357
28358         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28359     },
28360     
28361     setValue : function(v, format)
28362     {
28363         this.inputEl.dom.value = v;
28364         
28365         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28366         
28367         var d = Date.parseDate(v, f);
28368         
28369         if(!d){
28370             this.validate();
28371             return;
28372         }
28373         
28374         this.setDay(d.format(this.dayFormat));
28375         this.setMonth(d.format(this.monthFormat));
28376         this.setYear(d.format(this.yearFormat));
28377         
28378         this.validate();
28379         
28380         return;
28381     },
28382     
28383     setDay : function(v)
28384     {
28385         this.dayField.setValue(v);
28386         this.inputEl.dom.value = this.getValue();
28387         this.validate();
28388         return;
28389     },
28390     
28391     setMonth : function(v)
28392     {
28393         this.monthField.setValue(v, true);
28394         this.inputEl.dom.value = this.getValue();
28395         this.validate();
28396         return;
28397     },
28398     
28399     setYear : function(v)
28400     {
28401         this.yearField.setValue(v);
28402         this.inputEl.dom.value = this.getValue();
28403         this.validate();
28404         return;
28405     },
28406     
28407     getDay : function()
28408     {
28409         return this.dayField.getValue();
28410     },
28411     
28412     getMonth : function()
28413     {
28414         return this.monthField.getValue();
28415     },
28416     
28417     getYear : function()
28418     {
28419         return this.yearField.getValue();
28420     },
28421     
28422     getValue : function()
28423     {
28424         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28425         
28426         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28427         
28428         return date;
28429     },
28430     
28431     reset : function()
28432     {
28433         this.setDay('');
28434         this.setMonth('');
28435         this.setYear('');
28436         this.inputEl.dom.value = '';
28437         this.validate();
28438         return;
28439     },
28440     
28441     validate : function()
28442     {
28443         var d = this.dayField.validate();
28444         var m = this.monthField.validate();
28445         var y = this.yearField.validate();
28446         
28447         var valid = true;
28448         
28449         if(
28450                 (!this.dayAllowBlank && !d) ||
28451                 (!this.monthAllowBlank && !m) ||
28452                 (!this.yearAllowBlank && !y)
28453         ){
28454             valid = false;
28455         }
28456         
28457         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28458             return valid;
28459         }
28460         
28461         if(valid){
28462             this.markValid();
28463             return valid;
28464         }
28465         
28466         this.markInvalid();
28467         
28468         return valid;
28469     },
28470     
28471     markValid : function()
28472     {
28473         
28474         var label = this.el.select('label', true).first();
28475         var icon = this.el.select('i.fa-star', true).first();
28476
28477         if(label && icon){
28478             icon.remove();
28479         }
28480         
28481         this.fireEvent('valid', this);
28482     },
28483     
28484      /**
28485      * Mark this field as invalid
28486      * @param {String} msg The validation message
28487      */
28488     markInvalid : function(msg)
28489     {
28490         
28491         var label = this.el.select('label', true).first();
28492         var icon = this.el.select('i.fa-star', true).first();
28493
28494         if(label && !icon){
28495             this.el.select('.roo-date-split-field-label', true).createChild({
28496                 tag : 'i',
28497                 cls : 'text-danger fa fa-lg fa-star',
28498                 tooltip : 'This field is required',
28499                 style : 'margin-right:5px;'
28500             }, label, true);
28501         }
28502         
28503         this.fireEvent('invalid', this, msg);
28504     },
28505     
28506     clearInvalid : function()
28507     {
28508         var label = this.el.select('label', true).first();
28509         var icon = this.el.select('i.fa-star', true).first();
28510
28511         if(label && icon){
28512             icon.remove();
28513         }
28514         
28515         this.fireEvent('valid', this);
28516     },
28517     
28518     getName: function()
28519     {
28520         return this.name;
28521     }
28522     
28523 });
28524
28525  /**
28526  *
28527  * This is based on 
28528  * http://masonry.desandro.com
28529  *
28530  * The idea is to render all the bricks based on vertical width...
28531  *
28532  * The original code extends 'outlayer' - we might need to use that....
28533  * 
28534  */
28535
28536
28537 /**
28538  * @class Roo.bootstrap.LayoutMasonry
28539  * @extends Roo.bootstrap.Component
28540  * Bootstrap Layout Masonry class
28541  * 
28542  * @constructor
28543  * Create a new Element
28544  * @param {Object} config The config object
28545  */
28546
28547 Roo.bootstrap.LayoutMasonry = function(config){
28548     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28549     
28550     this.bricks = [];
28551     
28552 };
28553
28554 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28555     
28556     /**
28557      * @cfg {Boolean} isLayoutInstant = no animation?
28558      */   
28559     isLayoutInstant : false, // needed?
28560    
28561     /**
28562      * @cfg {Number} boxWidth  width of the columns
28563      */   
28564     boxWidth : 450,
28565     
28566       /**
28567      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28568      */   
28569     boxHeight : 0,
28570     
28571     /**
28572      * @cfg {Number} padWidth padding below box..
28573      */   
28574     padWidth : 10, 
28575     
28576     /**
28577      * @cfg {Number} gutter gutter width..
28578      */   
28579     gutter : 10,
28580     
28581      /**
28582      * @cfg {Number} maxCols maximum number of columns
28583      */   
28584     
28585     maxCols: 0,
28586     
28587     /**
28588      * @cfg {Boolean} isAutoInitial defalut true
28589      */   
28590     isAutoInitial : true, 
28591     
28592     containerWidth: 0,
28593     
28594     /**
28595      * @cfg {Boolean} isHorizontal defalut false
28596      */   
28597     isHorizontal : false, 
28598
28599     currentSize : null,
28600     
28601     tag: 'div',
28602     
28603     cls: '',
28604     
28605     bricks: null, //CompositeElement
28606     
28607     cols : 1,
28608     
28609     _isLayoutInited : false,
28610     
28611 //    isAlternative : false, // only use for vertical layout...
28612     
28613     /**
28614      * @cfg {Number} alternativePadWidth padding below box..
28615      */   
28616     alternativePadWidth : 50, 
28617     
28618     getAutoCreate : function(){
28619         
28620         var cfg = {
28621             tag: this.tag,
28622             cls: 'blog-masonary-wrapper ' + this.cls,
28623             cn : {
28624                 cls : 'mas-boxes masonary'
28625             }
28626         };
28627         
28628         return cfg;
28629     },
28630     
28631     getChildContainer: function( )
28632     {
28633         if (this.boxesEl) {
28634             return this.boxesEl;
28635         }
28636         
28637         this.boxesEl = this.el.select('.mas-boxes').first();
28638         
28639         return this.boxesEl;
28640     },
28641     
28642     
28643     initEvents : function()
28644     {
28645         var _this = this;
28646         
28647         if(this.isAutoInitial){
28648             Roo.log('hook children rendered');
28649             this.on('childrenrendered', function() {
28650                 Roo.log('children rendered');
28651                 _this.initial();
28652             } ,this);
28653         }
28654     },
28655     
28656     initial : function()
28657     {
28658         this.currentSize = this.el.getBox(true);
28659         
28660         Roo.EventManager.onWindowResize(this.resize, this); 
28661
28662         if(!this.isAutoInitial){
28663             this.layout();
28664             return;
28665         }
28666         
28667         this.layout();
28668         
28669         return;
28670         //this.layout.defer(500,this);
28671         
28672     },
28673     
28674     resize : function()
28675     {
28676         Roo.log('resize');
28677         
28678         var cs = this.el.getBox(true);
28679         
28680         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28681             Roo.log("no change in with or X");
28682             return;
28683         }
28684         
28685         this.currentSize = cs;
28686         
28687         this.layout();
28688         
28689     },
28690     
28691     layout : function()
28692     {   
28693         this._resetLayout();
28694         
28695         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28696         
28697         this.layoutItems( isInstant );
28698       
28699         this._isLayoutInited = true;
28700         
28701     },
28702     
28703     _resetLayout : function()
28704     {
28705         if(this.isHorizontal){
28706             this.horizontalMeasureColumns();
28707             return;
28708         }
28709         
28710         this.verticalMeasureColumns();
28711         
28712     },
28713     
28714     verticalMeasureColumns : function()
28715     {
28716         this.getContainerWidth();
28717         
28718 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28719 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28720 //            return;
28721 //        }
28722         
28723         var boxWidth = this.boxWidth + this.padWidth;
28724         
28725         if(this.containerWidth < this.boxWidth){
28726             boxWidth = this.containerWidth
28727         }
28728         
28729         var containerWidth = this.containerWidth;
28730         
28731         var cols = Math.floor(containerWidth / boxWidth);
28732         
28733         this.cols = Math.max( cols, 1 );
28734         
28735         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28736         
28737         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28738         
28739         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28740         
28741         this.colWidth = boxWidth + avail - this.padWidth;
28742         
28743         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28744         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28745     },
28746     
28747     horizontalMeasureColumns : function()
28748     {
28749         this.getContainerWidth();
28750         
28751         var boxWidth = this.boxWidth;
28752         
28753         if(this.containerWidth < boxWidth){
28754             boxWidth = this.containerWidth;
28755         }
28756         
28757         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28758         
28759         this.el.setHeight(boxWidth);
28760         
28761     },
28762     
28763     getContainerWidth : function()
28764     {
28765         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
28766     },
28767     
28768     layoutItems : function( isInstant )
28769     {
28770         var items = Roo.apply([], this.bricks);
28771         
28772         if(this.isHorizontal){
28773             this._horizontalLayoutItems( items , isInstant );
28774             return;
28775         }
28776         
28777 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28778 //            this._verticalAlternativeLayoutItems( items , isInstant );
28779 //            return;
28780 //        }
28781         
28782         this._verticalLayoutItems( items , isInstant );
28783         
28784     },
28785     
28786     _verticalLayoutItems : function ( items , isInstant)
28787     {
28788         if ( !items || !items.length ) {
28789             return;
28790         }
28791         
28792         var standard = [
28793             ['xs', 'xs', 'xs', 'tall'],
28794             ['xs', 'xs', 'tall'],
28795             ['xs', 'xs', 'sm'],
28796             ['xs', 'xs', 'xs'],
28797             ['xs', 'tall'],
28798             ['xs', 'sm'],
28799             ['xs', 'xs'],
28800             ['xs'],
28801             
28802             ['sm', 'xs', 'xs'],
28803             ['sm', 'xs'],
28804             ['sm'],
28805             
28806             ['tall', 'xs', 'xs', 'xs'],
28807             ['tall', 'xs', 'xs'],
28808             ['tall', 'xs'],
28809             ['tall']
28810             
28811         ];
28812         
28813         var queue = [];
28814         
28815         var boxes = [];
28816         
28817         var box = [];
28818         
28819         Roo.each(items, function(item, k){
28820             
28821             switch (item.size) {
28822                 // these layouts take up a full box,
28823                 case 'md' :
28824                 case 'md-left' :
28825                 case 'md-right' :
28826                 case 'wide' :
28827                     
28828                     if(box.length){
28829                         boxes.push(box);
28830                         box = [];
28831                     }
28832                     
28833                     boxes.push([item]);
28834                     
28835                     break;
28836                     
28837                 case 'xs' :
28838                 case 'sm' :
28839                 case 'tall' :
28840                     
28841                     box.push(item);
28842                     
28843                     break;
28844                 default :
28845                     break;
28846                     
28847             }
28848             
28849         }, this);
28850         
28851         if(box.length){
28852             boxes.push(box);
28853             box = [];
28854         }
28855         
28856         var filterPattern = function(box, length)
28857         {
28858             if(!box.length){
28859                 return;
28860             }
28861             
28862             var match = false;
28863             
28864             var pattern = box.slice(0, length);
28865             
28866             var format = [];
28867             
28868             Roo.each(pattern, function(i){
28869                 format.push(i.size);
28870             }, this);
28871             
28872             Roo.each(standard, function(s){
28873                 
28874                 if(String(s) != String(format)){
28875                     return;
28876                 }
28877                 
28878                 match = true;
28879                 return false;
28880                 
28881             }, this);
28882             
28883             if(!match && length == 1){
28884                 return;
28885             }
28886             
28887             if(!match){
28888                 filterPattern(box, length - 1);
28889                 return;
28890             }
28891                 
28892             queue.push(pattern);
28893
28894             box = box.slice(length, box.length);
28895
28896             filterPattern(box, 4);
28897
28898             return;
28899             
28900         }
28901         
28902         Roo.each(boxes, function(box, k){
28903             
28904             if(!box.length){
28905                 return;
28906             }
28907             
28908             if(box.length == 1){
28909                 queue.push(box);
28910                 return;
28911             }
28912             
28913             filterPattern(box, 4);
28914             
28915         }, this);
28916         
28917         this._processVerticalLayoutQueue( queue, isInstant );
28918         
28919     },
28920     
28921 //    _verticalAlternativeLayoutItems : function( items , isInstant )
28922 //    {
28923 //        if ( !items || !items.length ) {
28924 //            return;
28925 //        }
28926 //
28927 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
28928 //        
28929 //    },
28930     
28931     _horizontalLayoutItems : function ( items , isInstant)
28932     {
28933         if ( !items || !items.length || items.length < 3) {
28934             return;
28935         }
28936         
28937         items.reverse();
28938         
28939         var eItems = items.slice(0, 3);
28940         
28941         items = items.slice(3, items.length);
28942         
28943         var standard = [
28944             ['xs', 'xs', 'xs', 'wide'],
28945             ['xs', 'xs', 'wide'],
28946             ['xs', 'xs', 'sm'],
28947             ['xs', 'xs', 'xs'],
28948             ['xs', 'wide'],
28949             ['xs', 'sm'],
28950             ['xs', 'xs'],
28951             ['xs'],
28952             
28953             ['sm', 'xs', 'xs'],
28954             ['sm', 'xs'],
28955             ['sm'],
28956             
28957             ['wide', 'xs', 'xs', 'xs'],
28958             ['wide', 'xs', 'xs'],
28959             ['wide', 'xs'],
28960             ['wide'],
28961             
28962             ['wide-thin']
28963         ];
28964         
28965         var queue = [];
28966         
28967         var boxes = [];
28968         
28969         var box = [];
28970         
28971         Roo.each(items, function(item, k){
28972             
28973             switch (item.size) {
28974                 case 'md' :
28975                 case 'md-left' :
28976                 case 'md-right' :
28977                 case 'tall' :
28978                     
28979                     if(box.length){
28980                         boxes.push(box);
28981                         box = [];
28982                     }
28983                     
28984                     boxes.push([item]);
28985                     
28986                     break;
28987                     
28988                 case 'xs' :
28989                 case 'sm' :
28990                 case 'wide' :
28991                 case 'wide-thin' :
28992                     
28993                     box.push(item);
28994                     
28995                     break;
28996                 default :
28997                     break;
28998                     
28999             }
29000             
29001         }, this);
29002         
29003         if(box.length){
29004             boxes.push(box);
29005             box = [];
29006         }
29007         
29008         var filterPattern = function(box, length)
29009         {
29010             if(!box.length){
29011                 return;
29012             }
29013             
29014             var match = false;
29015             
29016             var pattern = box.slice(0, length);
29017             
29018             var format = [];
29019             
29020             Roo.each(pattern, function(i){
29021                 format.push(i.size);
29022             }, this);
29023             
29024             Roo.each(standard, function(s){
29025                 
29026                 if(String(s) != String(format)){
29027                     return;
29028                 }
29029                 
29030                 match = true;
29031                 return false;
29032                 
29033             }, this);
29034             
29035             if(!match && length == 1){
29036                 return;
29037             }
29038             
29039             if(!match){
29040                 filterPattern(box, length - 1);
29041                 return;
29042             }
29043                 
29044             queue.push(pattern);
29045
29046             box = box.slice(length, box.length);
29047
29048             filterPattern(box, 4);
29049
29050             return;
29051             
29052         }
29053         
29054         Roo.each(boxes, function(box, k){
29055             
29056             if(!box.length){
29057                 return;
29058             }
29059             
29060             if(box.length == 1){
29061                 queue.push(box);
29062                 return;
29063             }
29064             
29065             filterPattern(box, 4);
29066             
29067         }, this);
29068         
29069         
29070         var prune = [];
29071         
29072         var pos = this.el.getBox(true);
29073         
29074         var minX = pos.x;
29075         
29076         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29077         
29078         var hit_end = false;
29079         
29080         Roo.each(queue, function(box){
29081             
29082             if(hit_end){
29083                 
29084                 Roo.each(box, function(b){
29085                 
29086                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29087                     b.el.hide();
29088
29089                 }, this);
29090
29091                 return;
29092             }
29093             
29094             var mx = 0;
29095             
29096             Roo.each(box, function(b){
29097                 
29098                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29099                 b.el.show();
29100
29101                 mx = Math.max(mx, b.x);
29102                 
29103             }, this);
29104             
29105             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29106             
29107             if(maxX < minX){
29108                 
29109                 Roo.each(box, function(b){
29110                 
29111                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29112                     b.el.hide();
29113                     
29114                 }, this);
29115                 
29116                 hit_end = true;
29117                 
29118                 return;
29119             }
29120             
29121             prune.push(box);
29122             
29123         }, this);
29124         
29125         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29126     },
29127     
29128     /** Sets position of item in DOM
29129     * @param {Element} item
29130     * @param {Number} x - horizontal position
29131     * @param {Number} y - vertical position
29132     * @param {Boolean} isInstant - disables transitions
29133     */
29134     _processVerticalLayoutQueue : function( queue, isInstant )
29135     {
29136         var pos = this.el.getBox(true);
29137         var x = pos.x;
29138         var y = pos.y;
29139         var maxY = [];
29140         
29141         for (var i = 0; i < this.cols; i++){
29142             maxY[i] = pos.y;
29143         }
29144         
29145         Roo.each(queue, function(box, k){
29146             
29147             var col = k % this.cols;
29148             
29149             Roo.each(box, function(b,kk){
29150                 
29151                 b.el.position('absolute');
29152                 
29153                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29154                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29155                 
29156                 if(b.size == 'md-left' || b.size == 'md-right'){
29157                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29158                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29159                 }
29160                 
29161                 b.el.setWidth(width);
29162                 b.el.setHeight(height);
29163                 // iframe?
29164                 b.el.select('iframe',true).setSize(width,height);
29165                 
29166             }, this);
29167             
29168             for (var i = 0; i < this.cols; i++){
29169                 
29170                 if(maxY[i] < maxY[col]){
29171                     col = i;
29172                     continue;
29173                 }
29174                 
29175                 col = Math.min(col, i);
29176                 
29177             }
29178             
29179             x = pos.x + col * (this.colWidth + this.padWidth);
29180             
29181             y = maxY[col];
29182             
29183             var positions = [];
29184             
29185             switch (box.length){
29186                 case 1 :
29187                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29188                     break;
29189                 case 2 :
29190                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29191                     break;
29192                 case 3 :
29193                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29194                     break;
29195                 case 4 :
29196                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29197                     break;
29198                 default :
29199                     break;
29200             }
29201             
29202             Roo.each(box, function(b,kk){
29203                 
29204                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29205                 
29206                 var sz = b.el.getSize();
29207                 
29208                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29209                 
29210             }, this);
29211             
29212         }, this);
29213         
29214         var mY = 0;
29215         
29216         for (var i = 0; i < this.cols; i++){
29217             mY = Math.max(mY, maxY[i]);
29218         }
29219         
29220         this.el.setHeight(mY - pos.y);
29221         
29222     },
29223     
29224 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29225 //    {
29226 //        var pos = this.el.getBox(true);
29227 //        var x = pos.x;
29228 //        var y = pos.y;
29229 //        var maxX = pos.right;
29230 //        
29231 //        var maxHeight = 0;
29232 //        
29233 //        Roo.each(items, function(item, k){
29234 //            
29235 //            var c = k % 2;
29236 //            
29237 //            item.el.position('absolute');
29238 //                
29239 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29240 //
29241 //            item.el.setWidth(width);
29242 //
29243 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29244 //
29245 //            item.el.setHeight(height);
29246 //            
29247 //            if(c == 0){
29248 //                item.el.setXY([x, y], isInstant ? false : true);
29249 //            } else {
29250 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29251 //            }
29252 //            
29253 //            y = y + height + this.alternativePadWidth;
29254 //            
29255 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29256 //            
29257 //        }, this);
29258 //        
29259 //        this.el.setHeight(maxHeight);
29260 //        
29261 //    },
29262     
29263     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29264     {
29265         var pos = this.el.getBox(true);
29266         
29267         var minX = pos.x;
29268         var minY = pos.y;
29269         
29270         var maxX = pos.right;
29271         
29272         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29273         
29274         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29275         
29276         Roo.each(queue, function(box, k){
29277             
29278             Roo.each(box, function(b, kk){
29279                 
29280                 b.el.position('absolute');
29281                 
29282                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29283                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29284                 
29285                 if(b.size == 'md-left' || b.size == 'md-right'){
29286                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29287                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29288                 }
29289                 
29290                 b.el.setWidth(width);
29291                 b.el.setHeight(height);
29292                 
29293             }, this);
29294             
29295             if(!box.length){
29296                 return;
29297             }
29298             
29299             var positions = [];
29300             
29301             switch (box.length){
29302                 case 1 :
29303                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29304                     break;
29305                 case 2 :
29306                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29307                     break;
29308                 case 3 :
29309                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29310                     break;
29311                 case 4 :
29312                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29313                     break;
29314                 default :
29315                     break;
29316             }
29317             
29318             Roo.each(box, function(b,kk){
29319                 
29320                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29321                 
29322                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29323                 
29324             }, this);
29325             
29326         }, this);
29327         
29328     },
29329     
29330     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29331     {
29332         Roo.each(eItems, function(b,k){
29333             
29334             b.size = (k == 0) ? 'sm' : 'xs';
29335             b.x = (k == 0) ? 2 : 1;
29336             b.y = (k == 0) ? 2 : 1;
29337             
29338             b.el.position('absolute');
29339             
29340             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29341                 
29342             b.el.setWidth(width);
29343             
29344             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29345             
29346             b.el.setHeight(height);
29347             
29348         }, this);
29349
29350         var positions = [];
29351         
29352         positions.push({
29353             x : maxX - this.unitWidth * 2 - this.gutter,
29354             y : minY
29355         });
29356         
29357         positions.push({
29358             x : maxX - this.unitWidth,
29359             y : minY + (this.unitWidth + this.gutter) * 2
29360         });
29361         
29362         positions.push({
29363             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29364             y : minY
29365         });
29366         
29367         Roo.each(eItems, function(b,k){
29368             
29369             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29370
29371         }, this);
29372         
29373     },
29374     
29375     getVerticalOneBoxColPositions : function(x, y, box)
29376     {
29377         var pos = [];
29378         
29379         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29380         
29381         if(box[0].size == 'md-left'){
29382             rand = 0;
29383         }
29384         
29385         if(box[0].size == 'md-right'){
29386             rand = 1;
29387         }
29388         
29389         pos.push({
29390             x : x + (this.unitWidth + this.gutter) * rand,
29391             y : y
29392         });
29393         
29394         return pos;
29395     },
29396     
29397     getVerticalTwoBoxColPositions : function(x, y, box)
29398     {
29399         var pos = [];
29400         
29401         if(box[0].size == 'xs'){
29402             
29403             pos.push({
29404                 x : x,
29405                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29406             });
29407
29408             pos.push({
29409                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29410                 y : y
29411             });
29412             
29413             return pos;
29414             
29415         }
29416         
29417         pos.push({
29418             x : x,
29419             y : y
29420         });
29421
29422         pos.push({
29423             x : x + (this.unitWidth + this.gutter) * 2,
29424             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29425         });
29426         
29427         return pos;
29428         
29429     },
29430     
29431     getVerticalThreeBoxColPositions : function(x, y, box)
29432     {
29433         var pos = [];
29434         
29435         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29436             
29437             pos.push({
29438                 x : x,
29439                 y : y
29440             });
29441
29442             pos.push({
29443                 x : x + (this.unitWidth + this.gutter) * 1,
29444                 y : y
29445             });
29446             
29447             pos.push({
29448                 x : x + (this.unitWidth + this.gutter) * 2,
29449                 y : y
29450             });
29451             
29452             return pos;
29453             
29454         }
29455         
29456         if(box[0].size == 'xs' && box[1].size == 'xs'){
29457             
29458             pos.push({
29459                 x : x,
29460                 y : y
29461             });
29462
29463             pos.push({
29464                 x : x,
29465                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29466             });
29467             
29468             pos.push({
29469                 x : x + (this.unitWidth + this.gutter) * 1,
29470                 y : y
29471             });
29472             
29473             return pos;
29474             
29475         }
29476         
29477         pos.push({
29478             x : x,
29479             y : y
29480         });
29481
29482         pos.push({
29483             x : x + (this.unitWidth + this.gutter) * 2,
29484             y : y
29485         });
29486
29487         pos.push({
29488             x : x + (this.unitWidth + this.gutter) * 2,
29489             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29490         });
29491             
29492         return pos;
29493         
29494     },
29495     
29496     getVerticalFourBoxColPositions : function(x, y, box)
29497     {
29498         var pos = [];
29499         
29500         if(box[0].size == 'xs'){
29501             
29502             pos.push({
29503                 x : x,
29504                 y : y
29505             });
29506
29507             pos.push({
29508                 x : x,
29509                 y : y + (this.unitHeight + this.gutter) * 1
29510             });
29511             
29512             pos.push({
29513                 x : x,
29514                 y : y + (this.unitHeight + this.gutter) * 2
29515             });
29516             
29517             pos.push({
29518                 x : x + (this.unitWidth + this.gutter) * 1,
29519                 y : y
29520             });
29521             
29522             return pos;
29523             
29524         }
29525         
29526         pos.push({
29527             x : x,
29528             y : y
29529         });
29530
29531         pos.push({
29532             x : x + (this.unitWidth + this.gutter) * 2,
29533             y : y
29534         });
29535
29536         pos.push({
29537             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29538             y : y + (this.unitHeight + this.gutter) * 1
29539         });
29540
29541         pos.push({
29542             x : x + (this.unitWidth + this.gutter) * 2,
29543             y : y + (this.unitWidth + this.gutter) * 2
29544         });
29545
29546         return pos;
29547         
29548     },
29549     
29550     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29551     {
29552         var pos = [];
29553         
29554         if(box[0].size == 'md-left'){
29555             pos.push({
29556                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29557                 y : minY
29558             });
29559             
29560             return pos;
29561         }
29562         
29563         if(box[0].size == 'md-right'){
29564             pos.push({
29565                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29566                 y : minY + (this.unitWidth + this.gutter) * 1
29567             });
29568             
29569             return pos;
29570         }
29571         
29572         var rand = Math.floor(Math.random() * (4 - box[0].y));
29573         
29574         pos.push({
29575             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29576             y : minY + (this.unitWidth + this.gutter) * rand
29577         });
29578         
29579         return pos;
29580         
29581     },
29582     
29583     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29584     {
29585         var pos = [];
29586         
29587         if(box[0].size == 'xs'){
29588             
29589             pos.push({
29590                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29591                 y : minY
29592             });
29593
29594             pos.push({
29595                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29596                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29597             });
29598             
29599             return pos;
29600             
29601         }
29602         
29603         pos.push({
29604             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29605             y : minY
29606         });
29607
29608         pos.push({
29609             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29610             y : minY + (this.unitWidth + this.gutter) * 2
29611         });
29612         
29613         return pos;
29614         
29615     },
29616     
29617     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29618     {
29619         var pos = [];
29620         
29621         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29622             
29623             pos.push({
29624                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29625                 y : minY
29626             });
29627
29628             pos.push({
29629                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29630                 y : minY + (this.unitWidth + this.gutter) * 1
29631             });
29632             
29633             pos.push({
29634                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29635                 y : minY + (this.unitWidth + this.gutter) * 2
29636             });
29637             
29638             return pos;
29639             
29640         }
29641         
29642         if(box[0].size == 'xs' && box[1].size == 'xs'){
29643             
29644             pos.push({
29645                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29646                 y : minY
29647             });
29648
29649             pos.push({
29650                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29651                 y : minY
29652             });
29653             
29654             pos.push({
29655                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29656                 y : minY + (this.unitWidth + this.gutter) * 1
29657             });
29658             
29659             return pos;
29660             
29661         }
29662         
29663         pos.push({
29664             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29665             y : minY
29666         });
29667
29668         pos.push({
29669             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29670             y : minY + (this.unitWidth + this.gutter) * 2
29671         });
29672
29673         pos.push({
29674             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29675             y : minY + (this.unitWidth + this.gutter) * 2
29676         });
29677             
29678         return pos;
29679         
29680     },
29681     
29682     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29683     {
29684         var pos = [];
29685         
29686         if(box[0].size == 'xs'){
29687             
29688             pos.push({
29689                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29690                 y : minY
29691             });
29692
29693             pos.push({
29694                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29695                 y : minY
29696             });
29697             
29698             pos.push({
29699                 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),
29700                 y : minY
29701             });
29702             
29703             pos.push({
29704                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29705                 y : minY + (this.unitWidth + this.gutter) * 1
29706             });
29707             
29708             return pos;
29709             
29710         }
29711         
29712         pos.push({
29713             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29714             y : minY
29715         });
29716         
29717         pos.push({
29718             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29719             y : minY + (this.unitWidth + this.gutter) * 2
29720         });
29721         
29722         pos.push({
29723             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29724             y : minY + (this.unitWidth + this.gutter) * 2
29725         });
29726         
29727         pos.push({
29728             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),
29729             y : minY + (this.unitWidth + this.gutter) * 2
29730         });
29731
29732         return pos;
29733         
29734     }
29735     
29736 });
29737
29738  
29739
29740  /**
29741  *
29742  * This is based on 
29743  * http://masonry.desandro.com
29744  *
29745  * The idea is to render all the bricks based on vertical width...
29746  *
29747  * The original code extends 'outlayer' - we might need to use that....
29748  * 
29749  */
29750
29751
29752 /**
29753  * @class Roo.bootstrap.LayoutMasonryAuto
29754  * @extends Roo.bootstrap.Component
29755  * Bootstrap Layout Masonry class
29756  * 
29757  * @constructor
29758  * Create a new Element
29759  * @param {Object} config The config object
29760  */
29761
29762 Roo.bootstrap.LayoutMasonryAuto = function(config){
29763     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29764 };
29765
29766 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
29767     
29768       /**
29769      * @cfg {Boolean} isFitWidth  - resize the width..
29770      */   
29771     isFitWidth : false,  // options..
29772     /**
29773      * @cfg {Boolean} isOriginLeft = left align?
29774      */   
29775     isOriginLeft : true,
29776     /**
29777      * @cfg {Boolean} isOriginTop = top align?
29778      */   
29779     isOriginTop : false,
29780     /**
29781      * @cfg {Boolean} isLayoutInstant = no animation?
29782      */   
29783     isLayoutInstant : false, // needed?
29784     /**
29785      * @cfg {Boolean} isResizingContainer = not sure if this is used..
29786      */   
29787     isResizingContainer : true,
29788     /**
29789      * @cfg {Number} columnWidth  width of the columns 
29790      */   
29791     
29792     columnWidth : 0,
29793     
29794     /**
29795      * @cfg {Number} maxCols maximum number of columns
29796      */   
29797     
29798     maxCols: 0,
29799     /**
29800      * @cfg {Number} padHeight padding below box..
29801      */   
29802     
29803     padHeight : 10, 
29804     
29805     /**
29806      * @cfg {Boolean} isAutoInitial defalut true
29807      */   
29808     
29809     isAutoInitial : true, 
29810     
29811     // private?
29812     gutter : 0,
29813     
29814     containerWidth: 0,
29815     initialColumnWidth : 0,
29816     currentSize : null,
29817     
29818     colYs : null, // array.
29819     maxY : 0,
29820     padWidth: 10,
29821     
29822     
29823     tag: 'div',
29824     cls: '',
29825     bricks: null, //CompositeElement
29826     cols : 0, // array?
29827     // element : null, // wrapped now this.el
29828     _isLayoutInited : null, 
29829     
29830     
29831     getAutoCreate : function(){
29832         
29833         var cfg = {
29834             tag: this.tag,
29835             cls: 'blog-masonary-wrapper ' + this.cls,
29836             cn : {
29837                 cls : 'mas-boxes masonary'
29838             }
29839         };
29840         
29841         return cfg;
29842     },
29843     
29844     getChildContainer: function( )
29845     {
29846         if (this.boxesEl) {
29847             return this.boxesEl;
29848         }
29849         
29850         this.boxesEl = this.el.select('.mas-boxes').first();
29851         
29852         return this.boxesEl;
29853     },
29854     
29855     
29856     initEvents : function()
29857     {
29858         var _this = this;
29859         
29860         if(this.isAutoInitial){
29861             Roo.log('hook children rendered');
29862             this.on('childrenrendered', function() {
29863                 Roo.log('children rendered');
29864                 _this.initial();
29865             } ,this);
29866         }
29867         
29868     },
29869     
29870     initial : function()
29871     {
29872         this.reloadItems();
29873
29874         this.currentSize = this.el.getBox(true);
29875
29876         /// was window resize... - let's see if this works..
29877         Roo.EventManager.onWindowResize(this.resize, this); 
29878
29879         if(!this.isAutoInitial){
29880             this.layout();
29881             return;
29882         }
29883         
29884         this.layout.defer(500,this);
29885     },
29886     
29887     reloadItems: function()
29888     {
29889         this.bricks = this.el.select('.masonry-brick', true);
29890         
29891         this.bricks.each(function(b) {
29892             //Roo.log(b.getSize());
29893             if (!b.attr('originalwidth')) {
29894                 b.attr('originalwidth',  b.getSize().width);
29895             }
29896             
29897         });
29898         
29899         Roo.log(this.bricks.elements.length);
29900     },
29901     
29902     resize : function()
29903     {
29904         Roo.log('resize');
29905         var cs = this.el.getBox(true);
29906         
29907         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29908             Roo.log("no change in with or X");
29909             return;
29910         }
29911         this.currentSize = cs;
29912         this.layout();
29913     },
29914     
29915     layout : function()
29916     {
29917          Roo.log('layout');
29918         this._resetLayout();
29919         //this._manageStamps();
29920       
29921         // don't animate first layout
29922         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29923         this.layoutItems( isInstant );
29924       
29925         // flag for initalized
29926         this._isLayoutInited = true;
29927     },
29928     
29929     layoutItems : function( isInstant )
29930     {
29931         //var items = this._getItemsForLayout( this.items );
29932         // original code supports filtering layout items.. we just ignore it..
29933         
29934         this._layoutItems( this.bricks , isInstant );
29935       
29936         this._postLayout();
29937     },
29938     _layoutItems : function ( items , isInstant)
29939     {
29940        //this.fireEvent( 'layout', this, items );
29941     
29942
29943         if ( !items || !items.elements.length ) {
29944           // no items, emit event with empty array
29945             return;
29946         }
29947
29948         var queue = [];
29949         items.each(function(item) {
29950             Roo.log("layout item");
29951             Roo.log(item);
29952             // get x/y object from method
29953             var position = this._getItemLayoutPosition( item );
29954             // enqueue
29955             position.item = item;
29956             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29957             queue.push( position );
29958         }, this);
29959       
29960         this._processLayoutQueue( queue );
29961     },
29962     /** Sets position of item in DOM
29963     * @param {Element} item
29964     * @param {Number} x - horizontal position
29965     * @param {Number} y - vertical position
29966     * @param {Boolean} isInstant - disables transitions
29967     */
29968     _processLayoutQueue : function( queue )
29969     {
29970         for ( var i=0, len = queue.length; i < len; i++ ) {
29971             var obj = queue[i];
29972             obj.item.position('absolute');
29973             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29974         }
29975     },
29976       
29977     
29978     /**
29979     * Any logic you want to do after each layout,
29980     * i.e. size the container
29981     */
29982     _postLayout : function()
29983     {
29984         this.resizeContainer();
29985     },
29986     
29987     resizeContainer : function()
29988     {
29989         if ( !this.isResizingContainer ) {
29990             return;
29991         }
29992         var size = this._getContainerSize();
29993         if ( size ) {
29994             this.el.setSize(size.width,size.height);
29995             this.boxesEl.setSize(size.width,size.height);
29996         }
29997     },
29998     
29999     
30000     
30001     _resetLayout : function()
30002     {
30003         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30004         this.colWidth = this.el.getWidth();
30005         //this.gutter = this.el.getWidth(); 
30006         
30007         this.measureColumns();
30008
30009         // reset column Y
30010         var i = this.cols;
30011         this.colYs = [];
30012         while (i--) {
30013             this.colYs.push( 0 );
30014         }
30015     
30016         this.maxY = 0;
30017     },
30018
30019     measureColumns : function()
30020     {
30021         this.getContainerWidth();
30022       // if columnWidth is 0, default to outerWidth of first item
30023         if ( !this.columnWidth ) {
30024             var firstItem = this.bricks.first();
30025             Roo.log(firstItem);
30026             this.columnWidth  = this.containerWidth;
30027             if (firstItem && firstItem.attr('originalwidth') ) {
30028                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30029             }
30030             // columnWidth fall back to item of first element
30031             Roo.log("set column width?");
30032                         this.initialColumnWidth = this.columnWidth  ;
30033
30034             // if first elem has no width, default to size of container
30035             
30036         }
30037         
30038         
30039         if (this.initialColumnWidth) {
30040             this.columnWidth = this.initialColumnWidth;
30041         }
30042         
30043         
30044             
30045         // column width is fixed at the top - however if container width get's smaller we should
30046         // reduce it...
30047         
30048         // this bit calcs how man columns..
30049             
30050         var columnWidth = this.columnWidth += this.gutter;
30051       
30052         // calculate columns
30053         var containerWidth = this.containerWidth + this.gutter;
30054         
30055         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30056         // fix rounding errors, typically with gutters
30057         var excess = columnWidth - containerWidth % columnWidth;
30058         
30059         
30060         // if overshoot is less than a pixel, round up, otherwise floor it
30061         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30062         cols = Math[ mathMethod ]( cols );
30063         this.cols = Math.max( cols, 1 );
30064         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30065         
30066          // padding positioning..
30067         var totalColWidth = this.cols * this.columnWidth;
30068         var padavail = this.containerWidth - totalColWidth;
30069         // so for 2 columns - we need 3 'pads'
30070         
30071         var padNeeded = (1+this.cols) * this.padWidth;
30072         
30073         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30074         
30075         this.columnWidth += padExtra
30076         //this.padWidth = Math.floor(padavail /  ( this.cols));
30077         
30078         // adjust colum width so that padding is fixed??
30079         
30080         // we have 3 columns ... total = width * 3
30081         // we have X left over... that should be used by 
30082         
30083         //if (this.expandC) {
30084             
30085         //}
30086         
30087         
30088         
30089     },
30090     
30091     getContainerWidth : function()
30092     {
30093        /* // container is parent if fit width
30094         var container = this.isFitWidth ? this.element.parentNode : this.element;
30095         // check that this.size and size are there
30096         // IE8 triggers resize on body size change, so they might not be
30097         
30098         var size = getSize( container );  //FIXME
30099         this.containerWidth = size && size.innerWidth; //FIXME
30100         */
30101          
30102         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30103         
30104     },
30105     
30106     _getItemLayoutPosition : function( item )  // what is item?
30107     {
30108         // we resize the item to our columnWidth..
30109       
30110         item.setWidth(this.columnWidth);
30111         item.autoBoxAdjust  = false;
30112         
30113         var sz = item.getSize();
30114  
30115         // how many columns does this brick span
30116         var remainder = this.containerWidth % this.columnWidth;
30117         
30118         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30119         // round if off by 1 pixel, otherwise use ceil
30120         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30121         colSpan = Math.min( colSpan, this.cols );
30122         
30123         // normally this should be '1' as we dont' currently allow multi width columns..
30124         
30125         var colGroup = this._getColGroup( colSpan );
30126         // get the minimum Y value from the columns
30127         var minimumY = Math.min.apply( Math, colGroup );
30128         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30129         
30130         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30131          
30132         // position the brick
30133         var position = {
30134             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30135             y: this.currentSize.y + minimumY + this.padHeight
30136         };
30137         
30138         Roo.log(position);
30139         // apply setHeight to necessary columns
30140         var setHeight = minimumY + sz.height + this.padHeight;
30141         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30142         
30143         var setSpan = this.cols + 1 - colGroup.length;
30144         for ( var i = 0; i < setSpan; i++ ) {
30145           this.colYs[ shortColIndex + i ] = setHeight ;
30146         }
30147       
30148         return position;
30149     },
30150     
30151     /**
30152      * @param {Number} colSpan - number of columns the element spans
30153      * @returns {Array} colGroup
30154      */
30155     _getColGroup : function( colSpan )
30156     {
30157         if ( colSpan < 2 ) {
30158           // if brick spans only one column, use all the column Ys
30159           return this.colYs;
30160         }
30161       
30162         var colGroup = [];
30163         // how many different places could this brick fit horizontally
30164         var groupCount = this.cols + 1 - colSpan;
30165         // for each group potential horizontal position
30166         for ( var i = 0; i < groupCount; i++ ) {
30167           // make an array of colY values for that one group
30168           var groupColYs = this.colYs.slice( i, i + colSpan );
30169           // and get the max value of the array
30170           colGroup[i] = Math.max.apply( Math, groupColYs );
30171         }
30172         return colGroup;
30173     },
30174     /*
30175     _manageStamp : function( stamp )
30176     {
30177         var stampSize =  stamp.getSize();
30178         var offset = stamp.getBox();
30179         // get the columns that this stamp affects
30180         var firstX = this.isOriginLeft ? offset.x : offset.right;
30181         var lastX = firstX + stampSize.width;
30182         var firstCol = Math.floor( firstX / this.columnWidth );
30183         firstCol = Math.max( 0, firstCol );
30184         
30185         var lastCol = Math.floor( lastX / this.columnWidth );
30186         // lastCol should not go over if multiple of columnWidth #425
30187         lastCol -= lastX % this.columnWidth ? 0 : 1;
30188         lastCol = Math.min( this.cols - 1, lastCol );
30189         
30190         // set colYs to bottom of the stamp
30191         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30192             stampSize.height;
30193             
30194         for ( var i = firstCol; i <= lastCol; i++ ) {
30195           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30196         }
30197     },
30198     */
30199     
30200     _getContainerSize : function()
30201     {
30202         this.maxY = Math.max.apply( Math, this.colYs );
30203         var size = {
30204             height: this.maxY
30205         };
30206       
30207         if ( this.isFitWidth ) {
30208             size.width = this._getContainerFitWidth();
30209         }
30210       
30211         return size;
30212     },
30213     
30214     _getContainerFitWidth : function()
30215     {
30216         var unusedCols = 0;
30217         // count unused columns
30218         var i = this.cols;
30219         while ( --i ) {
30220           if ( this.colYs[i] !== 0 ) {
30221             break;
30222           }
30223           unusedCols++;
30224         }
30225         // fit container to columns that have been used
30226         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30227     },
30228     
30229     needsResizeLayout : function()
30230     {
30231         var previousWidth = this.containerWidth;
30232         this.getContainerWidth();
30233         return previousWidth !== this.containerWidth;
30234     }
30235  
30236 });
30237
30238  
30239
30240  /*
30241  * - LGPL
30242  *
30243  * element
30244  * 
30245  */
30246
30247 /**
30248  * @class Roo.bootstrap.MasonryBrick
30249  * @extends Roo.bootstrap.Component
30250  * Bootstrap MasonryBrick class
30251  * 
30252  * @constructor
30253  * Create a new MasonryBrick
30254  * @param {Object} config The config object
30255  */
30256
30257 Roo.bootstrap.MasonryBrick = function(config){
30258     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30259     
30260     this.addEvents({
30261         // raw events
30262         /**
30263          * @event click
30264          * When a MasonryBrick is clcik
30265          * @param {Roo.bootstrap.MasonryBrick} this
30266          * @param {Roo.EventObject} e
30267          */
30268         "click" : true
30269     });
30270 };
30271
30272 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30273     
30274     /**
30275      * @cfg {String} title
30276      */   
30277     title : '',
30278     /**
30279      * @cfg {String} html
30280      */   
30281     html : '',
30282     /**
30283      * @cfg {String} bgimage
30284      */   
30285     bgimage : '',
30286     /**
30287      * @cfg {String} videourl
30288      */   
30289     videourl : '',
30290     /**
30291      * @cfg {String} cls
30292      */   
30293     cls : '',
30294     /**
30295      * @cfg {String} href
30296      */   
30297     href : '',
30298     /**
30299      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30300      */   
30301     size : 'xs',
30302     
30303     /**
30304      * @cfg {String} (center|bottom) placetitle
30305      */   
30306     placetitle : '',
30307     
30308     getAutoCreate : function()
30309     {
30310         var cls = 'masonry-brick';
30311         
30312         if(this.href.length){
30313             cls += ' masonry-brick-link';
30314         }
30315         
30316         if(this.bgimage.length){
30317             cls += ' masonry-brick-image';
30318         }
30319         
30320         if(this.size){
30321             cls += ' masonry-' + this.size + '-brick';
30322         }
30323         
30324         if(this.placetitle.length){
30325             
30326             switch (this.placetitle) {
30327                 case 'center' :
30328                     cls += ' masonry-center-title';
30329                     break;
30330                 case 'bottom' :
30331                     cls += ' masonry-bottom-title';
30332                     break;
30333                 default:
30334                     break;
30335             }
30336             
30337         } else {
30338             if(!this.html.length && !this.bgimage.length){
30339                 cls += ' masonry-center-title';
30340             }
30341
30342             if(!this.html.length && this.bgimage.length){
30343                 cls += ' masonry-bottom-title';
30344             }
30345         }
30346         
30347         if(this.cls){
30348             cls += ' ' + this.cls;
30349         }
30350         
30351         var cfg = {
30352             tag: (this.href.length) ? 'a' : 'div',
30353             cls: cls,
30354             cn: [
30355                 {
30356                     tag: 'div',
30357                     cls: 'masonry-brick-paragraph',
30358                     cn: []
30359                 }
30360             ]
30361         };
30362         
30363         if(this.href.length){
30364             cfg.href = this.href;
30365         }
30366         
30367         var cn = cfg.cn[0].cn;
30368         
30369         if(this.title.length){
30370             cn.push({
30371                 tag: 'h4',
30372                 cls: 'masonry-brick-title',
30373                 html: this.title
30374             });
30375         }
30376         
30377         if(this.html.length){
30378             cn.push({
30379                 tag: 'p',
30380                 cls: 'masonry-brick-text',
30381                 html: this.html
30382             });
30383         }  
30384         if (!this.title.length && !this.html.length) {
30385             cfg.cn[0].cls += ' hide';
30386         }
30387         
30388         if(this.bgimage.length){
30389             cfg.cn.push({
30390                 tag: 'img',
30391                 cls: 'masonry-brick-image-view',
30392                 src: this.bgimage
30393             });
30394         }
30395         if(this.videourl.length){
30396             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30397             // youtube support only?
30398             cfg.cn.push({
30399                 tag: 'iframe',
30400                 cls: 'masonry-brick-image-view',
30401                 src: vurl,
30402                 frameborder : 0,
30403                 allowfullscreen : true
30404             });
30405             
30406             
30407         }
30408         return cfg;
30409         
30410     },
30411     
30412     initEvents: function() 
30413     {
30414         switch (this.size) {
30415             case 'xs' :
30416 //                this.intSize = 1;
30417                 this.x = 1;
30418                 this.y = 1;
30419                 break;
30420             case 'sm' :
30421 //                this.intSize = 2;
30422                 this.x = 2;
30423                 this.y = 2;
30424                 break;
30425             case 'md' :
30426             case 'md-left' :
30427             case 'md-right' :
30428 //                this.intSize = 3;
30429                 this.x = 3;
30430                 this.y = 3;
30431                 break;
30432             case 'tall' :
30433 //                this.intSize = 3;
30434                 this.x = 2;
30435                 this.y = 3;
30436                 break;
30437             case 'wide' :
30438 //                this.intSize = 3;
30439                 this.x = 3;
30440                 this.y = 2;
30441                 break;
30442             case 'wide-thin' :
30443 //                this.intSize = 3;
30444                 this.x = 3;
30445                 this.y = 1;
30446                 break;
30447                         
30448             default :
30449                 break;
30450         }
30451         
30452         
30453         
30454         if(Roo.isTouch){
30455             this.el.on('touchstart', this.onTouchStart, this);
30456             this.el.on('touchmove', this.onTouchMove, this);
30457             this.el.on('touchend', this.onTouchEnd, this);
30458             this.el.on('contextmenu', this.onContextMenu, this);
30459         } else {
30460             this.el.on('mouseenter'  ,this.enter, this);
30461             this.el.on('mouseleave', this.leave, this);
30462         }
30463         
30464         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30465             this.parent().bricks.push(this);   
30466         }
30467         
30468     },
30469     
30470     onClick: function(e, el)
30471     {
30472         if(!Roo.isTouch){
30473             return;
30474         }
30475         
30476         var time = this.endTimer - this.startTimer;
30477         
30478         //alert(time);
30479         
30480         if(time < 1000){
30481             return;
30482         }
30483         
30484         e.preventDefault();
30485     },
30486     
30487     enter: function(e, el)
30488     {
30489         e.preventDefault();
30490         
30491         if(this.bgimage.length && this.html.length){
30492             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30493         }
30494     },
30495     
30496     leave: function(e, el)
30497     {
30498         e.preventDefault();
30499         
30500         if(this.bgimage.length && this.html.length){
30501             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30502         }
30503     },
30504     
30505     onTouchStart: function(e, el)
30506     {
30507 //        e.preventDefault();
30508         
30509         this.touchmoved = false;
30510         
30511         if(!this.bgimage.length || !this.html.length){
30512             return;
30513         }
30514         
30515         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30516         
30517         this.timer = new Date().getTime();
30518         
30519     },
30520     
30521     onTouchMove: function(e, el)
30522     {
30523         this.touchmoved = true;
30524     },
30525     
30526     onContextMenu : function(e,el)
30527     {
30528         e.preventDefault();
30529         e.stopPropagation();
30530         return false;
30531     },
30532     
30533     onTouchEnd: function(e, el)
30534     {
30535 //        e.preventDefault();
30536         
30537         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30538         
30539             this.leave(e,el);
30540             
30541             return;
30542         }
30543         
30544         if(!this.bgimage.length || !this.html.length){
30545             
30546             if(this.href.length){
30547                 window.location.href = this.href;
30548             }
30549             
30550             return;
30551         }
30552         
30553         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30554         
30555         window.location.href = this.href;
30556     }
30557     
30558 });
30559
30560  
30561
30562  /*
30563  * - LGPL
30564  *
30565  * element
30566  * 
30567  */
30568
30569 /**
30570  * @class Roo.bootstrap.Brick
30571  * @extends Roo.bootstrap.Component
30572  * Bootstrap Brick class
30573  * 
30574  * @constructor
30575  * Create a new Brick
30576  * @param {Object} config The config object
30577  */
30578
30579 Roo.bootstrap.Brick = function(config){
30580     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30581     
30582     this.addEvents({
30583         // raw events
30584         /**
30585          * @event click
30586          * When a Brick is click
30587          * @param {Roo.bootstrap.Brick} this
30588          * @param {Roo.EventObject} e
30589          */
30590         "click" : true
30591     });
30592 };
30593
30594 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30595     
30596     /**
30597      * @cfg {String} title
30598      */   
30599     title : '',
30600     /**
30601      * @cfg {String} html
30602      */   
30603     html : '',
30604     /**
30605      * @cfg {String} bgimage
30606      */   
30607     bgimage : '',
30608     /**
30609      * @cfg {String} cls
30610      */   
30611     cls : '',
30612     /**
30613      * @cfg {String} href
30614      */   
30615     href : '',
30616     /**
30617      * @cfg {String} video
30618      */   
30619     video : '',
30620     /**
30621      * @cfg {Boolean} square
30622      */   
30623     square : true,
30624     
30625     getAutoCreate : function()
30626     {
30627         var cls = 'roo-brick';
30628         
30629         if(this.href.length){
30630             cls += ' roo-brick-link';
30631         }
30632         
30633         if(this.bgimage.length){
30634             cls += ' roo-brick-image';
30635         }
30636         
30637         if(!this.html.length && !this.bgimage.length){
30638             cls += ' roo-brick-center-title';
30639         }
30640         
30641         if(!this.html.length && this.bgimage.length){
30642             cls += ' roo-brick-bottom-title';
30643         }
30644         
30645         if(this.cls){
30646             cls += ' ' + this.cls;
30647         }
30648         
30649         var cfg = {
30650             tag: (this.href.length) ? 'a' : 'div',
30651             cls: cls,
30652             cn: [
30653                 {
30654                     tag: 'div',
30655                     cls: 'roo-brick-paragraph',
30656                     cn: []
30657                 }
30658             ]
30659         };
30660         
30661         if(this.href.length){
30662             cfg.href = this.href;
30663         }
30664         
30665         var cn = cfg.cn[0].cn;
30666         
30667         if(this.title.length){
30668             cn.push({
30669                 tag: 'h4',
30670                 cls: 'roo-brick-title',
30671                 html: this.title
30672             });
30673         }
30674         
30675         if(this.html.length){
30676             cn.push({
30677                 tag: 'p',
30678                 cls: 'roo-brick-text',
30679                 html: this.html
30680             });
30681         } else {
30682             cn.cls += ' hide';
30683         }
30684         
30685         if(this.bgimage.length){
30686             cfg.cn.push({
30687                 tag: 'img',
30688                 cls: 'roo-brick-image-view',
30689                 src: this.bgimage
30690             });
30691         }
30692         
30693         return cfg;
30694     },
30695     
30696     initEvents: function() 
30697     {
30698         if(this.title.length || this.html.length){
30699             this.el.on('mouseenter'  ,this.enter, this);
30700             this.el.on('mouseleave', this.leave, this);
30701         }
30702         
30703         
30704         Roo.EventManager.onWindowResize(this.resize, this); 
30705         
30706         this.resize();
30707     },
30708     
30709     resize : function()
30710     {
30711         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30712         
30713         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30714 //        paragraph.setHeight(paragraph.getWidth());
30715         
30716         if(this.bgimage.length){
30717             var image = this.el.select('.roo-brick-image-view', true).first();
30718             image.setWidth(paragraph.getWidth());
30719             image.setHeight(paragraph.getWidth());
30720         }
30721         
30722     },
30723     
30724     enter: function(e, el)
30725     {
30726         e.preventDefault();
30727         
30728         if(this.bgimage.length){
30729             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30730             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30731         }
30732     },
30733     
30734     leave: function(e, el)
30735     {
30736         e.preventDefault();
30737         
30738         if(this.bgimage.length){
30739             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30740             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30741         }
30742     }
30743     
30744 });
30745
30746  
30747
30748  /*
30749  * Based on:
30750  * Ext JS Library 1.1.1
30751  * Copyright(c) 2006-2007, Ext JS, LLC.
30752  *
30753  * Originally Released Under LGPL - original licence link has changed is not relivant.
30754  *
30755  * Fork - LGPL
30756  * <script type="text/javascript">
30757  */
30758
30759
30760 /**
30761  * @class Roo.bootstrap.SplitBar
30762  * @extends Roo.util.Observable
30763  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30764  * <br><br>
30765  * Usage:
30766  * <pre><code>
30767 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30768                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30769 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30770 split.minSize = 100;
30771 split.maxSize = 600;
30772 split.animate = true;
30773 split.on('moved', splitterMoved);
30774 </code></pre>
30775  * @constructor
30776  * Create a new SplitBar
30777  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
30778  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
30779  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30780  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
30781                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30782                         position of the SplitBar).
30783  */
30784 Roo.bootstrap.SplitBar = function(cfg){
30785     
30786     /** @private */
30787     
30788     //{
30789     //  dragElement : elm
30790     //  resizingElement: el,
30791         // optional..
30792     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30793     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
30794         // existingProxy ???
30795     //}
30796     
30797     this.el = Roo.get(cfg.dragElement, true);
30798     this.el.dom.unselectable = "on";
30799     /** @private */
30800     this.resizingEl = Roo.get(cfg.resizingElement, true);
30801
30802     /**
30803      * @private
30804      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30805      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30806      * @type Number
30807      */
30808     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30809     
30810     /**
30811      * The minimum size of the resizing element. (Defaults to 0)
30812      * @type Number
30813      */
30814     this.minSize = 0;
30815     
30816     /**
30817      * The maximum size of the resizing element. (Defaults to 2000)
30818      * @type Number
30819      */
30820     this.maxSize = 2000;
30821     
30822     /**
30823      * Whether to animate the transition to the new size
30824      * @type Boolean
30825      */
30826     this.animate = false;
30827     
30828     /**
30829      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30830      * @type Boolean
30831      */
30832     this.useShim = false;
30833     
30834     /** @private */
30835     this.shim = null;
30836     
30837     if(!cfg.existingProxy){
30838         /** @private */
30839         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30840     }else{
30841         this.proxy = Roo.get(cfg.existingProxy).dom;
30842     }
30843     /** @private */
30844     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30845     
30846     /** @private */
30847     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30848     
30849     /** @private */
30850     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30851     
30852     /** @private */
30853     this.dragSpecs = {};
30854     
30855     /**
30856      * @private The adapter to use to positon and resize elements
30857      */
30858     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30859     this.adapter.init(this);
30860     
30861     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30862         /** @private */
30863         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30864         this.el.addClass("roo-splitbar-h");
30865     }else{
30866         /** @private */
30867         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30868         this.el.addClass("roo-splitbar-v");
30869     }
30870     
30871     this.addEvents({
30872         /**
30873          * @event resize
30874          * Fires when the splitter is moved (alias for {@link #event-moved})
30875          * @param {Roo.bootstrap.SplitBar} this
30876          * @param {Number} newSize the new width or height
30877          */
30878         "resize" : true,
30879         /**
30880          * @event moved
30881          * Fires when the splitter is moved
30882          * @param {Roo.bootstrap.SplitBar} this
30883          * @param {Number} newSize the new width or height
30884          */
30885         "moved" : true,
30886         /**
30887          * @event beforeresize
30888          * Fires before the splitter is dragged
30889          * @param {Roo.bootstrap.SplitBar} this
30890          */
30891         "beforeresize" : true,
30892
30893         "beforeapply" : true
30894     });
30895
30896     Roo.util.Observable.call(this);
30897 };
30898
30899 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30900     onStartProxyDrag : function(x, y){
30901         this.fireEvent("beforeresize", this);
30902         if(!this.overlay){
30903             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
30904             o.unselectable();
30905             o.enableDisplayMode("block");
30906             // all splitbars share the same overlay
30907             Roo.bootstrap.SplitBar.prototype.overlay = o;
30908         }
30909         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30910         this.overlay.show();
30911         Roo.get(this.proxy).setDisplayed("block");
30912         var size = this.adapter.getElementSize(this);
30913         this.activeMinSize = this.getMinimumSize();;
30914         this.activeMaxSize = this.getMaximumSize();;
30915         var c1 = size - this.activeMinSize;
30916         var c2 = Math.max(this.activeMaxSize - size, 0);
30917         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30918             this.dd.resetConstraints();
30919             this.dd.setXConstraint(
30920                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
30921                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30922             );
30923             this.dd.setYConstraint(0, 0);
30924         }else{
30925             this.dd.resetConstraints();
30926             this.dd.setXConstraint(0, 0);
30927             this.dd.setYConstraint(
30928                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
30929                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30930             );
30931          }
30932         this.dragSpecs.startSize = size;
30933         this.dragSpecs.startPoint = [x, y];
30934         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30935     },
30936     
30937     /** 
30938      * @private Called after the drag operation by the DDProxy
30939      */
30940     onEndProxyDrag : function(e){
30941         Roo.get(this.proxy).setDisplayed(false);
30942         var endPoint = Roo.lib.Event.getXY(e);
30943         if(this.overlay){
30944             this.overlay.hide();
30945         }
30946         var newSize;
30947         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30948             newSize = this.dragSpecs.startSize + 
30949                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30950                     endPoint[0] - this.dragSpecs.startPoint[0] :
30951                     this.dragSpecs.startPoint[0] - endPoint[0]
30952                 );
30953         }else{
30954             newSize = this.dragSpecs.startSize + 
30955                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30956                     endPoint[1] - this.dragSpecs.startPoint[1] :
30957                     this.dragSpecs.startPoint[1] - endPoint[1]
30958                 );
30959         }
30960         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30961         if(newSize != this.dragSpecs.startSize){
30962             if(this.fireEvent('beforeapply', this, newSize) !== false){
30963                 this.adapter.setElementSize(this, newSize);
30964                 this.fireEvent("moved", this, newSize);
30965                 this.fireEvent("resize", this, newSize);
30966             }
30967         }
30968     },
30969     
30970     /**
30971      * Get the adapter this SplitBar uses
30972      * @return The adapter object
30973      */
30974     getAdapter : function(){
30975         return this.adapter;
30976     },
30977     
30978     /**
30979      * Set the adapter this SplitBar uses
30980      * @param {Object} adapter A SplitBar adapter object
30981      */
30982     setAdapter : function(adapter){
30983         this.adapter = adapter;
30984         this.adapter.init(this);
30985     },
30986     
30987     /**
30988      * Gets the minimum size for the resizing element
30989      * @return {Number} The minimum size
30990      */
30991     getMinimumSize : function(){
30992         return this.minSize;
30993     },
30994     
30995     /**
30996      * Sets the minimum size for the resizing element
30997      * @param {Number} minSize The minimum size
30998      */
30999     setMinimumSize : function(minSize){
31000         this.minSize = minSize;
31001     },
31002     
31003     /**
31004      * Gets the maximum size for the resizing element
31005      * @return {Number} The maximum size
31006      */
31007     getMaximumSize : function(){
31008         return this.maxSize;
31009     },
31010     
31011     /**
31012      * Sets the maximum size for the resizing element
31013      * @param {Number} maxSize The maximum size
31014      */
31015     setMaximumSize : function(maxSize){
31016         this.maxSize = maxSize;
31017     },
31018     
31019     /**
31020      * Sets the initialize size for the resizing element
31021      * @param {Number} size The initial size
31022      */
31023     setCurrentSize : function(size){
31024         var oldAnimate = this.animate;
31025         this.animate = false;
31026         this.adapter.setElementSize(this, size);
31027         this.animate = oldAnimate;
31028     },
31029     
31030     /**
31031      * Destroy this splitbar. 
31032      * @param {Boolean} removeEl True to remove the element
31033      */
31034     destroy : function(removeEl){
31035         if(this.shim){
31036             this.shim.remove();
31037         }
31038         this.dd.unreg();
31039         this.proxy.parentNode.removeChild(this.proxy);
31040         if(removeEl){
31041             this.el.remove();
31042         }
31043     }
31044 });
31045
31046 /**
31047  * @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.
31048  */
31049 Roo.bootstrap.SplitBar.createProxy = function(dir){
31050     var proxy = new Roo.Element(document.createElement("div"));
31051     proxy.unselectable();
31052     var cls = 'roo-splitbar-proxy';
31053     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31054     document.body.appendChild(proxy.dom);
31055     return proxy.dom;
31056 };
31057
31058 /** 
31059  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31060  * Default Adapter. It assumes the splitter and resizing element are not positioned
31061  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31062  */
31063 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31064 };
31065
31066 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31067     // do nothing for now
31068     init : function(s){
31069     
31070     },
31071     /**
31072      * Called before drag operations to get the current size of the resizing element. 
31073      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31074      */
31075      getElementSize : function(s){
31076         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31077             return s.resizingEl.getWidth();
31078         }else{
31079             return s.resizingEl.getHeight();
31080         }
31081     },
31082     
31083     /**
31084      * Called after drag operations to set the size of the resizing element.
31085      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31086      * @param {Number} newSize The new size to set
31087      * @param {Function} onComplete A function to be invoked when resizing is complete
31088      */
31089     setElementSize : function(s, newSize, onComplete){
31090         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31091             if(!s.animate){
31092                 s.resizingEl.setWidth(newSize);
31093                 if(onComplete){
31094                     onComplete(s, newSize);
31095                 }
31096             }else{
31097                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31098             }
31099         }else{
31100             
31101             if(!s.animate){
31102                 s.resizingEl.setHeight(newSize);
31103                 if(onComplete){
31104                     onComplete(s, newSize);
31105                 }
31106             }else{
31107                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31108             }
31109         }
31110     }
31111 };
31112
31113 /** 
31114  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31115  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31116  * Adapter that  moves the splitter element to align with the resized sizing element. 
31117  * Used with an absolute positioned SplitBar.
31118  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31119  * document.body, make sure you assign an id to the body element.
31120  */
31121 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31122     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31123     this.container = Roo.get(container);
31124 };
31125
31126 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31127     init : function(s){
31128         this.basic.init(s);
31129     },
31130     
31131     getElementSize : function(s){
31132         return this.basic.getElementSize(s);
31133     },
31134     
31135     setElementSize : function(s, newSize, onComplete){
31136         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31137     },
31138     
31139     moveSplitter : function(s){
31140         var yes = Roo.bootstrap.SplitBar;
31141         switch(s.placement){
31142             case yes.LEFT:
31143                 s.el.setX(s.resizingEl.getRight());
31144                 break;
31145             case yes.RIGHT:
31146                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31147                 break;
31148             case yes.TOP:
31149                 s.el.setY(s.resizingEl.getBottom());
31150                 break;
31151             case yes.BOTTOM:
31152                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31153                 break;
31154         }
31155     }
31156 };
31157
31158 /**
31159  * Orientation constant - Create a vertical SplitBar
31160  * @static
31161  * @type Number
31162  */
31163 Roo.bootstrap.SplitBar.VERTICAL = 1;
31164
31165 /**
31166  * Orientation constant - Create a horizontal SplitBar
31167  * @static
31168  * @type Number
31169  */
31170 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31171
31172 /**
31173  * Placement constant - The resizing element is to the left of the splitter element
31174  * @static
31175  * @type Number
31176  */
31177 Roo.bootstrap.SplitBar.LEFT = 1;
31178
31179 /**
31180  * Placement constant - The resizing element is to the right of the splitter element
31181  * @static
31182  * @type Number
31183  */
31184 Roo.bootstrap.SplitBar.RIGHT = 2;
31185
31186 /**
31187  * Placement constant - The resizing element is positioned above the splitter element
31188  * @static
31189  * @type Number
31190  */
31191 Roo.bootstrap.SplitBar.TOP = 3;
31192
31193 /**
31194  * Placement constant - The resizing element is positioned under splitter element
31195  * @static
31196  * @type Number
31197  */
31198 Roo.bootstrap.SplitBar.BOTTOM = 4;
31199 Roo.namespace("Roo.bootstrap.layout");/*
31200  * Based on:
31201  * Ext JS Library 1.1.1
31202  * Copyright(c) 2006-2007, Ext JS, LLC.
31203  *
31204  * Originally Released Under LGPL - original licence link has changed is not relivant.
31205  *
31206  * Fork - LGPL
31207  * <script type="text/javascript">
31208  */
31209  
31210 /**
31211  * @class Roo.bootstrap.layout.Manager
31212  * @extends Roo.bootstrap.Component
31213  * Base class for layout managers.
31214  */
31215 Roo.bootstrap.layout.Manager = function(config)
31216 {
31217     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31218     
31219     
31220      
31221     
31222     
31223     /** false to disable window resize monitoring @type Boolean */
31224     this.monitorWindowResize = true;
31225     this.regions = {};
31226     this.addEvents({
31227         /**
31228          * @event layout
31229          * Fires when a layout is performed. 
31230          * @param {Roo.LayoutManager} this
31231          */
31232         "layout" : true,
31233         /**
31234          * @event regionresized
31235          * Fires when the user resizes a region. 
31236          * @param {Roo.LayoutRegion} region The resized region
31237          * @param {Number} newSize The new size (width for east/west, height for north/south)
31238          */
31239         "regionresized" : true,
31240         /**
31241          * @event regioncollapsed
31242          * Fires when a region is collapsed. 
31243          * @param {Roo.LayoutRegion} region The collapsed region
31244          */
31245         "regioncollapsed" : true,
31246         /**
31247          * @event regionexpanded
31248          * Fires when a region is expanded.  
31249          * @param {Roo.LayoutRegion} region The expanded region
31250          */
31251         "regionexpanded" : true
31252     });
31253     this.updating = false;
31254     
31255     if (config.el) {
31256         this.el = Roo.get(config.el);
31257         this.initEvents();
31258     }
31259     
31260 };
31261
31262 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31263     
31264     
31265     regions : null,
31266     
31267     monitorWindowResize : true,
31268     
31269     
31270     updating : false,
31271     
31272     
31273     onRender : function(ct, position)
31274     {
31275         if(!this.el){
31276             this.el = Roo.get(ct);
31277             this.initEvents();
31278         }
31279     },
31280     
31281     
31282     initEvents: function()
31283     {
31284         
31285         
31286         // ie scrollbar fix
31287         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31288             document.body.scroll = "no";
31289         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31290             this.el.position('relative');
31291         }
31292         this.id = this.el.id;
31293         this.el.addClass("roo-layout-container");
31294         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31295         if(this.el.dom != document.body ) {
31296             this.el.on('resize', this.layout,this);
31297             this.el.on('show', this.layout,this);
31298         }
31299
31300     },
31301     
31302     /**
31303      * Returns true if this layout is currently being updated
31304      * @return {Boolean}
31305      */
31306     isUpdating : function(){
31307         return this.updating; 
31308     },
31309     
31310     /**
31311      * Suspend the LayoutManager from doing auto-layouts while
31312      * making multiple add or remove calls
31313      */
31314     beginUpdate : function(){
31315         this.updating = true;    
31316     },
31317     
31318     /**
31319      * Restore auto-layouts and optionally disable the manager from performing a layout
31320      * @param {Boolean} noLayout true to disable a layout update 
31321      */
31322     endUpdate : function(noLayout){
31323         this.updating = false;
31324         if(!noLayout){
31325             this.layout();
31326         }    
31327     },
31328     
31329     layout: function(){
31330         // abstract...
31331     },
31332     
31333     onRegionResized : function(region, newSize){
31334         this.fireEvent("regionresized", region, newSize);
31335         this.layout();
31336     },
31337     
31338     onRegionCollapsed : function(region){
31339         this.fireEvent("regioncollapsed", region);
31340     },
31341     
31342     onRegionExpanded : function(region){
31343         this.fireEvent("regionexpanded", region);
31344     },
31345         
31346     /**
31347      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31348      * performs box-model adjustments.
31349      * @return {Object} The size as an object {width: (the width), height: (the height)}
31350      */
31351     getViewSize : function()
31352     {
31353         var size;
31354         if(this.el.dom != document.body){
31355             size = this.el.getSize();
31356         }else{
31357             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31358         }
31359         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31360         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31361         return size;
31362     },
31363     
31364     /**
31365      * Returns the Element this layout is bound to.
31366      * @return {Roo.Element}
31367      */
31368     getEl : function(){
31369         return this.el;
31370     },
31371     
31372     /**
31373      * Returns the specified region.
31374      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31375      * @return {Roo.LayoutRegion}
31376      */
31377     getRegion : function(target){
31378         return this.regions[target.toLowerCase()];
31379     },
31380     
31381     onWindowResize : function(){
31382         if(this.monitorWindowResize){
31383             this.layout();
31384         }
31385     }
31386 });/*
31387  * Based on:
31388  * Ext JS Library 1.1.1
31389  * Copyright(c) 2006-2007, Ext JS, LLC.
31390  *
31391  * Originally Released Under LGPL - original licence link has changed is not relivant.
31392  *
31393  * Fork - LGPL
31394  * <script type="text/javascript">
31395  */
31396 /**
31397  * @class Roo.bootstrap.layout.Border
31398  * @extends Roo.bootstrap.layout.Manager
31399  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31400  * please see: examples/bootstrap/nested.html<br><br>
31401  
31402 <b>The container the layout is rendered into can be either the body element or any other element.
31403 If it is not the body element, the container needs to either be an absolute positioned element,
31404 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31405 the container size if it is not the body element.</b>
31406
31407 * @constructor
31408 * Create a new Border
31409 * @param {Object} config Configuration options
31410  */
31411 Roo.bootstrap.layout.Border = function(config){
31412     config = config || {};
31413     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31414     
31415     
31416     
31417     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31418         if(config[region]){
31419             config[region].region = region;
31420             this.addRegion(config[region]);
31421         }
31422     },this);
31423     
31424 };
31425
31426 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31427
31428 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31429     /**
31430      * Creates and adds a new region if it doesn't already exist.
31431      * @param {String} target The target region key (north, south, east, west or center).
31432      * @param {Object} config The regions config object
31433      * @return {BorderLayoutRegion} The new region
31434      */
31435     addRegion : function(config)
31436     {
31437         if(!this.regions[config.region]){
31438             var r = this.factory(config);
31439             this.bindRegion(r);
31440         }
31441         return this.regions[config.region];
31442     },
31443
31444     // private (kinda)
31445     bindRegion : function(r){
31446         this.regions[r.config.region] = r;
31447         
31448         r.on("visibilitychange",    this.layout, this);
31449         r.on("paneladded",          this.layout, this);
31450         r.on("panelremoved",        this.layout, this);
31451         r.on("invalidated",         this.layout, this);
31452         r.on("resized",             this.onRegionResized, this);
31453         r.on("collapsed",           this.onRegionCollapsed, this);
31454         r.on("expanded",            this.onRegionExpanded, this);
31455     },
31456
31457     /**
31458      * Performs a layout update.
31459      */
31460     layout : function()
31461     {
31462         if(this.updating) {
31463             return;
31464         }
31465         var size = this.getViewSize();
31466         var w = size.width;
31467         var h = size.height;
31468         var centerW = w;
31469         var centerH = h;
31470         var centerY = 0;
31471         var centerX = 0;
31472         //var x = 0, y = 0;
31473
31474         var rs = this.regions;
31475         var north = rs["north"];
31476         var south = rs["south"]; 
31477         var west = rs["west"];
31478         var east = rs["east"];
31479         var center = rs["center"];
31480         //if(this.hideOnLayout){ // not supported anymore
31481             //c.el.setStyle("display", "none");
31482         //}
31483         if(north && north.isVisible()){
31484             var b = north.getBox();
31485             var m = north.getMargins();
31486             b.width = w - (m.left+m.right);
31487             b.x = m.left;
31488             b.y = m.top;
31489             centerY = b.height + b.y + m.bottom;
31490             centerH -= centerY;
31491             north.updateBox(this.safeBox(b));
31492         }
31493         if(south && south.isVisible()){
31494             var b = south.getBox();
31495             var m = south.getMargins();
31496             b.width = w - (m.left+m.right);
31497             b.x = m.left;
31498             var totalHeight = (b.height + m.top + m.bottom);
31499             b.y = h - totalHeight + m.top;
31500             centerH -= totalHeight;
31501             south.updateBox(this.safeBox(b));
31502         }
31503         if(west && west.isVisible()){
31504             var b = west.getBox();
31505             var m = west.getMargins();
31506             b.height = centerH - (m.top+m.bottom);
31507             b.x = m.left;
31508             b.y = centerY + m.top;
31509             var totalWidth = (b.width + m.left + m.right);
31510             centerX += totalWidth;
31511             centerW -= totalWidth;
31512             west.updateBox(this.safeBox(b));
31513         }
31514         if(east && east.isVisible()){
31515             var b = east.getBox();
31516             var m = east.getMargins();
31517             b.height = centerH - (m.top+m.bottom);
31518             var totalWidth = (b.width + m.left + m.right);
31519             b.x = w - totalWidth + m.left;
31520             b.y = centerY + m.top;
31521             centerW -= totalWidth;
31522             east.updateBox(this.safeBox(b));
31523         }
31524         if(center){
31525             var m = center.getMargins();
31526             var centerBox = {
31527                 x: centerX + m.left,
31528                 y: centerY + m.top,
31529                 width: centerW - (m.left+m.right),
31530                 height: centerH - (m.top+m.bottom)
31531             };
31532             //if(this.hideOnLayout){
31533                 //center.el.setStyle("display", "block");
31534             //}
31535             center.updateBox(this.safeBox(centerBox));
31536         }
31537         this.el.repaint();
31538         this.fireEvent("layout", this);
31539     },
31540
31541     // private
31542     safeBox : function(box){
31543         box.width = Math.max(0, box.width);
31544         box.height = Math.max(0, box.height);
31545         return box;
31546     },
31547
31548     /**
31549      * Adds a ContentPanel (or subclass) to this layout.
31550      * @param {String} target The target region key (north, south, east, west or center).
31551      * @param {Roo.ContentPanel} panel The panel to add
31552      * @return {Roo.ContentPanel} The added panel
31553      */
31554     add : function(target, panel){
31555          
31556         target = target.toLowerCase();
31557         return this.regions[target].add(panel);
31558     },
31559
31560     /**
31561      * Remove a ContentPanel (or subclass) to this layout.
31562      * @param {String} target The target region key (north, south, east, west or center).
31563      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31564      * @return {Roo.ContentPanel} The removed panel
31565      */
31566     remove : function(target, panel){
31567         target = target.toLowerCase();
31568         return this.regions[target].remove(panel);
31569     },
31570
31571     /**
31572      * Searches all regions for a panel with the specified id
31573      * @param {String} panelId
31574      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31575      */
31576     findPanel : function(panelId){
31577         var rs = this.regions;
31578         for(var target in rs){
31579             if(typeof rs[target] != "function"){
31580                 var p = rs[target].getPanel(panelId);
31581                 if(p){
31582                     return p;
31583                 }
31584             }
31585         }
31586         return null;
31587     },
31588
31589     /**
31590      * Searches all regions for a panel with the specified id and activates (shows) it.
31591      * @param {String/ContentPanel} panelId The panels id or the panel itself
31592      * @return {Roo.ContentPanel} The shown panel or null
31593      */
31594     showPanel : function(panelId) {
31595       var rs = this.regions;
31596       for(var target in rs){
31597          var r = rs[target];
31598          if(typeof r != "function"){
31599             if(r.hasPanel(panelId)){
31600                return r.showPanel(panelId);
31601             }
31602          }
31603       }
31604       return null;
31605    },
31606
31607    /**
31608      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31609      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31610      */
31611    /*
31612     restoreState : function(provider){
31613         if(!provider){
31614             provider = Roo.state.Manager;
31615         }
31616         var sm = new Roo.LayoutStateManager();
31617         sm.init(this, provider);
31618     },
31619 */
31620  
31621  
31622     /**
31623      * Adds a xtype elements to the layout.
31624      * <pre><code>
31625
31626 layout.addxtype({
31627        xtype : 'ContentPanel',
31628        region: 'west',
31629        items: [ .... ]
31630    }
31631 );
31632
31633 layout.addxtype({
31634         xtype : 'NestedLayoutPanel',
31635         region: 'west',
31636         layout: {
31637            center: { },
31638            west: { }   
31639         },
31640         items : [ ... list of content panels or nested layout panels.. ]
31641    }
31642 );
31643 </code></pre>
31644      * @param {Object} cfg Xtype definition of item to add.
31645      */
31646     addxtype : function(cfg)
31647     {
31648         // basically accepts a pannel...
31649         // can accept a layout region..!?!?
31650         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31651         
31652         
31653         // theory?  children can only be panels??
31654         
31655         //if (!cfg.xtype.match(/Panel$/)) {
31656         //    return false;
31657         //}
31658         var ret = false;
31659         
31660         if (typeof(cfg.region) == 'undefined') {
31661             Roo.log("Failed to add Panel, region was not set");
31662             Roo.log(cfg);
31663             return false;
31664         }
31665         var region = cfg.region;
31666         delete cfg.region;
31667         
31668           
31669         var xitems = [];
31670         if (cfg.items) {
31671             xitems = cfg.items;
31672             delete cfg.items;
31673         }
31674         var nb = false;
31675         
31676         switch(cfg.xtype) 
31677         {
31678             case 'Content':  // ContentPanel (el, cfg)
31679             case 'Scroll':  // ContentPanel (el, cfg)
31680             case 'View': 
31681                 cfg.autoCreate = true;
31682                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31683                 //} else {
31684                 //    var el = this.el.createChild();
31685                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31686                 //}
31687                 
31688                 this.add(region, ret);
31689                 break;
31690             
31691             /*
31692             case 'TreePanel': // our new panel!
31693                 cfg.el = this.el.createChild();
31694                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31695                 this.add(region, ret);
31696                 break;
31697             */
31698             
31699             case 'Nest': 
31700                 // create a new Layout (which is  a Border Layout...
31701                 
31702                 var clayout = cfg.layout;
31703                 clayout.el  = this.el.createChild();
31704                 clayout.items   = clayout.items  || [];
31705                 
31706                 delete cfg.layout;
31707                 
31708                 // replace this exitems with the clayout ones..
31709                 xitems = clayout.items;
31710                  
31711                 // force background off if it's in center...
31712                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31713                     cfg.background = false;
31714                 }
31715                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31716                 
31717                 
31718                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31719                 //console.log('adding nested layout panel '  + cfg.toSource());
31720                 this.add(region, ret);
31721                 nb = {}; /// find first...
31722                 break;
31723             
31724             case 'Grid':
31725                 
31726                 // needs grid and region
31727                 
31728                 //var el = this.getRegion(region).el.createChild();
31729                 /*
31730                  *var el = this.el.createChild();
31731                 // create the grid first...
31732                 cfg.grid.container = el;
31733                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31734                 */
31735                 
31736                 if (region == 'center' && this.active ) {
31737                     cfg.background = false;
31738                 }
31739                 
31740                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31741                 
31742                 this.add(region, ret);
31743                 /*
31744                 if (cfg.background) {
31745                     // render grid on panel activation (if panel background)
31746                     ret.on('activate', function(gp) {
31747                         if (!gp.grid.rendered) {
31748                     //        gp.grid.render(el);
31749                         }
31750                     });
31751                 } else {
31752                   //  cfg.grid.render(el);
31753                 }
31754                 */
31755                 break;
31756            
31757            
31758             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31759                 // it was the old xcomponent building that caused this before.
31760                 // espeically if border is the top element in the tree.
31761                 ret = this;
31762                 break; 
31763                 
31764                     
31765                 
31766                 
31767                 
31768             default:
31769                 /*
31770                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31771                     
31772                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31773                     this.add(region, ret);
31774                 } else {
31775                 */
31776                     Roo.log(cfg);
31777                     throw "Can not add '" + cfg.xtype + "' to Border";
31778                     return null;
31779              
31780                                 
31781              
31782         }
31783         this.beginUpdate();
31784         // add children..
31785         var region = '';
31786         var abn = {};
31787         Roo.each(xitems, function(i)  {
31788             region = nb && i.region ? i.region : false;
31789             
31790             var add = ret.addxtype(i);
31791            
31792             if (region) {
31793                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31794                 if (!i.background) {
31795                     abn[region] = nb[region] ;
31796                 }
31797             }
31798             
31799         });
31800         this.endUpdate();
31801
31802         // make the last non-background panel active..
31803         //if (nb) { Roo.log(abn); }
31804         if (nb) {
31805             
31806             for(var r in abn) {
31807                 region = this.getRegion(r);
31808                 if (region) {
31809                     // tried using nb[r], but it does not work..
31810                      
31811                     region.showPanel(abn[r]);
31812                    
31813                 }
31814             }
31815         }
31816         return ret;
31817         
31818     },
31819     
31820     
31821 // private
31822     factory : function(cfg)
31823     {
31824         
31825         var validRegions = Roo.bootstrap.layout.Border.regions;
31826
31827         var target = cfg.region;
31828         cfg.mgr = this;
31829         
31830         var r = Roo.bootstrap.layout;
31831         Roo.log(target);
31832         switch(target){
31833             case "north":
31834                 return new r.North(cfg);
31835             case "south":
31836                 return new r.South(cfg);
31837             case "east":
31838                 return new r.East(cfg);
31839             case "west":
31840                 return new r.West(cfg);
31841             case "center":
31842                 return new r.Center(cfg);
31843         }
31844         throw 'Layout region "'+target+'" not supported.';
31845     }
31846     
31847     
31848 });
31849  /*
31850  * Based on:
31851  * Ext JS Library 1.1.1
31852  * Copyright(c) 2006-2007, Ext JS, LLC.
31853  *
31854  * Originally Released Under LGPL - original licence link has changed is not relivant.
31855  *
31856  * Fork - LGPL
31857  * <script type="text/javascript">
31858  */
31859  
31860 /**
31861  * @class Roo.bootstrap.layout.Basic
31862  * @extends Roo.util.Observable
31863  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31864  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31865  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31866  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
31867  * @cfg {string}   region  the region that it inhabits..
31868  * @cfg {bool}   skipConfig skip config?
31869  * 
31870
31871  */
31872 Roo.bootstrap.layout.Basic = function(config){
31873     
31874     this.mgr = config.mgr;
31875     
31876     this.position = config.region;
31877     
31878     var skipConfig = config.skipConfig;
31879     
31880     this.events = {
31881         /**
31882          * @scope Roo.BasicLayoutRegion
31883          */
31884         
31885         /**
31886          * @event beforeremove
31887          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31888          * @param {Roo.LayoutRegion} this
31889          * @param {Roo.ContentPanel} panel The panel
31890          * @param {Object} e The cancel event object
31891          */
31892         "beforeremove" : true,
31893         /**
31894          * @event invalidated
31895          * Fires when the layout for this region is changed.
31896          * @param {Roo.LayoutRegion} this
31897          */
31898         "invalidated" : true,
31899         /**
31900          * @event visibilitychange
31901          * Fires when this region is shown or hidden 
31902          * @param {Roo.LayoutRegion} this
31903          * @param {Boolean} visibility true or false
31904          */
31905         "visibilitychange" : true,
31906         /**
31907          * @event paneladded
31908          * Fires when a panel is added. 
31909          * @param {Roo.LayoutRegion} this
31910          * @param {Roo.ContentPanel} panel The panel
31911          */
31912         "paneladded" : true,
31913         /**
31914          * @event panelremoved
31915          * Fires when a panel is removed. 
31916          * @param {Roo.LayoutRegion} this
31917          * @param {Roo.ContentPanel} panel The panel
31918          */
31919         "panelremoved" : true,
31920         /**
31921          * @event beforecollapse
31922          * Fires when this region before collapse.
31923          * @param {Roo.LayoutRegion} this
31924          */
31925         "beforecollapse" : true,
31926         /**
31927          * @event collapsed
31928          * Fires when this region is collapsed.
31929          * @param {Roo.LayoutRegion} this
31930          */
31931         "collapsed" : true,
31932         /**
31933          * @event expanded
31934          * Fires when this region is expanded.
31935          * @param {Roo.LayoutRegion} this
31936          */
31937         "expanded" : true,
31938         /**
31939          * @event slideshow
31940          * Fires when this region is slid into view.
31941          * @param {Roo.LayoutRegion} this
31942          */
31943         "slideshow" : true,
31944         /**
31945          * @event slidehide
31946          * Fires when this region slides out of view. 
31947          * @param {Roo.LayoutRegion} this
31948          */
31949         "slidehide" : true,
31950         /**
31951          * @event panelactivated
31952          * Fires when a panel is activated. 
31953          * @param {Roo.LayoutRegion} this
31954          * @param {Roo.ContentPanel} panel The activated panel
31955          */
31956         "panelactivated" : true,
31957         /**
31958          * @event resized
31959          * Fires when the user resizes this region. 
31960          * @param {Roo.LayoutRegion} this
31961          * @param {Number} newSize The new size (width for east/west, height for north/south)
31962          */
31963         "resized" : true
31964     };
31965     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31966     this.panels = new Roo.util.MixedCollection();
31967     this.panels.getKey = this.getPanelId.createDelegate(this);
31968     this.box = null;
31969     this.activePanel = null;
31970     // ensure listeners are added...
31971     
31972     if (config.listeners || config.events) {
31973         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31974             listeners : config.listeners || {},
31975             events : config.events || {}
31976         });
31977     }
31978     
31979     if(skipConfig !== true){
31980         this.applyConfig(config);
31981     }
31982 };
31983
31984 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31985 {
31986     getPanelId : function(p){
31987         return p.getId();
31988     },
31989     
31990     applyConfig : function(config){
31991         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31992         this.config = config;
31993         
31994     },
31995     
31996     /**
31997      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31998      * the width, for horizontal (north, south) the height.
31999      * @param {Number} newSize The new width or height
32000      */
32001     resizeTo : function(newSize){
32002         var el = this.el ? this.el :
32003                  (this.activePanel ? this.activePanel.getEl() : null);
32004         if(el){
32005             switch(this.position){
32006                 case "east":
32007                 case "west":
32008                     el.setWidth(newSize);
32009                     this.fireEvent("resized", this, newSize);
32010                 break;
32011                 case "north":
32012                 case "south":
32013                     el.setHeight(newSize);
32014                     this.fireEvent("resized", this, newSize);
32015                 break;                
32016             }
32017         }
32018     },
32019     
32020     getBox : function(){
32021         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32022     },
32023     
32024     getMargins : function(){
32025         return this.margins;
32026     },
32027     
32028     updateBox : function(box){
32029         this.box = box;
32030         var el = this.activePanel.getEl();
32031         el.dom.style.left = box.x + "px";
32032         el.dom.style.top = box.y + "px";
32033         this.activePanel.setSize(box.width, box.height);
32034     },
32035     
32036     /**
32037      * Returns the container element for this region.
32038      * @return {Roo.Element}
32039      */
32040     getEl : function(){
32041         return this.activePanel;
32042     },
32043     
32044     /**
32045      * Returns true if this region is currently visible.
32046      * @return {Boolean}
32047      */
32048     isVisible : function(){
32049         return this.activePanel ? true : false;
32050     },
32051     
32052     setActivePanel : function(panel){
32053         panel = this.getPanel(panel);
32054         if(this.activePanel && this.activePanel != panel){
32055             this.activePanel.setActiveState(false);
32056             this.activePanel.getEl().setLeftTop(-10000,-10000);
32057         }
32058         this.activePanel = panel;
32059         panel.setActiveState(true);
32060         if(this.box){
32061             panel.setSize(this.box.width, this.box.height);
32062         }
32063         this.fireEvent("panelactivated", this, panel);
32064         this.fireEvent("invalidated");
32065     },
32066     
32067     /**
32068      * Show the specified panel.
32069      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32070      * @return {Roo.ContentPanel} The shown panel or null
32071      */
32072     showPanel : function(panel){
32073         panel = this.getPanel(panel);
32074         if(panel){
32075             this.setActivePanel(panel);
32076         }
32077         return panel;
32078     },
32079     
32080     /**
32081      * Get the active panel for this region.
32082      * @return {Roo.ContentPanel} The active panel or null
32083      */
32084     getActivePanel : function(){
32085         return this.activePanel;
32086     },
32087     
32088     /**
32089      * Add the passed ContentPanel(s)
32090      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32091      * @return {Roo.ContentPanel} The panel added (if only one was added)
32092      */
32093     add : function(panel){
32094         if(arguments.length > 1){
32095             for(var i = 0, len = arguments.length; i < len; i++) {
32096                 this.add(arguments[i]);
32097             }
32098             return null;
32099         }
32100         if(this.hasPanel(panel)){
32101             this.showPanel(panel);
32102             return panel;
32103         }
32104         var el = panel.getEl();
32105         if(el.dom.parentNode != this.mgr.el.dom){
32106             this.mgr.el.dom.appendChild(el.dom);
32107         }
32108         if(panel.setRegion){
32109             panel.setRegion(this);
32110         }
32111         this.panels.add(panel);
32112         el.setStyle("position", "absolute");
32113         if(!panel.background){
32114             this.setActivePanel(panel);
32115             if(this.config.initialSize && this.panels.getCount()==1){
32116                 this.resizeTo(this.config.initialSize);
32117             }
32118         }
32119         this.fireEvent("paneladded", this, panel);
32120         return panel;
32121     },
32122     
32123     /**
32124      * Returns true if the panel is in this region.
32125      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32126      * @return {Boolean}
32127      */
32128     hasPanel : function(panel){
32129         if(typeof panel == "object"){ // must be panel obj
32130             panel = panel.getId();
32131         }
32132         return this.getPanel(panel) ? true : false;
32133     },
32134     
32135     /**
32136      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32137      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32138      * @param {Boolean} preservePanel Overrides the config preservePanel option
32139      * @return {Roo.ContentPanel} The panel that was removed
32140      */
32141     remove : function(panel, preservePanel){
32142         panel = this.getPanel(panel);
32143         if(!panel){
32144             return null;
32145         }
32146         var e = {};
32147         this.fireEvent("beforeremove", this, panel, e);
32148         if(e.cancel === true){
32149             return null;
32150         }
32151         var panelId = panel.getId();
32152         this.panels.removeKey(panelId);
32153         return panel;
32154     },
32155     
32156     /**
32157      * Returns the panel specified or null if it's not in this region.
32158      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32159      * @return {Roo.ContentPanel}
32160      */
32161     getPanel : function(id){
32162         if(typeof id == "object"){ // must be panel obj
32163             return id;
32164         }
32165         return this.panels.get(id);
32166     },
32167     
32168     /**
32169      * Returns this regions position (north/south/east/west/center).
32170      * @return {String} 
32171      */
32172     getPosition: function(){
32173         return this.position;    
32174     }
32175 });/*
32176  * Based on:
32177  * Ext JS Library 1.1.1
32178  * Copyright(c) 2006-2007, Ext JS, LLC.
32179  *
32180  * Originally Released Under LGPL - original licence link has changed is not relivant.
32181  *
32182  * Fork - LGPL
32183  * <script type="text/javascript">
32184  */
32185  
32186 /**
32187  * @class Roo.bootstrap.layout.Region
32188  * @extends Roo.bootstrap.layout.Basic
32189  * This class represents a region in a layout manager.
32190  
32191  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32192  * @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})
32193  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32194  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32195  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32196  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32197  * @cfg {String}    title           The title for the region (overrides panel titles)
32198  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32199  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32200  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32201  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32202  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32203  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32204  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32205  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32206  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32207  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32208
32209  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32210  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32211  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32212  * @cfg {Number}    width           For East/West panels
32213  * @cfg {Number}    height          For North/South panels
32214  * @cfg {Boolean}   split           To show the splitter
32215  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32216  * 
32217  * @cfg {string}   cls             Extra CSS classes to add to region
32218  * 
32219  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32220  * @cfg {string}   region  the region that it inhabits..
32221  *
32222
32223  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32224  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32225
32226  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32227  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32228  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32229  */
32230 Roo.bootstrap.layout.Region = function(config)
32231 {
32232     this.applyConfig(config);
32233
32234     var mgr = config.mgr;
32235     var pos = config.region;
32236     config.skipConfig = true;
32237     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32238     
32239     if (mgr.el) {
32240         this.onRender(mgr.el);   
32241     }
32242      
32243     this.visible = true;
32244     this.collapsed = false;
32245 };
32246
32247 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32248
32249     position: '', // set by wrapper (eg. north/south etc..)
32250
32251     createBody : function(){
32252         /** This region's body element 
32253         * @type Roo.Element */
32254         this.bodyEl = this.el.createChild({
32255                 tag: "div",
32256                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32257         });
32258     },
32259
32260     onRender: function(ctr, pos)
32261     {
32262         var dh = Roo.DomHelper;
32263         /** This region's container element 
32264         * @type Roo.Element */
32265         this.el = dh.append(ctr.dom, {
32266                 tag: "div",
32267                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32268             }, true);
32269         /** This region's title element 
32270         * @type Roo.Element */
32271     
32272         this.titleEl = dh.append(this.el.dom,
32273             {
32274                     tag: "div",
32275                     unselectable: "on",
32276                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32277                     children:[
32278                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32279                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32280                     ]}, true);
32281         
32282         this.titleEl.enableDisplayMode();
32283         /** This region's title text element 
32284         * @type HTMLElement */
32285         this.titleTextEl = this.titleEl.dom.firstChild;
32286         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32287         /*
32288         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32289         this.closeBtn.enableDisplayMode();
32290         this.closeBtn.on("click", this.closeClicked, this);
32291         this.closeBtn.hide();
32292     */
32293         this.createBody(this.config);
32294         if(this.config.hideWhenEmpty){
32295             this.hide();
32296             this.on("paneladded", this.validateVisibility, this);
32297             this.on("panelremoved", this.validateVisibility, this);
32298         }
32299         if(this.autoScroll){
32300             this.bodyEl.setStyle("overflow", "auto");
32301         }else{
32302             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32303         }
32304         //if(c.titlebar !== false){
32305             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32306                 this.titleEl.hide();
32307             }else{
32308                 this.titleEl.show();
32309                 if(this.config.title){
32310                     this.titleTextEl.innerHTML = this.config.title;
32311                 }
32312             }
32313         //}
32314         if(this.config.collapsed){
32315             this.collapse(true);
32316         }
32317         if(this.config.hidden){
32318             this.hide();
32319         }
32320     },
32321     
32322     applyConfig : function(c)
32323     {
32324         /*
32325          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32326             var dh = Roo.DomHelper;
32327             if(c.titlebar !== false){
32328                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32329                 this.collapseBtn.on("click", this.collapse, this);
32330                 this.collapseBtn.enableDisplayMode();
32331                 /*
32332                 if(c.showPin === true || this.showPin){
32333                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32334                     this.stickBtn.enableDisplayMode();
32335                     this.stickBtn.on("click", this.expand, this);
32336                     this.stickBtn.hide();
32337                 }
32338                 
32339             }
32340             */
32341             /** This region's collapsed element
32342             * @type Roo.Element */
32343             /*
32344              *
32345             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32346                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32347             ]}, true);
32348             
32349             if(c.floatable !== false){
32350                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32351                this.collapsedEl.on("click", this.collapseClick, this);
32352             }
32353
32354             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32355                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32356                    id: "message", unselectable: "on", style:{"float":"left"}});
32357                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32358              }
32359             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32360             this.expandBtn.on("click", this.expand, this);
32361             
32362         }
32363         
32364         if(this.collapseBtn){
32365             this.collapseBtn.setVisible(c.collapsible == true);
32366         }
32367         
32368         this.cmargins = c.cmargins || this.cmargins ||
32369                          (this.position == "west" || this.position == "east" ?
32370                              {top: 0, left: 2, right:2, bottom: 0} :
32371                              {top: 2, left: 0, right:0, bottom: 2});
32372         */
32373         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32374         
32375         
32376         this.bottomTabs = c.tabPosition != "top";
32377         
32378         this.autoScroll = c.autoScroll || false;
32379         
32380         
32381        
32382         
32383         this.duration = c.duration || .30;
32384         this.slideDuration = c.slideDuration || .45;
32385         this.config = c;
32386        
32387     },
32388     /**
32389      * Returns true if this region is currently visible.
32390      * @return {Boolean}
32391      */
32392     isVisible : function(){
32393         return this.visible;
32394     },
32395
32396     /**
32397      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32398      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32399      */
32400     //setCollapsedTitle : function(title){
32401     //    title = title || "&#160;";
32402      //   if(this.collapsedTitleTextEl){
32403       //      this.collapsedTitleTextEl.innerHTML = title;
32404        // }
32405     //},
32406
32407     getBox : function(){
32408         var b;
32409       //  if(!this.collapsed){
32410             b = this.el.getBox(false, true);
32411        // }else{
32412           //  b = this.collapsedEl.getBox(false, true);
32413         //}
32414         return b;
32415     },
32416
32417     getMargins : function(){
32418         return this.margins;
32419         //return this.collapsed ? this.cmargins : this.margins;
32420     },
32421 /*
32422     highlight : function(){
32423         this.el.addClass("x-layout-panel-dragover");
32424     },
32425
32426     unhighlight : function(){
32427         this.el.removeClass("x-layout-panel-dragover");
32428     },
32429 */
32430     updateBox : function(box)
32431     {
32432         this.box = box;
32433         if(!this.collapsed){
32434             this.el.dom.style.left = box.x + "px";
32435             this.el.dom.style.top = box.y + "px";
32436             this.updateBody(box.width, box.height);
32437         }else{
32438             this.collapsedEl.dom.style.left = box.x + "px";
32439             this.collapsedEl.dom.style.top = box.y + "px";
32440             this.collapsedEl.setSize(box.width, box.height);
32441         }
32442         if(this.tabs){
32443             this.tabs.autoSizeTabs();
32444         }
32445     },
32446
32447     updateBody : function(w, h)
32448     {
32449         if(w !== null){
32450             this.el.setWidth(w);
32451             w -= this.el.getBorderWidth("rl");
32452             if(this.config.adjustments){
32453                 w += this.config.adjustments[0];
32454             }
32455         }
32456         if(h !== null){
32457             this.el.setHeight(h);
32458             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32459             h -= this.el.getBorderWidth("tb");
32460             if(this.config.adjustments){
32461                 h += this.config.adjustments[1];
32462             }
32463             this.bodyEl.setHeight(h);
32464             if(this.tabs){
32465                 h = this.tabs.syncHeight(h);
32466             }
32467         }
32468         if(this.panelSize){
32469             w = w !== null ? w : this.panelSize.width;
32470             h = h !== null ? h : this.panelSize.height;
32471         }
32472         if(this.activePanel){
32473             var el = this.activePanel.getEl();
32474             w = w !== null ? w : el.getWidth();
32475             h = h !== null ? h : el.getHeight();
32476             this.panelSize = {width: w, height: h};
32477             this.activePanel.setSize(w, h);
32478         }
32479         if(Roo.isIE && this.tabs){
32480             this.tabs.el.repaint();
32481         }
32482     },
32483
32484     /**
32485      * Returns the container element for this region.
32486      * @return {Roo.Element}
32487      */
32488     getEl : function(){
32489         return this.el;
32490     },
32491
32492     /**
32493      * Hides this region.
32494      */
32495     hide : function(){
32496         //if(!this.collapsed){
32497             this.el.dom.style.left = "-2000px";
32498             this.el.hide();
32499         //}else{
32500          //   this.collapsedEl.dom.style.left = "-2000px";
32501          //   this.collapsedEl.hide();
32502        // }
32503         this.visible = false;
32504         this.fireEvent("visibilitychange", this, false);
32505     },
32506
32507     /**
32508      * Shows this region if it was previously hidden.
32509      */
32510     show : function(){
32511         //if(!this.collapsed){
32512             this.el.show();
32513         //}else{
32514         //    this.collapsedEl.show();
32515        // }
32516         this.visible = true;
32517         this.fireEvent("visibilitychange", this, true);
32518     },
32519 /*
32520     closeClicked : function(){
32521         if(this.activePanel){
32522             this.remove(this.activePanel);
32523         }
32524     },
32525
32526     collapseClick : function(e){
32527         if(this.isSlid){
32528            e.stopPropagation();
32529            this.slideIn();
32530         }else{
32531            e.stopPropagation();
32532            this.slideOut();
32533         }
32534     },
32535 */
32536     /**
32537      * Collapses this region.
32538      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32539      */
32540     /*
32541     collapse : function(skipAnim, skipCheck = false){
32542         if(this.collapsed) {
32543             return;
32544         }
32545         
32546         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32547             
32548             this.collapsed = true;
32549             if(this.split){
32550                 this.split.el.hide();
32551             }
32552             if(this.config.animate && skipAnim !== true){
32553                 this.fireEvent("invalidated", this);
32554                 this.animateCollapse();
32555             }else{
32556                 this.el.setLocation(-20000,-20000);
32557                 this.el.hide();
32558                 this.collapsedEl.show();
32559                 this.fireEvent("collapsed", this);
32560                 this.fireEvent("invalidated", this);
32561             }
32562         }
32563         
32564     },
32565 */
32566     animateCollapse : function(){
32567         // overridden
32568     },
32569
32570     /**
32571      * Expands this region if it was previously collapsed.
32572      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32573      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32574      */
32575     /*
32576     expand : function(e, skipAnim){
32577         if(e) {
32578             e.stopPropagation();
32579         }
32580         if(!this.collapsed || this.el.hasActiveFx()) {
32581             return;
32582         }
32583         if(this.isSlid){
32584             this.afterSlideIn();
32585             skipAnim = true;
32586         }
32587         this.collapsed = false;
32588         if(this.config.animate && skipAnim !== true){
32589             this.animateExpand();
32590         }else{
32591             this.el.show();
32592             if(this.split){
32593                 this.split.el.show();
32594             }
32595             this.collapsedEl.setLocation(-2000,-2000);
32596             this.collapsedEl.hide();
32597             this.fireEvent("invalidated", this);
32598             this.fireEvent("expanded", this);
32599         }
32600     },
32601 */
32602     animateExpand : function(){
32603         // overridden
32604     },
32605
32606     initTabs : function()
32607     {
32608         this.bodyEl.setStyle("overflow", "hidden");
32609         var ts = new Roo.bootstrap.panel.Tabs({
32610                 el: this.bodyEl.dom,
32611                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32612                 disableTooltips: this.config.disableTabTips,
32613                 toolbar : this.config.toolbar
32614             });
32615         
32616         if(this.config.hideTabs){
32617             ts.stripWrap.setDisplayed(false);
32618         }
32619         this.tabs = ts;
32620         ts.resizeTabs = this.config.resizeTabs === true;
32621         ts.minTabWidth = this.config.minTabWidth || 40;
32622         ts.maxTabWidth = this.config.maxTabWidth || 250;
32623         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32624         ts.monitorResize = false;
32625         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32626         ts.bodyEl.addClass('roo-layout-tabs-body');
32627         this.panels.each(this.initPanelAsTab, this);
32628     },
32629
32630     initPanelAsTab : function(panel){
32631         var ti = this.tabs.addTab(
32632                     panel.getEl().id,
32633                     panel.getTitle(), null,
32634                     this.config.closeOnTab && panel.isClosable()
32635             );
32636         if(panel.tabTip !== undefined){
32637             ti.setTooltip(panel.tabTip);
32638         }
32639         ti.on("activate", function(){
32640               this.setActivePanel(panel);
32641         }, this);
32642         
32643         if(this.config.closeOnTab){
32644             ti.on("beforeclose", function(t, e){
32645                 e.cancel = true;
32646                 this.remove(panel);
32647             }, this);
32648         }
32649         return ti;
32650     },
32651
32652     updatePanelTitle : function(panel, title)
32653     {
32654         if(this.activePanel == panel){
32655             this.updateTitle(title);
32656         }
32657         if(this.tabs){
32658             var ti = this.tabs.getTab(panel.getEl().id);
32659             ti.setText(title);
32660             if(panel.tabTip !== undefined){
32661                 ti.setTooltip(panel.tabTip);
32662             }
32663         }
32664     },
32665
32666     updateTitle : function(title){
32667         if(this.titleTextEl && !this.config.title){
32668             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32669         }
32670     },
32671
32672     setActivePanel : function(panel)
32673     {
32674         panel = this.getPanel(panel);
32675         if(this.activePanel && this.activePanel != panel){
32676             this.activePanel.setActiveState(false);
32677         }
32678         this.activePanel = panel;
32679         panel.setActiveState(true);
32680         if(this.panelSize){
32681             panel.setSize(this.panelSize.width, this.panelSize.height);
32682         }
32683         if(this.closeBtn){
32684             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32685         }
32686         this.updateTitle(panel.getTitle());
32687         if(this.tabs){
32688             this.fireEvent("invalidated", this);
32689         }
32690         this.fireEvent("panelactivated", this, panel);
32691     },
32692
32693     /**
32694      * Shows the specified panel.
32695      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32696      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32697      */
32698     showPanel : function(panel)
32699     {
32700         panel = this.getPanel(panel);
32701         if(panel){
32702             if(this.tabs){
32703                 var tab = this.tabs.getTab(panel.getEl().id);
32704                 if(tab.isHidden()){
32705                     this.tabs.unhideTab(tab.id);
32706                 }
32707                 tab.activate();
32708             }else{
32709                 this.setActivePanel(panel);
32710             }
32711         }
32712         return panel;
32713     },
32714
32715     /**
32716      * Get the active panel for this region.
32717      * @return {Roo.ContentPanel} The active panel or null
32718      */
32719     getActivePanel : function(){
32720         return this.activePanel;
32721     },
32722
32723     validateVisibility : function(){
32724         if(this.panels.getCount() < 1){
32725             this.updateTitle("&#160;");
32726             this.closeBtn.hide();
32727             this.hide();
32728         }else{
32729             if(!this.isVisible()){
32730                 this.show();
32731             }
32732         }
32733     },
32734
32735     /**
32736      * Adds the passed ContentPanel(s) to this region.
32737      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32738      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32739      */
32740     add : function(panel){
32741         if(arguments.length > 1){
32742             for(var i = 0, len = arguments.length; i < len; i++) {
32743                 this.add(arguments[i]);
32744             }
32745             return null;
32746         }
32747         if(this.hasPanel(panel)){
32748             this.showPanel(panel);
32749             return panel;
32750         }
32751         panel.setRegion(this);
32752         this.panels.add(panel);
32753         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32754             this.bodyEl.dom.appendChild(panel.getEl().dom);
32755             if(panel.background !== true){
32756                 this.setActivePanel(panel);
32757             }
32758             this.fireEvent("paneladded", this, panel);
32759             return panel;
32760         }
32761         if(!this.tabs){
32762             this.initTabs();
32763         }else{
32764             this.initPanelAsTab(panel);
32765         }
32766         
32767         
32768         if(panel.background !== true){
32769             this.tabs.activate(panel.getEl().id);
32770         }
32771         this.fireEvent("paneladded", this, panel);
32772         return panel;
32773     },
32774
32775     /**
32776      * Hides the tab for the specified panel.
32777      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32778      */
32779     hidePanel : function(panel){
32780         if(this.tabs && (panel = this.getPanel(panel))){
32781             this.tabs.hideTab(panel.getEl().id);
32782         }
32783     },
32784
32785     /**
32786      * Unhides the tab for a previously hidden panel.
32787      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32788      */
32789     unhidePanel : function(panel){
32790         if(this.tabs && (panel = this.getPanel(panel))){
32791             this.tabs.unhideTab(panel.getEl().id);
32792         }
32793     },
32794
32795     clearPanels : function(){
32796         while(this.panels.getCount() > 0){
32797              this.remove(this.panels.first());
32798         }
32799     },
32800
32801     /**
32802      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32803      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32804      * @param {Boolean} preservePanel Overrides the config preservePanel option
32805      * @return {Roo.ContentPanel} The panel that was removed
32806      */
32807     remove : function(panel, preservePanel)
32808     {
32809         panel = this.getPanel(panel);
32810         if(!panel){
32811             return null;
32812         }
32813         var e = {};
32814         this.fireEvent("beforeremove", this, panel, e);
32815         if(e.cancel === true){
32816             return null;
32817         }
32818         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32819         var panelId = panel.getId();
32820         this.panels.removeKey(panelId);
32821         if(preservePanel){
32822             document.body.appendChild(panel.getEl().dom);
32823         }
32824         if(this.tabs){
32825             this.tabs.removeTab(panel.getEl().id);
32826         }else if (!preservePanel){
32827             this.bodyEl.dom.removeChild(panel.getEl().dom);
32828         }
32829         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32830             var p = this.panels.first();
32831             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32832             tempEl.appendChild(p.getEl().dom);
32833             this.bodyEl.update("");
32834             this.bodyEl.dom.appendChild(p.getEl().dom);
32835             tempEl = null;
32836             this.updateTitle(p.getTitle());
32837             this.tabs = null;
32838             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32839             this.setActivePanel(p);
32840         }
32841         panel.setRegion(null);
32842         if(this.activePanel == panel){
32843             this.activePanel = null;
32844         }
32845         if(this.config.autoDestroy !== false && preservePanel !== true){
32846             try{panel.destroy();}catch(e){}
32847         }
32848         this.fireEvent("panelremoved", this, panel);
32849         return panel;
32850     },
32851
32852     /**
32853      * Returns the TabPanel component used by this region
32854      * @return {Roo.TabPanel}
32855      */
32856     getTabs : function(){
32857         return this.tabs;
32858     },
32859
32860     createTool : function(parentEl, className){
32861         var btn = Roo.DomHelper.append(parentEl, {
32862             tag: "div",
32863             cls: "x-layout-tools-button",
32864             children: [ {
32865                 tag: "div",
32866                 cls: "roo-layout-tools-button-inner " + className,
32867                 html: "&#160;"
32868             }]
32869         }, true);
32870         btn.addClassOnOver("roo-layout-tools-button-over");
32871         return btn;
32872     }
32873 });/*
32874  * Based on:
32875  * Ext JS Library 1.1.1
32876  * Copyright(c) 2006-2007, Ext JS, LLC.
32877  *
32878  * Originally Released Under LGPL - original licence link has changed is not relivant.
32879  *
32880  * Fork - LGPL
32881  * <script type="text/javascript">
32882  */
32883  
32884
32885
32886 /**
32887  * @class Roo.SplitLayoutRegion
32888  * @extends Roo.LayoutRegion
32889  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32890  */
32891 Roo.bootstrap.layout.Split = function(config){
32892     this.cursor = config.cursor;
32893     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32894 };
32895
32896 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32897 {
32898     splitTip : "Drag to resize.",
32899     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32900     useSplitTips : false,
32901
32902     applyConfig : function(config){
32903         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32904     },
32905     
32906     onRender : function(ctr,pos) {
32907         
32908         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
32909         if(!this.config.split){
32910             return;
32911         }
32912         if(!this.split){
32913             
32914             var splitEl = Roo.DomHelper.append(ctr.dom,  {
32915                             tag: "div",
32916                             id: this.el.id + "-split",
32917                             cls: "roo-layout-split roo-layout-split-"+this.position,
32918                             html: "&#160;"
32919             });
32920             /** The SplitBar for this region 
32921             * @type Roo.SplitBar */
32922             // does not exist yet...
32923             Roo.log([this.position, this.orientation]);
32924             
32925             this.split = new Roo.bootstrap.SplitBar({
32926                 dragElement : splitEl,
32927                 resizingElement: this.el,
32928                 orientation : this.orientation
32929             });
32930             
32931             this.split.on("moved", this.onSplitMove, this);
32932             this.split.useShim = this.config.useShim === true;
32933             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32934             if(this.useSplitTips){
32935                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32936             }
32937             //if(config.collapsible){
32938             //    this.split.el.on("dblclick", this.collapse,  this);
32939             //}
32940         }
32941         if(typeof this.config.minSize != "undefined"){
32942             this.split.minSize = this.config.minSize;
32943         }
32944         if(typeof this.config.maxSize != "undefined"){
32945             this.split.maxSize = this.config.maxSize;
32946         }
32947         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
32948             this.hideSplitter();
32949         }
32950         
32951     },
32952
32953     getHMaxSize : function(){
32954          var cmax = this.config.maxSize || 10000;
32955          var center = this.mgr.getRegion("center");
32956          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32957     },
32958
32959     getVMaxSize : function(){
32960          var cmax = this.config.maxSize || 10000;
32961          var center = this.mgr.getRegion("center");
32962          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32963     },
32964
32965     onSplitMove : function(split, newSize){
32966         this.fireEvent("resized", this, newSize);
32967     },
32968     
32969     /** 
32970      * Returns the {@link Roo.SplitBar} for this region.
32971      * @return {Roo.SplitBar}
32972      */
32973     getSplitBar : function(){
32974         return this.split;
32975     },
32976     
32977     hide : function(){
32978         this.hideSplitter();
32979         Roo.bootstrap.layout.Split.superclass.hide.call(this);
32980     },
32981
32982     hideSplitter : function(){
32983         if(this.split){
32984             this.split.el.setLocation(-2000,-2000);
32985             this.split.el.hide();
32986         }
32987     },
32988
32989     show : function(){
32990         if(this.split){
32991             this.split.el.show();
32992         }
32993         Roo.bootstrap.layout.Split.superclass.show.call(this);
32994     },
32995     
32996     beforeSlide: function(){
32997         if(Roo.isGecko){// firefox overflow auto bug workaround
32998             this.bodyEl.clip();
32999             if(this.tabs) {
33000                 this.tabs.bodyEl.clip();
33001             }
33002             if(this.activePanel){
33003                 this.activePanel.getEl().clip();
33004                 
33005                 if(this.activePanel.beforeSlide){
33006                     this.activePanel.beforeSlide();
33007                 }
33008             }
33009         }
33010     },
33011     
33012     afterSlide : function(){
33013         if(Roo.isGecko){// firefox overflow auto bug workaround
33014             this.bodyEl.unclip();
33015             if(this.tabs) {
33016                 this.tabs.bodyEl.unclip();
33017             }
33018             if(this.activePanel){
33019                 this.activePanel.getEl().unclip();
33020                 if(this.activePanel.afterSlide){
33021                     this.activePanel.afterSlide();
33022                 }
33023             }
33024         }
33025     },
33026
33027     initAutoHide : function(){
33028         if(this.autoHide !== false){
33029             if(!this.autoHideHd){
33030                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33031                 this.autoHideHd = {
33032                     "mouseout": function(e){
33033                         if(!e.within(this.el, true)){
33034                             st.delay(500);
33035                         }
33036                     },
33037                     "mouseover" : function(e){
33038                         st.cancel();
33039                     },
33040                     scope : this
33041                 };
33042             }
33043             this.el.on(this.autoHideHd);
33044         }
33045     },
33046
33047     clearAutoHide : function(){
33048         if(this.autoHide !== false){
33049             this.el.un("mouseout", this.autoHideHd.mouseout);
33050             this.el.un("mouseover", this.autoHideHd.mouseover);
33051         }
33052     },
33053
33054     clearMonitor : function(){
33055         Roo.get(document).un("click", this.slideInIf, this);
33056     },
33057
33058     // these names are backwards but not changed for compat
33059     slideOut : function(){
33060         if(this.isSlid || this.el.hasActiveFx()){
33061             return;
33062         }
33063         this.isSlid = true;
33064         if(this.collapseBtn){
33065             this.collapseBtn.hide();
33066         }
33067         this.closeBtnState = this.closeBtn.getStyle('display');
33068         this.closeBtn.hide();
33069         if(this.stickBtn){
33070             this.stickBtn.show();
33071         }
33072         this.el.show();
33073         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33074         this.beforeSlide();
33075         this.el.setStyle("z-index", 10001);
33076         this.el.slideIn(this.getSlideAnchor(), {
33077             callback: function(){
33078                 this.afterSlide();
33079                 this.initAutoHide();
33080                 Roo.get(document).on("click", this.slideInIf, this);
33081                 this.fireEvent("slideshow", this);
33082             },
33083             scope: this,
33084             block: true
33085         });
33086     },
33087
33088     afterSlideIn : function(){
33089         this.clearAutoHide();
33090         this.isSlid = false;
33091         this.clearMonitor();
33092         this.el.setStyle("z-index", "");
33093         if(this.collapseBtn){
33094             this.collapseBtn.show();
33095         }
33096         this.closeBtn.setStyle('display', this.closeBtnState);
33097         if(this.stickBtn){
33098             this.stickBtn.hide();
33099         }
33100         this.fireEvent("slidehide", this);
33101     },
33102
33103     slideIn : function(cb){
33104         if(!this.isSlid || this.el.hasActiveFx()){
33105             Roo.callback(cb);
33106             return;
33107         }
33108         this.isSlid = false;
33109         this.beforeSlide();
33110         this.el.slideOut(this.getSlideAnchor(), {
33111             callback: function(){
33112                 this.el.setLeftTop(-10000, -10000);
33113                 this.afterSlide();
33114                 this.afterSlideIn();
33115                 Roo.callback(cb);
33116             },
33117             scope: this,
33118             block: true
33119         });
33120     },
33121     
33122     slideInIf : function(e){
33123         if(!e.within(this.el)){
33124             this.slideIn();
33125         }
33126     },
33127
33128     animateCollapse : function(){
33129         this.beforeSlide();
33130         this.el.setStyle("z-index", 20000);
33131         var anchor = this.getSlideAnchor();
33132         this.el.slideOut(anchor, {
33133             callback : function(){
33134                 this.el.setStyle("z-index", "");
33135                 this.collapsedEl.slideIn(anchor, {duration:.3});
33136                 this.afterSlide();
33137                 this.el.setLocation(-10000,-10000);
33138                 this.el.hide();
33139                 this.fireEvent("collapsed", this);
33140             },
33141             scope: this,
33142             block: true
33143         });
33144     },
33145
33146     animateExpand : function(){
33147         this.beforeSlide();
33148         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33149         this.el.setStyle("z-index", 20000);
33150         this.collapsedEl.hide({
33151             duration:.1
33152         });
33153         this.el.slideIn(this.getSlideAnchor(), {
33154             callback : function(){
33155                 this.el.setStyle("z-index", "");
33156                 this.afterSlide();
33157                 if(this.split){
33158                     this.split.el.show();
33159                 }
33160                 this.fireEvent("invalidated", this);
33161                 this.fireEvent("expanded", this);
33162             },
33163             scope: this,
33164             block: true
33165         });
33166     },
33167
33168     anchors : {
33169         "west" : "left",
33170         "east" : "right",
33171         "north" : "top",
33172         "south" : "bottom"
33173     },
33174
33175     sanchors : {
33176         "west" : "l",
33177         "east" : "r",
33178         "north" : "t",
33179         "south" : "b"
33180     },
33181
33182     canchors : {
33183         "west" : "tl-tr",
33184         "east" : "tr-tl",
33185         "north" : "tl-bl",
33186         "south" : "bl-tl"
33187     },
33188
33189     getAnchor : function(){
33190         return this.anchors[this.position];
33191     },
33192
33193     getCollapseAnchor : function(){
33194         return this.canchors[this.position];
33195     },
33196
33197     getSlideAnchor : function(){
33198         return this.sanchors[this.position];
33199     },
33200
33201     getAlignAdj : function(){
33202         var cm = this.cmargins;
33203         switch(this.position){
33204             case "west":
33205                 return [0, 0];
33206             break;
33207             case "east":
33208                 return [0, 0];
33209             break;
33210             case "north":
33211                 return [0, 0];
33212             break;
33213             case "south":
33214                 return [0, 0];
33215             break;
33216         }
33217     },
33218
33219     getExpandAdj : function(){
33220         var c = this.collapsedEl, cm = this.cmargins;
33221         switch(this.position){
33222             case "west":
33223                 return [-(cm.right+c.getWidth()+cm.left), 0];
33224             break;
33225             case "east":
33226                 return [cm.right+c.getWidth()+cm.left, 0];
33227             break;
33228             case "north":
33229                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33230             break;
33231             case "south":
33232                 return [0, cm.top+cm.bottom+c.getHeight()];
33233             break;
33234         }
33235     }
33236 });/*
33237  * Based on:
33238  * Ext JS Library 1.1.1
33239  * Copyright(c) 2006-2007, Ext JS, LLC.
33240  *
33241  * Originally Released Under LGPL - original licence link has changed is not relivant.
33242  *
33243  * Fork - LGPL
33244  * <script type="text/javascript">
33245  */
33246 /*
33247  * These classes are private internal classes
33248  */
33249 Roo.bootstrap.layout.Center = function(config){
33250     config.region = "center";
33251     Roo.bootstrap.layout.Region.call(this, config);
33252     this.visible = true;
33253     this.minWidth = config.minWidth || 20;
33254     this.minHeight = config.minHeight || 20;
33255 };
33256
33257 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33258     hide : function(){
33259         // center panel can't be hidden
33260     },
33261     
33262     show : function(){
33263         // center panel can't be hidden
33264     },
33265     
33266     getMinWidth: function(){
33267         return this.minWidth;
33268     },
33269     
33270     getMinHeight: function(){
33271         return this.minHeight;
33272     }
33273 });
33274
33275
33276
33277
33278  
33279
33280
33281
33282
33283
33284 Roo.bootstrap.layout.North = function(config)
33285 {
33286     config.region = 'north';
33287     config.cursor = 'n-resize';
33288     
33289     Roo.bootstrap.layout.Split.call(this, config);
33290     
33291     
33292     if(this.split){
33293         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33294         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33295         this.split.el.addClass("roo-layout-split-v");
33296     }
33297     var size = config.initialSize || config.height;
33298     if(typeof size != "undefined"){
33299         this.el.setHeight(size);
33300     }
33301 };
33302 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33303 {
33304     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33305     
33306     
33307     
33308     getBox : function(){
33309         if(this.collapsed){
33310             return this.collapsedEl.getBox();
33311         }
33312         var box = this.el.getBox();
33313         if(this.split){
33314             box.height += this.split.el.getHeight();
33315         }
33316         return box;
33317     },
33318     
33319     updateBox : function(box){
33320         if(this.split && !this.collapsed){
33321             box.height -= this.split.el.getHeight();
33322             this.split.el.setLeft(box.x);
33323             this.split.el.setTop(box.y+box.height);
33324             this.split.el.setWidth(box.width);
33325         }
33326         if(this.collapsed){
33327             this.updateBody(box.width, null);
33328         }
33329         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33330     }
33331 });
33332
33333
33334
33335
33336
33337 Roo.bootstrap.layout.South = function(config){
33338     config.region = 'south';
33339     config.cursor = 's-resize';
33340     Roo.bootstrap.layout.Split.call(this, config);
33341     if(this.split){
33342         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33343         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33344         this.split.el.addClass("roo-layout-split-v");
33345     }
33346     var size = config.initialSize || config.height;
33347     if(typeof size != "undefined"){
33348         this.el.setHeight(size);
33349     }
33350 };
33351
33352 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33353     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33354     getBox : function(){
33355         if(this.collapsed){
33356             return this.collapsedEl.getBox();
33357         }
33358         var box = this.el.getBox();
33359         if(this.split){
33360             var sh = this.split.el.getHeight();
33361             box.height += sh;
33362             box.y -= sh;
33363         }
33364         return box;
33365     },
33366     
33367     updateBox : function(box){
33368         if(this.split && !this.collapsed){
33369             var sh = this.split.el.getHeight();
33370             box.height -= sh;
33371             box.y += sh;
33372             this.split.el.setLeft(box.x);
33373             this.split.el.setTop(box.y-sh);
33374             this.split.el.setWidth(box.width);
33375         }
33376         if(this.collapsed){
33377             this.updateBody(box.width, null);
33378         }
33379         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33380     }
33381 });
33382
33383 Roo.bootstrap.layout.East = function(config){
33384     config.region = "east";
33385     config.cursor = "e-resize";
33386     Roo.bootstrap.layout.Split.call(this, config);
33387     if(this.split){
33388         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33389         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33390         this.split.el.addClass("roo-layout-split-h");
33391     }
33392     var size = config.initialSize || config.width;
33393     if(typeof size != "undefined"){
33394         this.el.setWidth(size);
33395     }
33396 };
33397 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33398     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33399     getBox : function(){
33400         if(this.collapsed){
33401             return this.collapsedEl.getBox();
33402         }
33403         var box = this.el.getBox();
33404         if(this.split){
33405             var sw = this.split.el.getWidth();
33406             box.width += sw;
33407             box.x -= sw;
33408         }
33409         return box;
33410     },
33411
33412     updateBox : function(box){
33413         if(this.split && !this.collapsed){
33414             var sw = this.split.el.getWidth();
33415             box.width -= sw;
33416             this.split.el.setLeft(box.x);
33417             this.split.el.setTop(box.y);
33418             this.split.el.setHeight(box.height);
33419             box.x += sw;
33420         }
33421         if(this.collapsed){
33422             this.updateBody(null, box.height);
33423         }
33424         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33425     }
33426 });
33427
33428 Roo.bootstrap.layout.West = function(config){
33429     config.region = "west";
33430     config.cursor = "w-resize";
33431     
33432     Roo.bootstrap.layout.Split.call(this, config);
33433     if(this.split){
33434         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33435         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33436         this.split.el.addClass("roo-layout-split-h");
33437     }
33438     
33439 };
33440 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33441     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33442     
33443     onRender: function(ctr, pos)
33444     {
33445         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33446         var size = this.config.initialSize || this.config.width;
33447         if(typeof size != "undefined"){
33448             this.el.setWidth(size);
33449         }
33450     },
33451     
33452     getBox : function(){
33453         if(this.collapsed){
33454             return this.collapsedEl.getBox();
33455         }
33456         var box = this.el.getBox();
33457         if(this.split){
33458             box.width += this.split.el.getWidth();
33459         }
33460         return box;
33461     },
33462     
33463     updateBox : function(box){
33464         if(this.split && !this.collapsed){
33465             var sw = this.split.el.getWidth();
33466             box.width -= sw;
33467             this.split.el.setLeft(box.x+box.width);
33468             this.split.el.setTop(box.y);
33469             this.split.el.setHeight(box.height);
33470         }
33471         if(this.collapsed){
33472             this.updateBody(null, box.height);
33473         }
33474         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33475     }
33476 });
33477 Roo.namespace("Roo.bootstrap.panel");/*
33478  * Based on:
33479  * Ext JS Library 1.1.1
33480  * Copyright(c) 2006-2007, Ext JS, LLC.
33481  *
33482  * Originally Released Under LGPL - original licence link has changed is not relivant.
33483  *
33484  * Fork - LGPL
33485  * <script type="text/javascript">
33486  */
33487 /**
33488  * @class Roo.ContentPanel
33489  * @extends Roo.util.Observable
33490  * A basic ContentPanel element.
33491  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33492  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33493  * @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
33494  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33495  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33496  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33497  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33498  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33499  * @cfg {String} title          The title for this panel
33500  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33501  * @cfg {String} url            Calls {@link #setUrl} with this value
33502  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33503  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33504  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33505  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33506
33507  * @constructor
33508  * Create a new ContentPanel.
33509  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33510  * @param {String/Object} config A string to set only the title or a config object
33511  * @param {String} content (optional) Set the HTML content for this panel
33512  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33513  */
33514 Roo.bootstrap.panel.Content = function( config){
33515     
33516     var el = config.el;
33517     var content = config.content;
33518
33519     if(config.autoCreate){ // xtype is available if this is called from factory
33520         el = Roo.id();
33521     }
33522     this.el = Roo.get(el);
33523     if(!this.el && config && config.autoCreate){
33524         if(typeof config.autoCreate == "object"){
33525             if(!config.autoCreate.id){
33526                 config.autoCreate.id = config.id||el;
33527             }
33528             this.el = Roo.DomHelper.append(document.body,
33529                         config.autoCreate, true);
33530         }else{
33531             var elcfg =  {   tag: "div",
33532                             cls: "roo-layout-inactive-content",
33533                             id: config.id||el
33534                             };
33535             if (config.html) {
33536                 elcfg.html = config.html;
33537                 
33538             }
33539                         
33540             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33541         }
33542     } 
33543     this.closable = false;
33544     this.loaded = false;
33545     this.active = false;
33546    
33547       
33548     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33549         
33550         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33551         
33552         this.wrapEl = this.el.wrap();
33553         var ti = [];
33554         if (config.toolbar.items) {
33555             ti = config.toolbar.items ;
33556             delete config.toolbar.items ;
33557         }
33558         
33559         var nitems = [];
33560         this.toolbar.render(this.wrapEl, 'before');
33561         for(var i =0;i < ti.length;i++) {
33562           //  Roo.log(['add child', items[i]]);
33563             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33564         }
33565         this.toolbar.items = nitems;
33566         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33567         delete config.toolbar;
33568         
33569     }
33570     /*
33571     // xtype created footer. - not sure if will work as we normally have to render first..
33572     if (this.footer && !this.footer.el && this.footer.xtype) {
33573         if (!this.wrapEl) {
33574             this.wrapEl = this.el.wrap();
33575         }
33576     
33577         this.footer.container = this.wrapEl.createChild();
33578          
33579         this.footer = Roo.factory(this.footer, Roo);
33580         
33581     }
33582     */
33583     
33584      if(typeof config == "string"){
33585         this.title = config;
33586     }else{
33587         Roo.apply(this, config);
33588     }
33589     
33590     if(this.resizeEl){
33591         this.resizeEl = Roo.get(this.resizeEl, true);
33592     }else{
33593         this.resizeEl = this.el;
33594     }
33595     // handle view.xtype
33596     
33597  
33598     
33599     
33600     this.addEvents({
33601         /**
33602          * @event activate
33603          * Fires when this panel is activated. 
33604          * @param {Roo.ContentPanel} this
33605          */
33606         "activate" : true,
33607         /**
33608          * @event deactivate
33609          * Fires when this panel is activated. 
33610          * @param {Roo.ContentPanel} this
33611          */
33612         "deactivate" : true,
33613
33614         /**
33615          * @event resize
33616          * Fires when this panel is resized if fitToFrame is true.
33617          * @param {Roo.ContentPanel} this
33618          * @param {Number} width The width after any component adjustments
33619          * @param {Number} height The height after any component adjustments
33620          */
33621         "resize" : true,
33622         
33623          /**
33624          * @event render
33625          * Fires when this tab is created
33626          * @param {Roo.ContentPanel} this
33627          */
33628         "render" : true
33629         
33630         
33631         
33632     });
33633     
33634
33635     
33636     
33637     if(this.autoScroll){
33638         this.resizeEl.setStyle("overflow", "auto");
33639     } else {
33640         // fix randome scrolling
33641         //this.el.on('scroll', function() {
33642         //    Roo.log('fix random scolling');
33643         //    this.scrollTo('top',0); 
33644         //});
33645     }
33646     content = content || this.content;
33647     if(content){
33648         this.setContent(content);
33649     }
33650     if(config && config.url){
33651         this.setUrl(this.url, this.params, this.loadOnce);
33652     }
33653     
33654     
33655     
33656     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33657     
33658     if (this.view && typeof(this.view.xtype) != 'undefined') {
33659         this.view.el = this.el.appendChild(document.createElement("div"));
33660         this.view = Roo.factory(this.view); 
33661         this.view.render  &&  this.view.render(false, '');  
33662     }
33663     
33664     
33665     this.fireEvent('render', this);
33666 };
33667
33668 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33669     tabTip:'',
33670     setRegion : function(region){
33671         this.region = region;
33672         if(region){
33673            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33674         }else{
33675            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33676         } 
33677     },
33678     
33679     /**
33680      * Returns the toolbar for this Panel if one was configured. 
33681      * @return {Roo.Toolbar} 
33682      */
33683     getToolbar : function(){
33684         return this.toolbar;
33685     },
33686     
33687     setActiveState : function(active){
33688         this.active = active;
33689         if(!active){
33690             this.fireEvent("deactivate", this);
33691         }else{
33692             this.fireEvent("activate", this);
33693         }
33694     },
33695     /**
33696      * Updates this panel's element
33697      * @param {String} content The new content
33698      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33699     */
33700     setContent : function(content, loadScripts){
33701         this.el.update(content, loadScripts);
33702     },
33703
33704     ignoreResize : function(w, h){
33705         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33706             return true;
33707         }else{
33708             this.lastSize = {width: w, height: h};
33709             return false;
33710         }
33711     },
33712     /**
33713      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33714      * @return {Roo.UpdateManager} The UpdateManager
33715      */
33716     getUpdateManager : function(){
33717         return this.el.getUpdateManager();
33718     },
33719      /**
33720      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33721      * @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:
33722 <pre><code>
33723 panel.load({
33724     url: "your-url.php",
33725     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33726     callback: yourFunction,
33727     scope: yourObject, //(optional scope)
33728     discardUrl: false,
33729     nocache: false,
33730     text: "Loading...",
33731     timeout: 30,
33732     scripts: false
33733 });
33734 </code></pre>
33735      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33736      * 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.
33737      * @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}
33738      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33739      * @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.
33740      * @return {Roo.ContentPanel} this
33741      */
33742     load : function(){
33743         var um = this.el.getUpdateManager();
33744         um.update.apply(um, arguments);
33745         return this;
33746     },
33747
33748
33749     /**
33750      * 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.
33751      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33752      * @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)
33753      * @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)
33754      * @return {Roo.UpdateManager} The UpdateManager
33755      */
33756     setUrl : function(url, params, loadOnce){
33757         if(this.refreshDelegate){
33758             this.removeListener("activate", this.refreshDelegate);
33759         }
33760         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33761         this.on("activate", this.refreshDelegate);
33762         return this.el.getUpdateManager();
33763     },
33764     
33765     _handleRefresh : function(url, params, loadOnce){
33766         if(!loadOnce || !this.loaded){
33767             var updater = this.el.getUpdateManager();
33768             updater.update(url, params, this._setLoaded.createDelegate(this));
33769         }
33770     },
33771     
33772     _setLoaded : function(){
33773         this.loaded = true;
33774     }, 
33775     
33776     /**
33777      * Returns this panel's id
33778      * @return {String} 
33779      */
33780     getId : function(){
33781         return this.el.id;
33782     },
33783     
33784     /** 
33785      * Returns this panel's element - used by regiosn to add.
33786      * @return {Roo.Element} 
33787      */
33788     getEl : function(){
33789         return this.wrapEl || this.el;
33790     },
33791     
33792    
33793     
33794     adjustForComponents : function(width, height)
33795     {
33796         //Roo.log('adjustForComponents ');
33797         if(this.resizeEl != this.el){
33798             width -= this.el.getFrameWidth('lr');
33799             height -= this.el.getFrameWidth('tb');
33800         }
33801         if(this.toolbar){
33802             var te = this.toolbar.getEl();
33803             height -= te.getHeight();
33804             te.setWidth(width);
33805         }
33806         if(this.footer){
33807             var te = this.footer.getEl();
33808             Roo.log("footer:" + te.getHeight());
33809             
33810             height -= te.getHeight();
33811             te.setWidth(width);
33812         }
33813         
33814         
33815         if(this.adjustments){
33816             width += this.adjustments[0];
33817             height += this.adjustments[1];
33818         }
33819         return {"width": width, "height": height};
33820     },
33821     
33822     setSize : function(width, height){
33823         if(this.fitToFrame && !this.ignoreResize(width, height)){
33824             if(this.fitContainer && this.resizeEl != this.el){
33825                 this.el.setSize(width, height);
33826             }
33827             var size = this.adjustForComponents(width, height);
33828             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33829             this.fireEvent('resize', this, size.width, size.height);
33830         }
33831     },
33832     
33833     /**
33834      * Returns this panel's title
33835      * @return {String} 
33836      */
33837     getTitle : function(){
33838         return this.title;
33839     },
33840     
33841     /**
33842      * Set this panel's title
33843      * @param {String} title
33844      */
33845     setTitle : function(title){
33846         this.title = title;
33847         if(this.region){
33848             this.region.updatePanelTitle(this, title);
33849         }
33850     },
33851     
33852     /**
33853      * Returns true is this panel was configured to be closable
33854      * @return {Boolean} 
33855      */
33856     isClosable : function(){
33857         return this.closable;
33858     },
33859     
33860     beforeSlide : function(){
33861         this.el.clip();
33862         this.resizeEl.clip();
33863     },
33864     
33865     afterSlide : function(){
33866         this.el.unclip();
33867         this.resizeEl.unclip();
33868     },
33869     
33870     /**
33871      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33872      *   Will fail silently if the {@link #setUrl} method has not been called.
33873      *   This does not activate the panel, just updates its content.
33874      */
33875     refresh : function(){
33876         if(this.refreshDelegate){
33877            this.loaded = false;
33878            this.refreshDelegate();
33879         }
33880     },
33881     
33882     /**
33883      * Destroys this panel
33884      */
33885     destroy : function(){
33886         this.el.removeAllListeners();
33887         var tempEl = document.createElement("span");
33888         tempEl.appendChild(this.el.dom);
33889         tempEl.innerHTML = "";
33890         this.el.remove();
33891         this.el = null;
33892     },
33893     
33894     /**
33895      * form - if the content panel contains a form - this is a reference to it.
33896      * @type {Roo.form.Form}
33897      */
33898     form : false,
33899     /**
33900      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33901      *    This contains a reference to it.
33902      * @type {Roo.View}
33903      */
33904     view : false,
33905     
33906       /**
33907      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33908      * <pre><code>
33909
33910 layout.addxtype({
33911        xtype : 'Form',
33912        items: [ .... ]
33913    }
33914 );
33915
33916 </code></pre>
33917      * @param {Object} cfg Xtype definition of item to add.
33918      */
33919     
33920     
33921     getChildContainer: function () {
33922         return this.getEl();
33923     }
33924     
33925     
33926     /*
33927         var  ret = new Roo.factory(cfg);
33928         return ret;
33929         
33930         
33931         // add form..
33932         if (cfg.xtype.match(/^Form$/)) {
33933             
33934             var el;
33935             //if (this.footer) {
33936             //    el = this.footer.container.insertSibling(false, 'before');
33937             //} else {
33938                 el = this.el.createChild();
33939             //}
33940
33941             this.form = new  Roo.form.Form(cfg);
33942             
33943             
33944             if ( this.form.allItems.length) {
33945                 this.form.render(el.dom);
33946             }
33947             return this.form;
33948         }
33949         // should only have one of theses..
33950         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33951             // views.. should not be just added - used named prop 'view''
33952             
33953             cfg.el = this.el.appendChild(document.createElement("div"));
33954             // factory?
33955             
33956             var ret = new Roo.factory(cfg);
33957              
33958              ret.render && ret.render(false, ''); // render blank..
33959             this.view = ret;
33960             return ret;
33961         }
33962         return false;
33963     }
33964     \*/
33965 });
33966  
33967 /**
33968  * @class Roo.bootstrap.panel.Grid
33969  * @extends Roo.bootstrap.panel.Content
33970  * @constructor
33971  * Create a new GridPanel.
33972  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33973  * @param {Object} config A the config object
33974   
33975  */
33976
33977
33978
33979 Roo.bootstrap.panel.Grid = function(config)
33980 {
33981     
33982       
33983     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33984         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
33985
33986     config.el = this.wrapper;
33987     //this.el = this.wrapper;
33988     
33989       if (config.container) {
33990         // ctor'ed from a Border/panel.grid
33991         
33992         
33993         this.wrapper.setStyle("overflow", "hidden");
33994         this.wrapper.addClass('roo-grid-container');
33995
33996     }
33997     
33998     
33999     if(config.toolbar){
34000         var tool_el = this.wrapper.createChild();    
34001         this.toolbar = Roo.factory(config.toolbar);
34002         var ti = [];
34003         if (config.toolbar.items) {
34004             ti = config.toolbar.items ;
34005             delete config.toolbar.items ;
34006         }
34007         
34008         var nitems = [];
34009         this.toolbar.render(tool_el);
34010         for(var i =0;i < ti.length;i++) {
34011           //  Roo.log(['add child', items[i]]);
34012             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34013         }
34014         this.toolbar.items = nitems;
34015         
34016         delete config.toolbar;
34017     }
34018     
34019     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34020     config.grid.scrollBody = true;;
34021     config.grid.monitorWindowResize = false; // turn off autosizing
34022     config.grid.autoHeight = false;
34023     config.grid.autoWidth = false;
34024     
34025     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34026     
34027     if (config.background) {
34028         // render grid on panel activation (if panel background)
34029         this.on('activate', function(gp) {
34030             if (!gp.grid.rendered) {
34031                 gp.grid.render(el);
34032                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34033
34034             }
34035         });
34036             
34037     } else {
34038         this.grid.render(this.wrapper);
34039         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34040
34041     }
34042     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34043     // ??? needed ??? config.el = this.wrapper;
34044     
34045     
34046     
34047   
34048     // xtype created footer. - not sure if will work as we normally have to render first..
34049     if (this.footer && !this.footer.el && this.footer.xtype) {
34050         
34051         var ctr = this.grid.getView().getFooterPanel(true);
34052         this.footer.dataSource = this.grid.dataSource;
34053         this.footer = Roo.factory(this.footer, Roo);
34054         this.footer.render(ctr);
34055         
34056     }
34057     
34058     
34059     
34060     
34061      
34062 };
34063
34064 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34065     getId : function(){
34066         return this.grid.id;
34067     },
34068     
34069     /**
34070      * Returns the grid for this panel
34071      * @return {Roo.bootstrap.Table} 
34072      */
34073     getGrid : function(){
34074         return this.grid;    
34075     },
34076     
34077     setSize : function(width, height){
34078         if(!this.ignoreResize(width, height)){
34079             var grid = this.grid;
34080             var size = this.adjustForComponents(width, height);
34081             var gridel = grid.getGridEl();
34082             gridel.setSize(size.width, size.height);
34083             /*
34084             var thd = grid.getGridEl().select('thead',true).first();
34085             var tbd = grid.getGridEl().select('tbody', true).first();
34086             if (tbd) {
34087                 tbd.setSize(width, height - thd.getHeight());
34088             }
34089             */
34090             grid.autoSize();
34091         }
34092     },
34093      
34094     
34095     
34096     beforeSlide : function(){
34097         this.grid.getView().scroller.clip();
34098     },
34099     
34100     afterSlide : function(){
34101         this.grid.getView().scroller.unclip();
34102     },
34103     
34104     destroy : function(){
34105         this.grid.destroy();
34106         delete this.grid;
34107         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34108     }
34109 });
34110
34111 /**
34112  * @class Roo.bootstrap.panel.Nest
34113  * @extends Roo.bootstrap.panel.Content
34114  * @constructor
34115  * Create a new Panel, that can contain a layout.Border.
34116  * 
34117  * 
34118  * @param {Roo.BorderLayout} layout The layout for this panel
34119  * @param {String/Object} config A string to set only the title or a config object
34120  */
34121 Roo.bootstrap.panel.Nest = function(config)
34122 {
34123     // construct with only one argument..
34124     /* FIXME - implement nicer consturctors
34125     if (layout.layout) {
34126         config = layout;
34127         layout = config.layout;
34128         delete config.layout;
34129     }
34130     if (layout.xtype && !layout.getEl) {
34131         // then layout needs constructing..
34132         layout = Roo.factory(layout, Roo);
34133     }
34134     */
34135     
34136     config.el =  config.layout.getEl();
34137     
34138     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34139     
34140     config.layout.monitorWindowResize = false; // turn off autosizing
34141     this.layout = config.layout;
34142     this.layout.getEl().addClass("roo-layout-nested-layout");
34143     
34144     
34145     
34146     
34147 };
34148
34149 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34150
34151     setSize : function(width, height){
34152         if(!this.ignoreResize(width, height)){
34153             var size = this.adjustForComponents(width, height);
34154             var el = this.layout.getEl();
34155             el.setSize(size.width, size.height);
34156             var touch = el.dom.offsetWidth;
34157             this.layout.layout();
34158             // ie requires a double layout on the first pass
34159             if(Roo.isIE && !this.initialized){
34160                 this.initialized = true;
34161                 this.layout.layout();
34162             }
34163         }
34164     },
34165     
34166     // activate all subpanels if not currently active..
34167     
34168     setActiveState : function(active){
34169         this.active = active;
34170         if(!active){
34171             this.fireEvent("deactivate", this);
34172             return;
34173         }
34174         
34175         this.fireEvent("activate", this);
34176         // not sure if this should happen before or after..
34177         if (!this.layout) {
34178             return; // should not happen..
34179         }
34180         var reg = false;
34181         for (var r in this.layout.regions) {
34182             reg = this.layout.getRegion(r);
34183             if (reg.getActivePanel()) {
34184                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34185                 reg.setActivePanel(reg.getActivePanel());
34186                 continue;
34187             }
34188             if (!reg.panels.length) {
34189                 continue;
34190             }
34191             reg.showPanel(reg.getPanel(0));
34192         }
34193         
34194         
34195         
34196         
34197     },
34198     
34199     /**
34200      * Returns the nested BorderLayout for this panel
34201      * @return {Roo.BorderLayout} 
34202      */
34203     getLayout : function(){
34204         return this.layout;
34205     },
34206     
34207      /**
34208      * Adds a xtype elements to the layout of the nested panel
34209      * <pre><code>
34210
34211 panel.addxtype({
34212        xtype : 'ContentPanel',
34213        region: 'west',
34214        items: [ .... ]
34215    }
34216 );
34217
34218 panel.addxtype({
34219         xtype : 'NestedLayoutPanel',
34220         region: 'west',
34221         layout: {
34222            center: { },
34223            west: { }   
34224         },
34225         items : [ ... list of content panels or nested layout panels.. ]
34226    }
34227 );
34228 </code></pre>
34229      * @param {Object} cfg Xtype definition of item to add.
34230      */
34231     addxtype : function(cfg) {
34232         return this.layout.addxtype(cfg);
34233     
34234     }
34235 });        /*
34236  * Based on:
34237  * Ext JS Library 1.1.1
34238  * Copyright(c) 2006-2007, Ext JS, LLC.
34239  *
34240  * Originally Released Under LGPL - original licence link has changed is not relivant.
34241  *
34242  * Fork - LGPL
34243  * <script type="text/javascript">
34244  */
34245 /**
34246  * @class Roo.TabPanel
34247  * @extends Roo.util.Observable
34248  * A lightweight tab container.
34249  * <br><br>
34250  * Usage:
34251  * <pre><code>
34252 // basic tabs 1, built from existing content
34253 var tabs = new Roo.TabPanel("tabs1");
34254 tabs.addTab("script", "View Script");
34255 tabs.addTab("markup", "View Markup");
34256 tabs.activate("script");
34257
34258 // more advanced tabs, built from javascript
34259 var jtabs = new Roo.TabPanel("jtabs");
34260 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34261
34262 // set up the UpdateManager
34263 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34264 var updater = tab2.getUpdateManager();
34265 updater.setDefaultUrl("ajax1.htm");
34266 tab2.on('activate', updater.refresh, updater, true);
34267
34268 // Use setUrl for Ajax loading
34269 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34270 tab3.setUrl("ajax2.htm", null, true);
34271
34272 // Disabled tab
34273 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34274 tab4.disable();
34275
34276 jtabs.activate("jtabs-1");
34277  * </code></pre>
34278  * @constructor
34279  * Create a new TabPanel.
34280  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34281  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34282  */
34283 Roo.bootstrap.panel.Tabs = function(config){
34284     /**
34285     * The container element for this TabPanel.
34286     * @type Roo.Element
34287     */
34288     this.el = Roo.get(config.el);
34289     delete config.el;
34290     if(config){
34291         if(typeof config == "boolean"){
34292             this.tabPosition = config ? "bottom" : "top";
34293         }else{
34294             Roo.apply(this, config);
34295         }
34296     }
34297     
34298     if(this.tabPosition == "bottom"){
34299         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34300         this.el.addClass("roo-tabs-bottom");
34301     }
34302     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34303     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34304     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34305     if(Roo.isIE){
34306         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34307     }
34308     if(this.tabPosition != "bottom"){
34309         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34310          * @type Roo.Element
34311          */
34312         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34313         this.el.addClass("roo-tabs-top");
34314     }
34315     this.items = [];
34316
34317     this.bodyEl.setStyle("position", "relative");
34318
34319     this.active = null;
34320     this.activateDelegate = this.activate.createDelegate(this);
34321
34322     this.addEvents({
34323         /**
34324          * @event tabchange
34325          * Fires when the active tab changes
34326          * @param {Roo.TabPanel} this
34327          * @param {Roo.TabPanelItem} activePanel The new active tab
34328          */
34329         "tabchange": true,
34330         /**
34331          * @event beforetabchange
34332          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34333          * @param {Roo.TabPanel} this
34334          * @param {Object} e Set cancel to true on this object to cancel the tab change
34335          * @param {Roo.TabPanelItem} tab The tab being changed to
34336          */
34337         "beforetabchange" : true
34338     });
34339
34340     Roo.EventManager.onWindowResize(this.onResize, this);
34341     this.cpad = this.el.getPadding("lr");
34342     this.hiddenCount = 0;
34343
34344
34345     // toolbar on the tabbar support...
34346     if (this.toolbar) {
34347         alert("no toolbar support yet");
34348         this.toolbar  = false;
34349         /*
34350         var tcfg = this.toolbar;
34351         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34352         this.toolbar = new Roo.Toolbar(tcfg);
34353         if (Roo.isSafari) {
34354             var tbl = tcfg.container.child('table', true);
34355             tbl.setAttribute('width', '100%');
34356         }
34357         */
34358         
34359     }
34360    
34361
34362
34363     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34364 };
34365
34366 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34367     /*
34368      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34369      */
34370     tabPosition : "top",
34371     /*
34372      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34373      */
34374     currentTabWidth : 0,
34375     /*
34376      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34377      */
34378     minTabWidth : 40,
34379     /*
34380      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34381      */
34382     maxTabWidth : 250,
34383     /*
34384      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34385      */
34386     preferredTabWidth : 175,
34387     /*
34388      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34389      */
34390     resizeTabs : false,
34391     /*
34392      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34393      */
34394     monitorResize : true,
34395     /*
34396      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34397      */
34398     toolbar : false,
34399
34400     /**
34401      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34402      * @param {String} id The id of the div to use <b>or create</b>
34403      * @param {String} text The text for the tab
34404      * @param {String} content (optional) Content to put in the TabPanelItem body
34405      * @param {Boolean} closable (optional) True to create a close icon on the tab
34406      * @return {Roo.TabPanelItem} The created TabPanelItem
34407      */
34408     addTab : function(id, text, content, closable)
34409     {
34410         var item = new Roo.bootstrap.panel.TabItem({
34411             panel: this,
34412             id : id,
34413             text : text,
34414             closable : closable
34415         });
34416         this.addTabItem(item);
34417         if(content){
34418             item.setContent(content);
34419         }
34420         return item;
34421     },
34422
34423     /**
34424      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34425      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34426      * @return {Roo.TabPanelItem}
34427      */
34428     getTab : function(id){
34429         return this.items[id];
34430     },
34431
34432     /**
34433      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34434      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34435      */
34436     hideTab : function(id){
34437         var t = this.items[id];
34438         if(!t.isHidden()){
34439            t.setHidden(true);
34440            this.hiddenCount++;
34441            this.autoSizeTabs();
34442         }
34443     },
34444
34445     /**
34446      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34447      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34448      */
34449     unhideTab : function(id){
34450         var t = this.items[id];
34451         if(t.isHidden()){
34452            t.setHidden(false);
34453            this.hiddenCount--;
34454            this.autoSizeTabs();
34455         }
34456     },
34457
34458     /**
34459      * Adds an existing {@link Roo.TabPanelItem}.
34460      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34461      */
34462     addTabItem : function(item){
34463         this.items[item.id] = item;
34464         this.items.push(item);
34465       //  if(this.resizeTabs){
34466     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34467   //         this.autoSizeTabs();
34468 //        }else{
34469 //            item.autoSize();
34470        // }
34471     },
34472
34473     /**
34474      * Removes a {@link Roo.TabPanelItem}.
34475      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34476      */
34477     removeTab : function(id){
34478         var items = this.items;
34479         var tab = items[id];
34480         if(!tab) { return; }
34481         var index = items.indexOf(tab);
34482         if(this.active == tab && items.length > 1){
34483             var newTab = this.getNextAvailable(index);
34484             if(newTab) {
34485                 newTab.activate();
34486             }
34487         }
34488         this.stripEl.dom.removeChild(tab.pnode.dom);
34489         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34490             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34491         }
34492         items.splice(index, 1);
34493         delete this.items[tab.id];
34494         tab.fireEvent("close", tab);
34495         tab.purgeListeners();
34496         this.autoSizeTabs();
34497     },
34498
34499     getNextAvailable : function(start){
34500         var items = this.items;
34501         var index = start;
34502         // look for a next tab that will slide over to
34503         // replace the one being removed
34504         while(index < items.length){
34505             var item = items[++index];
34506             if(item && !item.isHidden()){
34507                 return item;
34508             }
34509         }
34510         // if one isn't found select the previous tab (on the left)
34511         index = start;
34512         while(index >= 0){
34513             var item = items[--index];
34514             if(item && !item.isHidden()){
34515                 return item;
34516             }
34517         }
34518         return null;
34519     },
34520
34521     /**
34522      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34523      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34524      */
34525     disableTab : function(id){
34526         var tab = this.items[id];
34527         if(tab && this.active != tab){
34528             tab.disable();
34529         }
34530     },
34531
34532     /**
34533      * Enables a {@link Roo.TabPanelItem} that is disabled.
34534      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34535      */
34536     enableTab : function(id){
34537         var tab = this.items[id];
34538         tab.enable();
34539     },
34540
34541     /**
34542      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34543      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34544      * @return {Roo.TabPanelItem} The TabPanelItem.
34545      */
34546     activate : function(id){
34547         var tab = this.items[id];
34548         if(!tab){
34549             return null;
34550         }
34551         if(tab == this.active || tab.disabled){
34552             return tab;
34553         }
34554         var e = {};
34555         this.fireEvent("beforetabchange", this, e, tab);
34556         if(e.cancel !== true && !tab.disabled){
34557             if(this.active){
34558                 this.active.hide();
34559             }
34560             this.active = this.items[id];
34561             this.active.show();
34562             this.fireEvent("tabchange", this, this.active);
34563         }
34564         return tab;
34565     },
34566
34567     /**
34568      * Gets the active {@link Roo.TabPanelItem}.
34569      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34570      */
34571     getActiveTab : function(){
34572         return this.active;
34573     },
34574
34575     /**
34576      * Updates the tab body element to fit the height of the container element
34577      * for overflow scrolling
34578      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34579      */
34580     syncHeight : function(targetHeight){
34581         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34582         var bm = this.bodyEl.getMargins();
34583         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34584         this.bodyEl.setHeight(newHeight);
34585         return newHeight;
34586     },
34587
34588     onResize : function(){
34589         if(this.monitorResize){
34590             this.autoSizeTabs();
34591         }
34592     },
34593
34594     /**
34595      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34596      */
34597     beginUpdate : function(){
34598         this.updating = true;
34599     },
34600
34601     /**
34602      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34603      */
34604     endUpdate : function(){
34605         this.updating = false;
34606         this.autoSizeTabs();
34607     },
34608
34609     /**
34610      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34611      */
34612     autoSizeTabs : function(){
34613         var count = this.items.length;
34614         var vcount = count - this.hiddenCount;
34615         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34616             return;
34617         }
34618         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34619         var availWidth = Math.floor(w / vcount);
34620         var b = this.stripBody;
34621         if(b.getWidth() > w){
34622             var tabs = this.items;
34623             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34624             if(availWidth < this.minTabWidth){
34625                 /*if(!this.sleft){    // incomplete scrolling code
34626                     this.createScrollButtons();
34627                 }
34628                 this.showScroll();
34629                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34630             }
34631         }else{
34632             if(this.currentTabWidth < this.preferredTabWidth){
34633                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34634             }
34635         }
34636     },
34637
34638     /**
34639      * Returns the number of tabs in this TabPanel.
34640      * @return {Number}
34641      */
34642      getCount : function(){
34643          return this.items.length;
34644      },
34645
34646     /**
34647      * Resizes all the tabs to the passed width
34648      * @param {Number} The new width
34649      */
34650     setTabWidth : function(width){
34651         this.currentTabWidth = width;
34652         for(var i = 0, len = this.items.length; i < len; i++) {
34653                 if(!this.items[i].isHidden()) {
34654                 this.items[i].setWidth(width);
34655             }
34656         }
34657     },
34658
34659     /**
34660      * Destroys this TabPanel
34661      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34662      */
34663     destroy : function(removeEl){
34664         Roo.EventManager.removeResizeListener(this.onResize, this);
34665         for(var i = 0, len = this.items.length; i < len; i++){
34666             this.items[i].purgeListeners();
34667         }
34668         if(removeEl === true){
34669             this.el.update("");
34670             this.el.remove();
34671         }
34672     },
34673     
34674     createStrip : function(container)
34675     {
34676         var strip = document.createElement("nav");
34677         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34678         container.appendChild(strip);
34679         return strip;
34680     },
34681     
34682     createStripList : function(strip)
34683     {
34684         // div wrapper for retard IE
34685         // returns the "tr" element.
34686         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34687         //'<div class="x-tabs-strip-wrap">'+
34688           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34689           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34690         return strip.firstChild; //.firstChild.firstChild.firstChild;
34691     },
34692     createBody : function(container)
34693     {
34694         var body = document.createElement("div");
34695         Roo.id(body, "tab-body");
34696         //Roo.fly(body).addClass("x-tabs-body");
34697         Roo.fly(body).addClass("tab-content");
34698         container.appendChild(body);
34699         return body;
34700     },
34701     createItemBody :function(bodyEl, id){
34702         var body = Roo.getDom(id);
34703         if(!body){
34704             body = document.createElement("div");
34705             body.id = id;
34706         }
34707         //Roo.fly(body).addClass("x-tabs-item-body");
34708         Roo.fly(body).addClass("tab-pane");
34709          bodyEl.insertBefore(body, bodyEl.firstChild);
34710         return body;
34711     },
34712     /** @private */
34713     createStripElements :  function(stripEl, text, closable)
34714     {
34715         var td = document.createElement("li"); // was td..
34716         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34717         //stripEl.appendChild(td);
34718         /*if(closable){
34719             td.className = "x-tabs-closable";
34720             if(!this.closeTpl){
34721                 this.closeTpl = new Roo.Template(
34722                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34723                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34724                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34725                 );
34726             }
34727             var el = this.closeTpl.overwrite(td, {"text": text});
34728             var close = el.getElementsByTagName("div")[0];
34729             var inner = el.getElementsByTagName("em")[0];
34730             return {"el": el, "close": close, "inner": inner};
34731         } else {
34732         */
34733         // not sure what this is..
34734             if(!this.tabTpl){
34735                 //this.tabTpl = new Roo.Template(
34736                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34737                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34738                 //);
34739                 this.tabTpl = new Roo.Template(
34740                    '<a href="#">' +
34741                    '<span unselectable="on"' +
34742                             (this.disableTooltips ? '' : ' title="{text}"') +
34743                             ' >{text}</span></span></a>'
34744                 );
34745                 
34746             }
34747             var el = this.tabTpl.overwrite(td, {"text": text});
34748             var inner = el.getElementsByTagName("span")[0];
34749             return {"el": el, "inner": inner};
34750         //}
34751     }
34752         
34753     
34754 });
34755
34756 /**
34757  * @class Roo.TabPanelItem
34758  * @extends Roo.util.Observable
34759  * Represents an individual item (tab plus body) in a TabPanel.
34760  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34761  * @param {String} id The id of this TabPanelItem
34762  * @param {String} text The text for the tab of this TabPanelItem
34763  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34764  */
34765 Roo.bootstrap.panel.TabItem = function(config){
34766     /**
34767      * The {@link Roo.TabPanel} this TabPanelItem belongs to
34768      * @type Roo.TabPanel
34769      */
34770     this.tabPanel = config.panel;
34771     /**
34772      * The id for this TabPanelItem
34773      * @type String
34774      */
34775     this.id = config.id;
34776     /** @private */
34777     this.disabled = false;
34778     /** @private */
34779     this.text = config.text;
34780     /** @private */
34781     this.loaded = false;
34782     this.closable = config.closable;
34783
34784     /**
34785      * The body element for this TabPanelItem.
34786      * @type Roo.Element
34787      */
34788     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34789     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34790     this.bodyEl.setStyle("display", "block");
34791     this.bodyEl.setStyle("zoom", "1");
34792     //this.hideAction();
34793
34794     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34795     /** @private */
34796     this.el = Roo.get(els.el);
34797     this.inner = Roo.get(els.inner, true);
34798     this.textEl = Roo.get(this.el.dom.firstChild, true);
34799     this.pnode = Roo.get(els.el.parentNode, true);
34800     this.el.on("mousedown", this.onTabMouseDown, this);
34801     this.el.on("click", this.onTabClick, this);
34802     /** @private */
34803     if(config.closable){
34804         var c = Roo.get(els.close, true);
34805         c.dom.title = this.closeText;
34806         c.addClassOnOver("close-over");
34807         c.on("click", this.closeClick, this);
34808      }
34809
34810     this.addEvents({
34811          /**
34812          * @event activate
34813          * Fires when this tab becomes the active tab.
34814          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34815          * @param {Roo.TabPanelItem} this
34816          */
34817         "activate": true,
34818         /**
34819          * @event beforeclose
34820          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34821          * @param {Roo.TabPanelItem} this
34822          * @param {Object} e Set cancel to true on this object to cancel the close.
34823          */
34824         "beforeclose": true,
34825         /**
34826          * @event close
34827          * Fires when this tab is closed.
34828          * @param {Roo.TabPanelItem} this
34829          */
34830          "close": true,
34831         /**
34832          * @event deactivate
34833          * Fires when this tab is no longer the active tab.
34834          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34835          * @param {Roo.TabPanelItem} this
34836          */
34837          "deactivate" : true
34838     });
34839     this.hidden = false;
34840
34841     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34842 };
34843
34844 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34845            {
34846     purgeListeners : function(){
34847        Roo.util.Observable.prototype.purgeListeners.call(this);
34848        this.el.removeAllListeners();
34849     },
34850     /**
34851      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34852      */
34853     show : function(){
34854         this.pnode.addClass("active");
34855         this.showAction();
34856         if(Roo.isOpera){
34857             this.tabPanel.stripWrap.repaint();
34858         }
34859         this.fireEvent("activate", this.tabPanel, this);
34860     },
34861
34862     /**
34863      * Returns true if this tab is the active tab.
34864      * @return {Boolean}
34865      */
34866     isActive : function(){
34867         return this.tabPanel.getActiveTab() == this;
34868     },
34869
34870     /**
34871      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34872      */
34873     hide : function(){
34874         this.pnode.removeClass("active");
34875         this.hideAction();
34876         this.fireEvent("deactivate", this.tabPanel, this);
34877     },
34878
34879     hideAction : function(){
34880         this.bodyEl.hide();
34881         this.bodyEl.setStyle("position", "absolute");
34882         this.bodyEl.setLeft("-20000px");
34883         this.bodyEl.setTop("-20000px");
34884     },
34885
34886     showAction : function(){
34887         this.bodyEl.setStyle("position", "relative");
34888         this.bodyEl.setTop("");
34889         this.bodyEl.setLeft("");
34890         this.bodyEl.show();
34891     },
34892
34893     /**
34894      * Set the tooltip for the tab.
34895      * @param {String} tooltip The tab's tooltip
34896      */
34897     setTooltip : function(text){
34898         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34899             this.textEl.dom.qtip = text;
34900             this.textEl.dom.removeAttribute('title');
34901         }else{
34902             this.textEl.dom.title = text;
34903         }
34904     },
34905
34906     onTabClick : function(e){
34907         e.preventDefault();
34908         this.tabPanel.activate(this.id);
34909     },
34910
34911     onTabMouseDown : function(e){
34912         e.preventDefault();
34913         this.tabPanel.activate(this.id);
34914     },
34915 /*
34916     getWidth : function(){
34917         return this.inner.getWidth();
34918     },
34919
34920     setWidth : function(width){
34921         var iwidth = width - this.pnode.getPadding("lr");
34922         this.inner.setWidth(iwidth);
34923         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34924         this.pnode.setWidth(width);
34925     },
34926 */
34927     /**
34928      * Show or hide the tab
34929      * @param {Boolean} hidden True to hide or false to show.
34930      */
34931     setHidden : function(hidden){
34932         this.hidden = hidden;
34933         this.pnode.setStyle("display", hidden ? "none" : "");
34934     },
34935
34936     /**
34937      * Returns true if this tab is "hidden"
34938      * @return {Boolean}
34939      */
34940     isHidden : function(){
34941         return this.hidden;
34942     },
34943
34944     /**
34945      * Returns the text for this tab
34946      * @return {String}
34947      */
34948     getText : function(){
34949         return this.text;
34950     },
34951     /*
34952     autoSize : function(){
34953         //this.el.beginMeasure();
34954         this.textEl.setWidth(1);
34955         /*
34956          *  #2804 [new] Tabs in Roojs
34957          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34958          */
34959         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34960         //this.el.endMeasure();
34961     //},
34962
34963     /**
34964      * Sets the text for the tab (Note: this also sets the tooltip text)
34965      * @param {String} text The tab's text and tooltip
34966      */
34967     setText : function(text){
34968         this.text = text;
34969         this.textEl.update(text);
34970         this.setTooltip(text);
34971         //if(!this.tabPanel.resizeTabs){
34972         //    this.autoSize();
34973         //}
34974     },
34975     /**
34976      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34977      */
34978     activate : function(){
34979         this.tabPanel.activate(this.id);
34980     },
34981
34982     /**
34983      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34984      */
34985     disable : function(){
34986         if(this.tabPanel.active != this){
34987             this.disabled = true;
34988             this.pnode.addClass("disabled");
34989         }
34990     },
34991
34992     /**
34993      * Enables this TabPanelItem if it was previously disabled.
34994      */
34995     enable : function(){
34996         this.disabled = false;
34997         this.pnode.removeClass("disabled");
34998     },
34999
35000     /**
35001      * Sets the content for this TabPanelItem.
35002      * @param {String} content The content
35003      * @param {Boolean} loadScripts true to look for and load scripts
35004      */
35005     setContent : function(content, loadScripts){
35006         this.bodyEl.update(content, loadScripts);
35007     },
35008
35009     /**
35010      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35011      * @return {Roo.UpdateManager} The UpdateManager
35012      */
35013     getUpdateManager : function(){
35014         return this.bodyEl.getUpdateManager();
35015     },
35016
35017     /**
35018      * Set a URL to be used to load the content for this TabPanelItem.
35019      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35020      * @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)
35021      * @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)
35022      * @return {Roo.UpdateManager} The UpdateManager
35023      */
35024     setUrl : function(url, params, loadOnce){
35025         if(this.refreshDelegate){
35026             this.un('activate', this.refreshDelegate);
35027         }
35028         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35029         this.on("activate", this.refreshDelegate);
35030         return this.bodyEl.getUpdateManager();
35031     },
35032
35033     /** @private */
35034     _handleRefresh : function(url, params, loadOnce){
35035         if(!loadOnce || !this.loaded){
35036             var updater = this.bodyEl.getUpdateManager();
35037             updater.update(url, params, this._setLoaded.createDelegate(this));
35038         }
35039     },
35040
35041     /**
35042      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35043      *   Will fail silently if the setUrl method has not been called.
35044      *   This does not activate the panel, just updates its content.
35045      */
35046     refresh : function(){
35047         if(this.refreshDelegate){
35048            this.loaded = false;
35049            this.refreshDelegate();
35050         }
35051     },
35052
35053     /** @private */
35054     _setLoaded : function(){
35055         this.loaded = true;
35056     },
35057
35058     /** @private */
35059     closeClick : function(e){
35060         var o = {};
35061         e.stopEvent();
35062         this.fireEvent("beforeclose", this, o);
35063         if(o.cancel !== true){
35064             this.tabPanel.removeTab(this.id);
35065         }
35066     },
35067     /**
35068      * The text displayed in the tooltip for the close icon.
35069      * @type String
35070      */
35071     closeText : "Close this tab"
35072 });