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         
6006         this.el.on("contextmenu", this.onContextMenu, this);
6007         
6008         this.mainBody.on('scroll', this.onBodyScroll, this);
6009         
6010         
6011     },
6012     
6013     onContextMenu : function(e, t)
6014     {
6015         this.processEvent("contextmenu", e);
6016     },
6017     
6018     processEvent : function(name, e)
6019     {
6020         if (name != 'touchstart' ) {
6021             this.fireEvent(name, e);    
6022         }
6023         
6024         var t = e.getTarget();
6025         
6026         var cell = Roo.get(t);
6027         
6028         if(!cell){
6029             return;
6030         }
6031         
6032         if(cell.findParent('tfoot', false, true)){
6033             return;
6034         }
6035         
6036         if(cell.findParent('thead', false, true)){
6037             
6038             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6039                 cell = Roo.get(t).findParent('th', false, true);
6040                 if (!cell) {
6041                     Roo.log("failed to find th in thead?");
6042                     Roo.log(e.getTarget());
6043                     return;
6044                 }
6045             }
6046             
6047             var cellIndex = cell.dom.cellIndex;
6048             
6049             var ename = name == 'touchstart' ? 'click' : name;
6050             this.fireEvent("header" + ename, this, cellIndex, e);
6051             
6052             return;
6053         }
6054         
6055         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6056             cell = Roo.get(t).findParent('td', false, true);
6057             if (!cell) {
6058                 Roo.log("failed to find th in tbody?");
6059                 Roo.log(e.getTarget());
6060                 return;
6061             }
6062         }
6063         
6064         var row = cell.findParent('tr', false, true);
6065         var cellIndex = cell.dom.cellIndex;
6066         var rowIndex = row.dom.rowIndex - 1;
6067         
6068         if(row !== false){
6069             
6070             this.fireEvent("row" + name, this, rowIndex, e);
6071             
6072             if(cell !== false){
6073             
6074                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6075             }
6076         }
6077         
6078     },
6079     
6080     onMouseover : function(e, el)
6081     {
6082         var cell = Roo.get(el);
6083         
6084         if(!cell){
6085             return;
6086         }
6087         
6088         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6089             cell = cell.findParent('td', false, true);
6090         }
6091         
6092         var row = cell.findParent('tr', false, true);
6093         var cellIndex = cell.dom.cellIndex;
6094         var rowIndex = row.dom.rowIndex - 1; // start from 0
6095         
6096         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6097         
6098     },
6099     
6100     onMouseout : function(e, el)
6101     {
6102         var cell = Roo.get(el);
6103         
6104         if(!cell){
6105             return;
6106         }
6107         
6108         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6109             cell = cell.findParent('td', false, true);
6110         }
6111         
6112         var row = cell.findParent('tr', false, true);
6113         var cellIndex = cell.dom.cellIndex;
6114         var rowIndex = row.dom.rowIndex - 1; // start from 0
6115         
6116         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6117         
6118     },
6119     
6120     onClick : function(e, el)
6121     {
6122         var cell = Roo.get(el);
6123         
6124         if(!cell || (!this.cellSelection && !this.rowSelection)){
6125             return;
6126         }
6127         
6128         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6129             cell = cell.findParent('td', false, true);
6130         }
6131         
6132         if(!cell || typeof(cell) == 'undefined'){
6133             return;
6134         }
6135         
6136         var row = cell.findParent('tr', false, true);
6137         
6138         if(!row || typeof(row) == 'undefined'){
6139             return;
6140         }
6141         
6142         var cellIndex = cell.dom.cellIndex;
6143         var rowIndex = this.getRowIndex(row);
6144         
6145         // why??? - should these not be based on SelectionModel?
6146         if(this.cellSelection){
6147             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6148         }
6149         
6150         if(this.rowSelection){
6151             this.fireEvent('rowclick', this, row, rowIndex, e);
6152         }
6153         
6154         
6155     },
6156     
6157     onDblClick : function(e,el)
6158     {
6159         var cell = Roo.get(el);
6160         
6161         if(!cell || (!this.CellSelection && !this.RowSelection)){
6162             return;
6163         }
6164         
6165         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6166             cell = cell.findParent('td', false, true);
6167         }
6168         
6169         if(!cell || typeof(cell) == 'undefined'){
6170             return;
6171         }
6172         
6173         var row = cell.findParent('tr', false, true);
6174         
6175         if(!row || typeof(row) == 'undefined'){
6176             return;
6177         }
6178         
6179         var cellIndex = cell.dom.cellIndex;
6180         var rowIndex = this.getRowIndex(row);
6181         
6182         if(this.CellSelection){
6183             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6184         }
6185         
6186         if(this.RowSelection){
6187             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6188         }
6189     },
6190     
6191     sort : function(e,el)
6192     {
6193         var col = Roo.get(el);
6194         
6195         if(!col.hasClass('sortable')){
6196             return;
6197         }
6198         
6199         var sort = col.attr('sort');
6200         var dir = 'ASC';
6201         
6202         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6203             dir = 'DESC';
6204         }
6205         
6206         this.store.sortInfo = {field : sort, direction : dir};
6207         
6208         if (this.footer) {
6209             Roo.log("calling footer first");
6210             this.footer.onClick('first');
6211         } else {
6212         
6213             this.store.load({ params : { start : 0 } });
6214         }
6215     },
6216     
6217     renderHeader : function()
6218     {
6219         var header = {
6220             tag: 'thead',
6221             cn : []
6222         };
6223         
6224         var cm = this.cm;
6225         this.totalWidth = 0;
6226         
6227         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6228             
6229             var config = cm.config[i];
6230             
6231             var c = {
6232                 tag: 'th',
6233                 style : '',
6234                 html: cm.getColumnHeader(i)
6235             };
6236             
6237             var hh = '';
6238             
6239             if(typeof(config.sortable) != 'undefined' && config.sortable){
6240                 c.cls = 'sortable';
6241                 c.html = '<i class="glyphicon"></i>' + c.html;
6242             }
6243             
6244             if(typeof(config.lgHeader) != 'undefined'){
6245                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6246             }
6247             
6248             if(typeof(config.mdHeader) != 'undefined'){
6249                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6250             }
6251             
6252             if(typeof(config.smHeader) != 'undefined'){
6253                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6254             }
6255             
6256             if(typeof(config.xsHeader) != 'undefined'){
6257                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6258             }
6259             
6260             if(hh.length){
6261                 c.html = hh;
6262             }
6263             
6264             if(typeof(config.tooltip) != 'undefined'){
6265                 c.tooltip = config.tooltip;
6266             }
6267             
6268             if(typeof(config.colspan) != 'undefined'){
6269                 c.colspan = config.colspan;
6270             }
6271             
6272             if(typeof(config.hidden) != 'undefined' && config.hidden){
6273                 c.style += ' display:none;';
6274             }
6275             
6276             if(typeof(config.dataIndex) != 'undefined'){
6277                 c.sort = config.dataIndex;
6278             }
6279             
6280            
6281             
6282             if(typeof(config.align) != 'undefined' && config.align.length){
6283                 c.style += ' text-align:' + config.align + ';';
6284             }
6285             
6286             if(typeof(config.width) != 'undefined'){
6287                 c.style += ' width:' + config.width + 'px;';
6288                 this.totalWidth += config.width;
6289             } else {
6290                 this.totalWidth += 100; // assume minimum of 100 per column?
6291             }
6292             
6293             if(typeof(config.cls) != 'undefined'){
6294                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6295             }
6296             
6297             ['xs','sm','md','lg'].map(function(size){
6298                 
6299                 if(typeof(config[size]) == 'undefined'){
6300                     return;
6301                 }
6302                 
6303                 if (!config[size]) { // 0 = hidden
6304                     c.cls += ' hidden-' + size;
6305                     return;
6306                 }
6307                 
6308                 c.cls += ' col-' + size + '-' + config[size];
6309
6310             });
6311             
6312             header.cn.push(c)
6313         }
6314         
6315         return header;
6316     },
6317     
6318     renderBody : function()
6319     {
6320         var body = {
6321             tag: 'tbody',
6322             cn : [
6323                 {
6324                     tag: 'tr',
6325                     cn : [
6326                         {
6327                             tag : 'td',
6328                             colspan :  this.cm.getColumnCount()
6329                         }
6330                     ]
6331                 }
6332             ]
6333         };
6334         
6335         return body;
6336     },
6337     
6338     renderFooter : function()
6339     {
6340         var footer = {
6341             tag: 'tfoot',
6342             cn : [
6343                 {
6344                     tag: 'tr',
6345                     cn : [
6346                         {
6347                             tag : 'td',
6348                             colspan :  this.cm.getColumnCount()
6349                         }
6350                     ]
6351                 }
6352             ]
6353         };
6354         
6355         return footer;
6356     },
6357     
6358     
6359     
6360     onLoad : function()
6361     {
6362 //        Roo.log('ds onload');
6363         this.clear();
6364         
6365         var _this = this;
6366         var cm = this.cm;
6367         var ds = this.store;
6368         
6369         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6370             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6371             if (_this.store.sortInfo) {
6372                     
6373                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6374                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6375                 }
6376                 
6377                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6378                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6379                 }
6380             }
6381         });
6382         
6383         var tbody =  this.mainBody;
6384               
6385         if(ds.getCount() > 0){
6386             ds.data.each(function(d,rowIndex){
6387                 var row =  this.renderRow(cm, ds, rowIndex);
6388                 
6389                 tbody.createChild(row);
6390                 
6391                 var _this = this;
6392                 
6393                 if(row.cellObjects.length){
6394                     Roo.each(row.cellObjects, function(r){
6395                         _this.renderCellObject(r);
6396                     })
6397                 }
6398                 
6399             }, this);
6400         }
6401         
6402         Roo.each(this.el.select('tbody td', true).elements, function(e){
6403             e.on('mouseover', _this.onMouseover, _this);
6404         });
6405         
6406         Roo.each(this.el.select('tbody td', true).elements, function(e){
6407             e.on('mouseout', _this.onMouseout, _this);
6408         });
6409         this.fireEvent('rowsrendered', this);
6410         //if(this.loadMask){
6411         //    this.maskEl.hide();
6412         //}
6413         
6414         this.autoSize();
6415     },
6416     
6417     
6418     onUpdate : function(ds,record)
6419     {
6420         this.refreshRow(record);
6421     },
6422     
6423     onRemove : function(ds, record, index, isUpdate){
6424         if(isUpdate !== true){
6425             this.fireEvent("beforerowremoved", this, index, record);
6426         }
6427         var bt = this.mainBody.dom;
6428         
6429         var rows = this.el.select('tbody > tr', true).elements;
6430         
6431         if(typeof(rows[index]) != 'undefined'){
6432             bt.removeChild(rows[index].dom);
6433         }
6434         
6435 //        if(bt.rows[index]){
6436 //            bt.removeChild(bt.rows[index]);
6437 //        }
6438         
6439         if(isUpdate !== true){
6440             //this.stripeRows(index);
6441             //this.syncRowHeights(index, index);
6442             //this.layout();
6443             this.fireEvent("rowremoved", this, index, record);
6444         }
6445     },
6446     
6447     onAdd : function(ds, records, rowIndex)
6448     {
6449         //Roo.log('on Add called');
6450         // - note this does not handle multiple adding very well..
6451         var bt = this.mainBody.dom;
6452         for (var i =0 ; i < records.length;i++) {
6453             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6454             //Roo.log(records[i]);
6455             //Roo.log(this.store.getAt(rowIndex+i));
6456             this.insertRow(this.store, rowIndex + i, false);
6457             return;
6458         }
6459         
6460     },
6461     
6462     
6463     refreshRow : function(record){
6464         var ds = this.store, index;
6465         if(typeof record == 'number'){
6466             index = record;
6467             record = ds.getAt(index);
6468         }else{
6469             index = ds.indexOf(record);
6470         }
6471         this.insertRow(ds, index, true);
6472         this.onRemove(ds, record, index+1, true);
6473         //this.syncRowHeights(index, index);
6474         //this.layout();
6475         this.fireEvent("rowupdated", this, index, record);
6476     },
6477     
6478     insertRow : function(dm, rowIndex, isUpdate){
6479         
6480         if(!isUpdate){
6481             this.fireEvent("beforerowsinserted", this, rowIndex);
6482         }
6483             //var s = this.getScrollState();
6484         var row = this.renderRow(this.cm, this.store, rowIndex);
6485         // insert before rowIndex..
6486         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6487         
6488         var _this = this;
6489                 
6490         if(row.cellObjects.length){
6491             Roo.each(row.cellObjects, function(r){
6492                 _this.renderCellObject(r);
6493             })
6494         }
6495             
6496         if(!isUpdate){
6497             this.fireEvent("rowsinserted", this, rowIndex);
6498             //this.syncRowHeights(firstRow, lastRow);
6499             //this.stripeRows(firstRow);
6500             //this.layout();
6501         }
6502         
6503     },
6504     
6505     
6506     getRowDom : function(rowIndex)
6507     {
6508         var rows = this.el.select('tbody > tr', true).elements;
6509         
6510         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6511         
6512     },
6513     // returns the object tree for a tr..
6514   
6515     
6516     renderRow : function(cm, ds, rowIndex) 
6517     {
6518         
6519         var d = ds.getAt(rowIndex);
6520         
6521         var row = {
6522             tag : 'tr',
6523             cn : []
6524         };
6525             
6526         var cellObjects = [];
6527         
6528         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6529             var config = cm.config[i];
6530             
6531             var renderer = cm.getRenderer(i);
6532             var value = '';
6533             var id = false;
6534             
6535             if(typeof(renderer) !== 'undefined'){
6536                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6537             }
6538             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6539             // and are rendered into the cells after the row is rendered - using the id for the element.
6540             
6541             if(typeof(value) === 'object'){
6542                 id = Roo.id();
6543                 cellObjects.push({
6544                     container : id,
6545                     cfg : value 
6546                 })
6547             }
6548             
6549             var rowcfg = {
6550                 record: d,
6551                 rowIndex : rowIndex,
6552                 colIndex : i,
6553                 rowClass : ''
6554             };
6555
6556             this.fireEvent('rowclass', this, rowcfg);
6557             
6558             var td = {
6559                 tag: 'td',
6560                 cls : rowcfg.rowClass,
6561                 style: '',
6562                 html: (typeof(value) === 'object') ? '' : value
6563             };
6564             
6565             if (id) {
6566                 td.id = id;
6567             }
6568             
6569             if(typeof(config.colspan) != 'undefined'){
6570                 td.colspan = config.colspan;
6571             }
6572             
6573             if(typeof(config.hidden) != 'undefined' && config.hidden){
6574                 td.style += ' display:none;';
6575             }
6576             
6577             if(typeof(config.align) != 'undefined' && config.align.length){
6578                 td.style += ' text-align:' + config.align + ';';
6579             }
6580             
6581             if(typeof(config.width) != 'undefined'){
6582                 td.style += ' width:' +  config.width + 'px;';
6583             }
6584             
6585             if(typeof(config.cursor) != 'undefined'){
6586                 td.style += ' cursor:' +  config.cursor + ';';
6587             }
6588             
6589             if(typeof(config.cls) != 'undefined'){
6590                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6591             }
6592             
6593             ['xs','sm','md','lg'].map(function(size){
6594                 
6595                 if(typeof(config[size]) == 'undefined'){
6596                     return;
6597                 }
6598                 
6599                 if (!config[size]) { // 0 = hidden
6600                     td.cls += ' hidden-' + size;
6601                     return;
6602                 }
6603                 
6604                 td.cls += ' col-' + size + '-' + config[size];
6605
6606             });
6607              
6608             row.cn.push(td);
6609            
6610         }
6611         
6612         row.cellObjects = cellObjects;
6613         
6614         return row;
6615           
6616     },
6617     
6618     
6619     
6620     onBeforeLoad : function()
6621     {
6622         //Roo.log('ds onBeforeLoad');
6623         
6624         //this.clear();
6625         
6626         //if(this.loadMask){
6627         //    this.maskEl.show();
6628         //}
6629     },
6630      /**
6631      * Remove all rows
6632      */
6633     clear : function()
6634     {
6635         this.el.select('tbody', true).first().dom.innerHTML = '';
6636     },
6637     /**
6638      * Show or hide a row.
6639      * @param {Number} rowIndex to show or hide
6640      * @param {Boolean} state hide
6641      */
6642     setRowVisibility : function(rowIndex, state)
6643     {
6644         var bt = this.mainBody.dom;
6645         
6646         var rows = this.el.select('tbody > tr', true).elements;
6647         
6648         if(typeof(rows[rowIndex]) == 'undefined'){
6649             return;
6650         }
6651         rows[rowIndex].dom.style.display = state ? '' : 'none';
6652     },
6653     
6654     
6655     getSelectionModel : function(){
6656         if(!this.selModel){
6657             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6658         }
6659         return this.selModel;
6660     },
6661     /*
6662      * Render the Roo.bootstrap object from renderder
6663      */
6664     renderCellObject : function(r)
6665     {
6666         var _this = this;
6667         
6668         var t = r.cfg.render(r.container);
6669         
6670         if(r.cfg.cn){
6671             Roo.each(r.cfg.cn, function(c){
6672                 var child = {
6673                     container: t.getChildContainer(),
6674                     cfg: c
6675                 };
6676                 _this.renderCellObject(child);
6677             })
6678         }
6679     },
6680     
6681     getRowIndex : function(row)
6682     {
6683         var rowIndex = -1;
6684         
6685         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6686             if(el != row){
6687                 return;
6688             }
6689             
6690             rowIndex = index;
6691         });
6692         
6693         return rowIndex;
6694     },
6695      /**
6696      * Returns the grid's underlying element = used by panel.Grid
6697      * @return {Element} The element
6698      */
6699     getGridEl : function(){
6700         return this.el;
6701     },
6702      /**
6703      * Forces a resize - used by panel.Grid
6704      * @return {Element} The element
6705      */
6706     autoSize : function(){
6707         //var ctr = Roo.get(this.container.dom.parentElement);
6708         var ctr = Roo.get(this.el.dom);
6709         
6710         var thd = this.getGridEl().select('thead',true).first();
6711         var tbd = this.getGridEl().select('tbody', true).first();
6712         
6713         
6714         var cw = ctr.getWidth();
6715         
6716         if (tbd) {
6717             
6718             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6719             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6720             cw -= barsize;
6721         }
6722         cw = Math.max(cw, this.totalWidth);
6723         this.getGridEl().select('tr',true).setWidth(cw);
6724         
6725         return; // we doe not have a view in this design..
6726         
6727     },
6728     onBodyScroll: function()
6729     {
6730         
6731         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6732         this.mainHead.setStyle({
6733                     'position' : 'relative',
6734                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6735         });
6736         
6737         
6738     }
6739 });
6740
6741  
6742
6743  /*
6744  * - LGPL
6745  *
6746  * table cell
6747  * 
6748  */
6749
6750 /**
6751  * @class Roo.bootstrap.TableCell
6752  * @extends Roo.bootstrap.Component
6753  * Bootstrap TableCell class
6754  * @cfg {String} html cell contain text
6755  * @cfg {String} cls cell class
6756  * @cfg {String} tag cell tag (td|th) default td
6757  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6758  * @cfg {String} align Aligns the content in a cell
6759  * @cfg {String} axis Categorizes cells
6760  * @cfg {String} bgcolor Specifies the background color of a cell
6761  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6762  * @cfg {Number} colspan Specifies the number of columns a cell should span
6763  * @cfg {String} headers Specifies one or more header cells a cell is related to
6764  * @cfg {Number} height Sets the height of a cell
6765  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6766  * @cfg {Number} rowspan Sets the number of rows a cell should span
6767  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6768  * @cfg {String} valign Vertical aligns the content in a cell
6769  * @cfg {Number} width Specifies the width of a cell
6770  * 
6771  * @constructor
6772  * Create a new TableCell
6773  * @param {Object} config The config object
6774  */
6775
6776 Roo.bootstrap.TableCell = function(config){
6777     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6778 };
6779
6780 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6781     
6782     html: false,
6783     cls: false,
6784     tag: false,
6785     abbr: false,
6786     align: false,
6787     axis: false,
6788     bgcolor: false,
6789     charoff: false,
6790     colspan: false,
6791     headers: false,
6792     height: false,
6793     nowrap: false,
6794     rowspan: false,
6795     scope: false,
6796     valign: false,
6797     width: false,
6798     
6799     
6800     getAutoCreate : function(){
6801         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6802         
6803         cfg = {
6804             tag: 'td'
6805         };
6806         
6807         if(this.tag){
6808             cfg.tag = this.tag;
6809         }
6810         
6811         if (this.html) {
6812             cfg.html=this.html
6813         }
6814         if (this.cls) {
6815             cfg.cls=this.cls
6816         }
6817         if (this.abbr) {
6818             cfg.abbr=this.abbr
6819         }
6820         if (this.align) {
6821             cfg.align=this.align
6822         }
6823         if (this.axis) {
6824             cfg.axis=this.axis
6825         }
6826         if (this.bgcolor) {
6827             cfg.bgcolor=this.bgcolor
6828         }
6829         if (this.charoff) {
6830             cfg.charoff=this.charoff
6831         }
6832         if (this.colspan) {
6833             cfg.colspan=this.colspan
6834         }
6835         if (this.headers) {
6836             cfg.headers=this.headers
6837         }
6838         if (this.height) {
6839             cfg.height=this.height
6840         }
6841         if (this.nowrap) {
6842             cfg.nowrap=this.nowrap
6843         }
6844         if (this.rowspan) {
6845             cfg.rowspan=this.rowspan
6846         }
6847         if (this.scope) {
6848             cfg.scope=this.scope
6849         }
6850         if (this.valign) {
6851             cfg.valign=this.valign
6852         }
6853         if (this.width) {
6854             cfg.width=this.width
6855         }
6856         
6857         
6858         return cfg;
6859     }
6860    
6861 });
6862
6863  
6864
6865  /*
6866  * - LGPL
6867  *
6868  * table row
6869  * 
6870  */
6871
6872 /**
6873  * @class Roo.bootstrap.TableRow
6874  * @extends Roo.bootstrap.Component
6875  * Bootstrap TableRow class
6876  * @cfg {String} cls row class
6877  * @cfg {String} align Aligns the content in a table row
6878  * @cfg {String} bgcolor Specifies a background color for a table row
6879  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6880  * @cfg {String} valign Vertical aligns the content in a table row
6881  * 
6882  * @constructor
6883  * Create a new TableRow
6884  * @param {Object} config The config object
6885  */
6886
6887 Roo.bootstrap.TableRow = function(config){
6888     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6889 };
6890
6891 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6892     
6893     cls: false,
6894     align: false,
6895     bgcolor: false,
6896     charoff: false,
6897     valign: false,
6898     
6899     getAutoCreate : function(){
6900         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6901         
6902         cfg = {
6903             tag: 'tr'
6904         };
6905             
6906         if(this.cls){
6907             cfg.cls = this.cls;
6908         }
6909         if(this.align){
6910             cfg.align = this.align;
6911         }
6912         if(this.bgcolor){
6913             cfg.bgcolor = this.bgcolor;
6914         }
6915         if(this.charoff){
6916             cfg.charoff = this.charoff;
6917         }
6918         if(this.valign){
6919             cfg.valign = this.valign;
6920         }
6921         
6922         return cfg;
6923     }
6924    
6925 });
6926
6927  
6928
6929  /*
6930  * - LGPL
6931  *
6932  * table body
6933  * 
6934  */
6935
6936 /**
6937  * @class Roo.bootstrap.TableBody
6938  * @extends Roo.bootstrap.Component
6939  * Bootstrap TableBody class
6940  * @cfg {String} cls element class
6941  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6942  * @cfg {String} align Aligns the content inside the element
6943  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6944  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6945  * 
6946  * @constructor
6947  * Create a new TableBody
6948  * @param {Object} config The config object
6949  */
6950
6951 Roo.bootstrap.TableBody = function(config){
6952     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6953 };
6954
6955 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6956     
6957     cls: false,
6958     tag: false,
6959     align: false,
6960     charoff: false,
6961     valign: false,
6962     
6963     getAutoCreate : function(){
6964         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6965         
6966         cfg = {
6967             tag: 'tbody'
6968         };
6969             
6970         if (this.cls) {
6971             cfg.cls=this.cls
6972         }
6973         if(this.tag){
6974             cfg.tag = this.tag;
6975         }
6976         
6977         if(this.align){
6978             cfg.align = this.align;
6979         }
6980         if(this.charoff){
6981             cfg.charoff = this.charoff;
6982         }
6983         if(this.valign){
6984             cfg.valign = this.valign;
6985         }
6986         
6987         return cfg;
6988     }
6989     
6990     
6991 //    initEvents : function()
6992 //    {
6993 //        
6994 //        if(!this.store){
6995 //            return;
6996 //        }
6997 //        
6998 //        this.store = Roo.factory(this.store, Roo.data);
6999 //        this.store.on('load', this.onLoad, this);
7000 //        
7001 //        this.store.load();
7002 //        
7003 //    },
7004 //    
7005 //    onLoad: function () 
7006 //    {   
7007 //        this.fireEvent('load', this);
7008 //    }
7009 //    
7010 //   
7011 });
7012
7013  
7014
7015  /*
7016  * Based on:
7017  * Ext JS Library 1.1.1
7018  * Copyright(c) 2006-2007, Ext JS, LLC.
7019  *
7020  * Originally Released Under LGPL - original licence link has changed is not relivant.
7021  *
7022  * Fork - LGPL
7023  * <script type="text/javascript">
7024  */
7025
7026 // as we use this in bootstrap.
7027 Roo.namespace('Roo.form');
7028  /**
7029  * @class Roo.form.Action
7030  * Internal Class used to handle form actions
7031  * @constructor
7032  * @param {Roo.form.BasicForm} el The form element or its id
7033  * @param {Object} config Configuration options
7034  */
7035
7036  
7037  
7038 // define the action interface
7039 Roo.form.Action = function(form, options){
7040     this.form = form;
7041     this.options = options || {};
7042 };
7043 /**
7044  * Client Validation Failed
7045  * @const 
7046  */
7047 Roo.form.Action.CLIENT_INVALID = 'client';
7048 /**
7049  * Server Validation Failed
7050  * @const 
7051  */
7052 Roo.form.Action.SERVER_INVALID = 'server';
7053  /**
7054  * Connect to Server Failed
7055  * @const 
7056  */
7057 Roo.form.Action.CONNECT_FAILURE = 'connect';
7058 /**
7059  * Reading Data from Server Failed
7060  * @const 
7061  */
7062 Roo.form.Action.LOAD_FAILURE = 'load';
7063
7064 Roo.form.Action.prototype = {
7065     type : 'default',
7066     failureType : undefined,
7067     response : undefined,
7068     result : undefined,
7069
7070     // interface method
7071     run : function(options){
7072
7073     },
7074
7075     // interface method
7076     success : function(response){
7077
7078     },
7079
7080     // interface method
7081     handleResponse : function(response){
7082
7083     },
7084
7085     // default connection failure
7086     failure : function(response){
7087         
7088         this.response = response;
7089         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7090         this.form.afterAction(this, false);
7091     },
7092
7093     processResponse : function(response){
7094         this.response = response;
7095         if(!response.responseText){
7096             return true;
7097         }
7098         this.result = this.handleResponse(response);
7099         return this.result;
7100     },
7101
7102     // utility functions used internally
7103     getUrl : function(appendParams){
7104         var url = this.options.url || this.form.url || this.form.el.dom.action;
7105         if(appendParams){
7106             var p = this.getParams();
7107             if(p){
7108                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7109             }
7110         }
7111         return url;
7112     },
7113
7114     getMethod : function(){
7115         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7116     },
7117
7118     getParams : function(){
7119         var bp = this.form.baseParams;
7120         var p = this.options.params;
7121         if(p){
7122             if(typeof p == "object"){
7123                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7124             }else if(typeof p == 'string' && bp){
7125                 p += '&' + Roo.urlEncode(bp);
7126             }
7127         }else if(bp){
7128             p = Roo.urlEncode(bp);
7129         }
7130         return p;
7131     },
7132
7133     createCallback : function(){
7134         return {
7135             success: this.success,
7136             failure: this.failure,
7137             scope: this,
7138             timeout: (this.form.timeout*1000),
7139             upload: this.form.fileUpload ? this.success : undefined
7140         };
7141     }
7142 };
7143
7144 Roo.form.Action.Submit = function(form, options){
7145     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7146 };
7147
7148 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7149     type : 'submit',
7150
7151     haveProgress : false,
7152     uploadComplete : false,
7153     
7154     // uploadProgress indicator.
7155     uploadProgress : function()
7156     {
7157         if (!this.form.progressUrl) {
7158             return;
7159         }
7160         
7161         if (!this.haveProgress) {
7162             Roo.MessageBox.progress("Uploading", "Uploading");
7163         }
7164         if (this.uploadComplete) {
7165            Roo.MessageBox.hide();
7166            return;
7167         }
7168         
7169         this.haveProgress = true;
7170    
7171         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7172         
7173         var c = new Roo.data.Connection();
7174         c.request({
7175             url : this.form.progressUrl,
7176             params: {
7177                 id : uid
7178             },
7179             method: 'GET',
7180             success : function(req){
7181                //console.log(data);
7182                 var rdata = false;
7183                 var edata;
7184                 try  {
7185                    rdata = Roo.decode(req.responseText)
7186                 } catch (e) {
7187                     Roo.log("Invalid data from server..");
7188                     Roo.log(edata);
7189                     return;
7190                 }
7191                 if (!rdata || !rdata.success) {
7192                     Roo.log(rdata);
7193                     Roo.MessageBox.alert(Roo.encode(rdata));
7194                     return;
7195                 }
7196                 var data = rdata.data;
7197                 
7198                 if (this.uploadComplete) {
7199                    Roo.MessageBox.hide();
7200                    return;
7201                 }
7202                    
7203                 if (data){
7204                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7205                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7206                     );
7207                 }
7208                 this.uploadProgress.defer(2000,this);
7209             },
7210        
7211             failure: function(data) {
7212                 Roo.log('progress url failed ');
7213                 Roo.log(data);
7214             },
7215             scope : this
7216         });
7217            
7218     },
7219     
7220     
7221     run : function()
7222     {
7223         // run get Values on the form, so it syncs any secondary forms.
7224         this.form.getValues();
7225         
7226         var o = this.options;
7227         var method = this.getMethod();
7228         var isPost = method == 'POST';
7229         if(o.clientValidation === false || this.form.isValid()){
7230             
7231             if (this.form.progressUrl) {
7232                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7233                     (new Date() * 1) + '' + Math.random());
7234                     
7235             } 
7236             
7237             
7238             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7239                 form:this.form.el.dom,
7240                 url:this.getUrl(!isPost),
7241                 method: method,
7242                 params:isPost ? this.getParams() : null,
7243                 isUpload: this.form.fileUpload
7244             }));
7245             
7246             this.uploadProgress();
7247
7248         }else if (o.clientValidation !== false){ // client validation failed
7249             this.failureType = Roo.form.Action.CLIENT_INVALID;
7250             this.form.afterAction(this, false);
7251         }
7252     },
7253
7254     success : function(response)
7255     {
7256         this.uploadComplete= true;
7257         if (this.haveProgress) {
7258             Roo.MessageBox.hide();
7259         }
7260         
7261         
7262         var result = this.processResponse(response);
7263         if(result === true || result.success){
7264             this.form.afterAction(this, true);
7265             return;
7266         }
7267         if(result.errors){
7268             this.form.markInvalid(result.errors);
7269             this.failureType = Roo.form.Action.SERVER_INVALID;
7270         }
7271         this.form.afterAction(this, false);
7272     },
7273     failure : function(response)
7274     {
7275         this.uploadComplete= true;
7276         if (this.haveProgress) {
7277             Roo.MessageBox.hide();
7278         }
7279         
7280         this.response = response;
7281         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7282         this.form.afterAction(this, false);
7283     },
7284     
7285     handleResponse : function(response){
7286         if(this.form.errorReader){
7287             var rs = this.form.errorReader.read(response);
7288             var errors = [];
7289             if(rs.records){
7290                 for(var i = 0, len = rs.records.length; i < len; i++) {
7291                     var r = rs.records[i];
7292                     errors[i] = r.data;
7293                 }
7294             }
7295             if(errors.length < 1){
7296                 errors = null;
7297             }
7298             return {
7299                 success : rs.success,
7300                 errors : errors
7301             };
7302         }
7303         var ret = false;
7304         try {
7305             ret = Roo.decode(response.responseText);
7306         } catch (e) {
7307             ret = {
7308                 success: false,
7309                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7310                 errors : []
7311             };
7312         }
7313         return ret;
7314         
7315     }
7316 });
7317
7318
7319 Roo.form.Action.Load = function(form, options){
7320     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7321     this.reader = this.form.reader;
7322 };
7323
7324 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7325     type : 'load',
7326
7327     run : function(){
7328         
7329         Roo.Ajax.request(Roo.apply(
7330                 this.createCallback(), {
7331                     method:this.getMethod(),
7332                     url:this.getUrl(false),
7333                     params:this.getParams()
7334         }));
7335     },
7336
7337     success : function(response){
7338         
7339         var result = this.processResponse(response);
7340         if(result === true || !result.success || !result.data){
7341             this.failureType = Roo.form.Action.LOAD_FAILURE;
7342             this.form.afterAction(this, false);
7343             return;
7344         }
7345         this.form.clearInvalid();
7346         this.form.setValues(result.data);
7347         this.form.afterAction(this, true);
7348     },
7349
7350     handleResponse : function(response){
7351         if(this.form.reader){
7352             var rs = this.form.reader.read(response);
7353             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7354             return {
7355                 success : rs.success,
7356                 data : data
7357             };
7358         }
7359         return Roo.decode(response.responseText);
7360     }
7361 });
7362
7363 Roo.form.Action.ACTION_TYPES = {
7364     'load' : Roo.form.Action.Load,
7365     'submit' : Roo.form.Action.Submit
7366 };/*
7367  * - LGPL
7368  *
7369  * form
7370  * 
7371  */
7372
7373 /**
7374  * @class Roo.bootstrap.Form
7375  * @extends Roo.bootstrap.Component
7376  * Bootstrap Form class
7377  * @cfg {String} method  GET | POST (default POST)
7378  * @cfg {String} labelAlign top | left (default top)
7379  * @cfg {String} align left  | right - for navbars
7380  * @cfg {Boolean} loadMask load mask when submit (default true)
7381
7382  * 
7383  * @constructor
7384  * Create a new Form
7385  * @param {Object} config The config object
7386  */
7387
7388
7389 Roo.bootstrap.Form = function(config){
7390     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7391     this.addEvents({
7392         /**
7393          * @event clientvalidation
7394          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7395          * @param {Form} this
7396          * @param {Boolean} valid true if the form has passed client-side validation
7397          */
7398         clientvalidation: true,
7399         /**
7400          * @event beforeaction
7401          * Fires before any action is performed. Return false to cancel the action.
7402          * @param {Form} this
7403          * @param {Action} action The action to be performed
7404          */
7405         beforeaction: true,
7406         /**
7407          * @event actionfailed
7408          * Fires when an action fails.
7409          * @param {Form} this
7410          * @param {Action} action The action that failed
7411          */
7412         actionfailed : true,
7413         /**
7414          * @event actioncomplete
7415          * Fires when an action is completed.
7416          * @param {Form} this
7417          * @param {Action} action The action that completed
7418          */
7419         actioncomplete : true
7420     });
7421     
7422 };
7423
7424 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7425       
7426      /**
7427      * @cfg {String} method
7428      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7429      */
7430     method : 'POST',
7431     /**
7432      * @cfg {String} url
7433      * The URL to use for form actions if one isn't supplied in the action options.
7434      */
7435     /**
7436      * @cfg {Boolean} fileUpload
7437      * Set to true if this form is a file upload.
7438      */
7439      
7440     /**
7441      * @cfg {Object} baseParams
7442      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7443      */
7444       
7445     /**
7446      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7447      */
7448     timeout: 30,
7449     /**
7450      * @cfg {Sting} align (left|right) for navbar forms
7451      */
7452     align : 'left',
7453
7454     // private
7455     activeAction : null,
7456  
7457     /**
7458      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7459      * element by passing it or its id or mask the form itself by passing in true.
7460      * @type Mixed
7461      */
7462     waitMsgTarget : false,
7463     
7464     loadMask : true,
7465     
7466     getAutoCreate : function(){
7467         
7468         var cfg = {
7469             tag: 'form',
7470             method : this.method || 'POST',
7471             id : this.id || Roo.id(),
7472             cls : ''
7473         };
7474         if (this.parent().xtype.match(/^Nav/)) {
7475             cfg.cls = 'navbar-form navbar-' + this.align;
7476             
7477         }
7478         
7479         if (this.labelAlign == 'left' ) {
7480             cfg.cls += ' form-horizontal';
7481         }
7482         
7483         
7484         return cfg;
7485     },
7486     initEvents : function()
7487     {
7488         this.el.on('submit', this.onSubmit, this);
7489         // this was added as random key presses on the form where triggering form submit.
7490         this.el.on('keypress', function(e) {
7491             if (e.getCharCode() != 13) {
7492                 return true;
7493             }
7494             // we might need to allow it for textareas.. and some other items.
7495             // check e.getTarget().
7496             
7497             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7498                 return true;
7499             }
7500         
7501             Roo.log("keypress blocked");
7502             
7503             e.preventDefault();
7504             return false;
7505         });
7506         
7507     },
7508     // private
7509     onSubmit : function(e){
7510         e.stopEvent();
7511     },
7512     
7513      /**
7514      * Returns true if client-side validation on the form is successful.
7515      * @return Boolean
7516      */
7517     isValid : function(){
7518         var items = this.getItems();
7519         var valid = true;
7520         items.each(function(f){
7521            if(!f.validate()){
7522                valid = false;
7523                
7524            }
7525         });
7526         return valid;
7527     },
7528     /**
7529      * Returns true if any fields in this form have changed since their original load.
7530      * @return Boolean
7531      */
7532     isDirty : function(){
7533         var dirty = false;
7534         var items = this.getItems();
7535         items.each(function(f){
7536            if(f.isDirty()){
7537                dirty = true;
7538                return false;
7539            }
7540            return true;
7541         });
7542         return dirty;
7543     },
7544      /**
7545      * Performs a predefined action (submit or load) or custom actions you define on this form.
7546      * @param {String} actionName The name of the action type
7547      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7548      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7549      * accept other config options):
7550      * <pre>
7551 Property          Type             Description
7552 ----------------  ---------------  ----------------------------------------------------------------------------------
7553 url               String           The url for the action (defaults to the form's url)
7554 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7555 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7556 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7557                                    validate the form on the client (defaults to false)
7558      * </pre>
7559      * @return {BasicForm} this
7560      */
7561     doAction : function(action, options){
7562         if(typeof action == 'string'){
7563             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7564         }
7565         if(this.fireEvent('beforeaction', this, action) !== false){
7566             this.beforeAction(action);
7567             action.run.defer(100, action);
7568         }
7569         return this;
7570     },
7571     
7572     // private
7573     beforeAction : function(action){
7574         var o = action.options;
7575         
7576         if(this.loadMask){
7577             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7578         }
7579         // not really supported yet.. ??
7580         
7581         //if(this.waitMsgTarget === true){
7582         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7583         //}else if(this.waitMsgTarget){
7584         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7585         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7586         //}else {
7587         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7588        // }
7589          
7590     },
7591
7592     // private
7593     afterAction : function(action, success){
7594         this.activeAction = null;
7595         var o = action.options;
7596         
7597         //if(this.waitMsgTarget === true){
7598             this.el.unmask();
7599         //}else if(this.waitMsgTarget){
7600         //    this.waitMsgTarget.unmask();
7601         //}else{
7602         //    Roo.MessageBox.updateProgress(1);
7603         //    Roo.MessageBox.hide();
7604        // }
7605         // 
7606         if(success){
7607             if(o.reset){
7608                 this.reset();
7609             }
7610             Roo.callback(o.success, o.scope, [this, action]);
7611             this.fireEvent('actioncomplete', this, action);
7612             
7613         }else{
7614             
7615             // failure condition..
7616             // we have a scenario where updates need confirming.
7617             // eg. if a locking scenario exists..
7618             // we look for { errors : { needs_confirm : true }} in the response.
7619             if (
7620                 (typeof(action.result) != 'undefined')  &&
7621                 (typeof(action.result.errors) != 'undefined')  &&
7622                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7623            ){
7624                 var _t = this;
7625                 Roo.log("not supported yet");
7626                  /*
7627                 
7628                 Roo.MessageBox.confirm(
7629                     "Change requires confirmation",
7630                     action.result.errorMsg,
7631                     function(r) {
7632                         if (r != 'yes') {
7633                             return;
7634                         }
7635                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7636                     }
7637                     
7638                 );
7639                 */
7640                 
7641                 
7642                 return;
7643             }
7644             
7645             Roo.callback(o.failure, o.scope, [this, action]);
7646             // show an error message if no failed handler is set..
7647             if (!this.hasListener('actionfailed')) {
7648                 Roo.log("need to add dialog support");
7649                 /*
7650                 Roo.MessageBox.alert("Error",
7651                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7652                         action.result.errorMsg :
7653                         "Saving Failed, please check your entries or try again"
7654                 );
7655                 */
7656             }
7657             
7658             this.fireEvent('actionfailed', this, action);
7659         }
7660         
7661     },
7662     /**
7663      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7664      * @param {String} id The value to search for
7665      * @return Field
7666      */
7667     findField : function(id){
7668         var items = this.getItems();
7669         var field = items.get(id);
7670         if(!field){
7671              items.each(function(f){
7672                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7673                     field = f;
7674                     return false;
7675                 }
7676                 return true;
7677             });
7678         }
7679         return field || null;
7680     },
7681      /**
7682      * Mark fields in this form invalid in bulk.
7683      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7684      * @return {BasicForm} this
7685      */
7686     markInvalid : function(errors){
7687         if(errors instanceof Array){
7688             for(var i = 0, len = errors.length; i < len; i++){
7689                 var fieldError = errors[i];
7690                 var f = this.findField(fieldError.id);
7691                 if(f){
7692                     f.markInvalid(fieldError.msg);
7693                 }
7694             }
7695         }else{
7696             var field, id;
7697             for(id in errors){
7698                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7699                     field.markInvalid(errors[id]);
7700                 }
7701             }
7702         }
7703         //Roo.each(this.childForms || [], function (f) {
7704         //    f.markInvalid(errors);
7705         //});
7706         
7707         return this;
7708     },
7709
7710     /**
7711      * Set values for fields in this form in bulk.
7712      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7713      * @return {BasicForm} this
7714      */
7715     setValues : function(values){
7716         if(values instanceof Array){ // array of objects
7717             for(var i = 0, len = values.length; i < len; i++){
7718                 var v = values[i];
7719                 var f = this.findField(v.id);
7720                 if(f){
7721                     f.setValue(v.value);
7722                     if(this.trackResetOnLoad){
7723                         f.originalValue = f.getValue();
7724                     }
7725                 }
7726             }
7727         }else{ // object hash
7728             var field, id;
7729             for(id in values){
7730                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7731                     
7732                     if (field.setFromData && 
7733                         field.valueField && 
7734                         field.displayField &&
7735                         // combos' with local stores can 
7736                         // be queried via setValue()
7737                         // to set their value..
7738                         (field.store && !field.store.isLocal)
7739                         ) {
7740                         // it's a combo
7741                         var sd = { };
7742                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7743                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7744                         field.setFromData(sd);
7745                         
7746                     } else {
7747                         field.setValue(values[id]);
7748                     }
7749                     
7750                     
7751                     if(this.trackResetOnLoad){
7752                         field.originalValue = field.getValue();
7753                     }
7754                 }
7755             }
7756         }
7757          
7758         //Roo.each(this.childForms || [], function (f) {
7759         //    f.setValues(values);
7760         //});
7761                 
7762         return this;
7763     },
7764
7765     /**
7766      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7767      * they are returned as an array.
7768      * @param {Boolean} asString
7769      * @return {Object}
7770      */
7771     getValues : function(asString){
7772         //if (this.childForms) {
7773             // copy values from the child forms
7774         //    Roo.each(this.childForms, function (f) {
7775         //        this.setValues(f.getValues());
7776         //    }, this);
7777         //}
7778         
7779         
7780         
7781         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7782         if(asString === true){
7783             return fs;
7784         }
7785         return Roo.urlDecode(fs);
7786     },
7787     
7788     /**
7789      * Returns the fields in this form as an object with key/value pairs. 
7790      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7791      * @return {Object}
7792      */
7793     getFieldValues : function(with_hidden)
7794     {
7795         var items = this.getItems();
7796         var ret = {};
7797         items.each(function(f){
7798             if (!f.getName()) {
7799                 return;
7800             }
7801             var v = f.getValue();
7802             if (f.inputType =='radio') {
7803                 if (typeof(ret[f.getName()]) == 'undefined') {
7804                     ret[f.getName()] = ''; // empty..
7805                 }
7806                 
7807                 if (!f.el.dom.checked) {
7808                     return;
7809                     
7810                 }
7811                 v = f.el.dom.value;
7812                 
7813             }
7814             
7815             // not sure if this supported any more..
7816             if ((typeof(v) == 'object') && f.getRawValue) {
7817                 v = f.getRawValue() ; // dates..
7818             }
7819             // combo boxes where name != hiddenName...
7820             if (f.name != f.getName()) {
7821                 ret[f.name] = f.getRawValue();
7822             }
7823             ret[f.getName()] = v;
7824         });
7825         
7826         return ret;
7827     },
7828
7829     /**
7830      * Clears all invalid messages in this form.
7831      * @return {BasicForm} this
7832      */
7833     clearInvalid : function(){
7834         var items = this.getItems();
7835         
7836         items.each(function(f){
7837            f.clearInvalid();
7838         });
7839         
7840         
7841         
7842         return this;
7843     },
7844
7845     /**
7846      * Resets this form.
7847      * @return {BasicForm} this
7848      */
7849     reset : function(){
7850         var items = this.getItems();
7851         items.each(function(f){
7852             f.reset();
7853         });
7854         
7855         Roo.each(this.childForms || [], function (f) {
7856             f.reset();
7857         });
7858        
7859         
7860         return this;
7861     },
7862     getItems : function()
7863     {
7864         var r=new Roo.util.MixedCollection(false, function(o){
7865             return o.id || (o.id = Roo.id());
7866         });
7867         var iter = function(el) {
7868             if (el.inputEl) {
7869                 r.add(el);
7870             }
7871             if (!el.items) {
7872                 return;
7873             }
7874             Roo.each(el.items,function(e) {
7875                 iter(e);
7876             });
7877             
7878             
7879         };
7880         
7881         iter(this);
7882         return r;
7883         
7884         
7885         
7886         
7887     }
7888     
7889 });
7890
7891  
7892 /*
7893  * Based on:
7894  * Ext JS Library 1.1.1
7895  * Copyright(c) 2006-2007, Ext JS, LLC.
7896  *
7897  * Originally Released Under LGPL - original licence link has changed is not relivant.
7898  *
7899  * Fork - LGPL
7900  * <script type="text/javascript">
7901  */
7902 /**
7903  * @class Roo.form.VTypes
7904  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7905  * @singleton
7906  */
7907 Roo.form.VTypes = function(){
7908     // closure these in so they are only created once.
7909     var alpha = /^[a-zA-Z_]+$/;
7910     var alphanum = /^[a-zA-Z0-9_]+$/;
7911     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7912     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7913
7914     // All these messages and functions are configurable
7915     return {
7916         /**
7917          * The function used to validate email addresses
7918          * @param {String} value The email address
7919          */
7920         'email' : function(v){
7921             return email.test(v);
7922         },
7923         /**
7924          * The error text to display when the email validation function returns false
7925          * @type String
7926          */
7927         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7928         /**
7929          * The keystroke filter mask to be applied on email input
7930          * @type RegExp
7931          */
7932         'emailMask' : /[a-z0-9_\.\-@]/i,
7933
7934         /**
7935          * The function used to validate URLs
7936          * @param {String} value The URL
7937          */
7938         'url' : function(v){
7939             return url.test(v);
7940         },
7941         /**
7942          * The error text to display when the url validation function returns false
7943          * @type String
7944          */
7945         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7946         
7947         /**
7948          * The function used to validate alpha values
7949          * @param {String} value The value
7950          */
7951         'alpha' : function(v){
7952             return alpha.test(v);
7953         },
7954         /**
7955          * The error text to display when the alpha validation function returns false
7956          * @type String
7957          */
7958         'alphaText' : 'This field should only contain letters and _',
7959         /**
7960          * The keystroke filter mask to be applied on alpha input
7961          * @type RegExp
7962          */
7963         'alphaMask' : /[a-z_]/i,
7964
7965         /**
7966          * The function used to validate alphanumeric values
7967          * @param {String} value The value
7968          */
7969         'alphanum' : function(v){
7970             return alphanum.test(v);
7971         },
7972         /**
7973          * The error text to display when the alphanumeric validation function returns false
7974          * @type String
7975          */
7976         'alphanumText' : 'This field should only contain letters, numbers and _',
7977         /**
7978          * The keystroke filter mask to be applied on alphanumeric input
7979          * @type RegExp
7980          */
7981         'alphanumMask' : /[a-z0-9_]/i
7982     };
7983 }();/*
7984  * - LGPL
7985  *
7986  * Input
7987  * 
7988  */
7989
7990 /**
7991  * @class Roo.bootstrap.Input
7992  * @extends Roo.bootstrap.Component
7993  * Bootstrap Input class
7994  * @cfg {Boolean} disabled is it disabled
7995  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7996  * @cfg {String} name name of the input
7997  * @cfg {string} fieldLabel - the label associated
7998  * @cfg {string} placeholder - placeholder to put in text.
7999  * @cfg {string}  before - input group add on before
8000  * @cfg {string} after - input group add on after
8001  * @cfg {string} size - (lg|sm) or leave empty..
8002  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8003  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8004  * @cfg {Number} md colspan out of 12 for computer-sized screens
8005  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8006  * @cfg {string} value default value of the input
8007  * @cfg {Number} labelWidth set the width of label (0-12)
8008  * @cfg {String} labelAlign (top|left)
8009  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8010  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8011
8012  * @cfg {String} align (left|center|right) Default left
8013  * @cfg {Boolean} forceFeedback (true|false) Default false
8014  * 
8015  * 
8016  * 
8017  * 
8018  * @constructor
8019  * Create a new Input
8020  * @param {Object} config The config object
8021  */
8022
8023 Roo.bootstrap.Input = function(config){
8024     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8025    
8026         this.addEvents({
8027             /**
8028              * @event focus
8029              * Fires when this field receives input focus.
8030              * @param {Roo.form.Field} this
8031              */
8032             focus : true,
8033             /**
8034              * @event blur
8035              * Fires when this field loses input focus.
8036              * @param {Roo.form.Field} this
8037              */
8038             blur : true,
8039             /**
8040              * @event specialkey
8041              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8042              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8043              * @param {Roo.form.Field} this
8044              * @param {Roo.EventObject} e The event object
8045              */
8046             specialkey : true,
8047             /**
8048              * @event change
8049              * Fires just before the field blurs if the field value has changed.
8050              * @param {Roo.form.Field} this
8051              * @param {Mixed} newValue The new value
8052              * @param {Mixed} oldValue The original value
8053              */
8054             change : true,
8055             /**
8056              * @event invalid
8057              * Fires after the field has been marked as invalid.
8058              * @param {Roo.form.Field} this
8059              * @param {String} msg The validation message
8060              */
8061             invalid : true,
8062             /**
8063              * @event valid
8064              * Fires after the field has been validated with no errors.
8065              * @param {Roo.form.Field} this
8066              */
8067             valid : true,
8068              /**
8069              * @event keyup
8070              * Fires after the key up
8071              * @param {Roo.form.Field} this
8072              * @param {Roo.EventObject}  e The event Object
8073              */
8074             keyup : true
8075         });
8076 };
8077
8078 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8079      /**
8080      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8081       automatic validation (defaults to "keyup").
8082      */
8083     validationEvent : "keyup",
8084      /**
8085      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8086      */
8087     validateOnBlur : true,
8088     /**
8089      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8090      */
8091     validationDelay : 250,
8092      /**
8093      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8094      */
8095     focusClass : "x-form-focus",  // not needed???
8096     
8097        
8098     /**
8099      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8100      */
8101     invalidClass : "has-warning",
8102     
8103     /**
8104      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8105      */
8106     validClass : "has-success",
8107     
8108     /**
8109      * @cfg {Boolean} hasFeedback (true|false) default true
8110      */
8111     hasFeedback : true,
8112     
8113     /**
8114      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8115      */
8116     invalidFeedbackClass : "glyphicon-warning-sign",
8117     
8118     /**
8119      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8120      */
8121     validFeedbackClass : "glyphicon-ok",
8122     
8123     /**
8124      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8125      */
8126     selectOnFocus : false,
8127     
8128      /**
8129      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8130      */
8131     maskRe : null,
8132        /**
8133      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8134      */
8135     vtype : null,
8136     
8137       /**
8138      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8139      */
8140     disableKeyFilter : false,
8141     
8142        /**
8143      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8144      */
8145     disabled : false,
8146      /**
8147      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8148      */
8149     allowBlank : true,
8150     /**
8151      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8152      */
8153     blankText : "This field is required",
8154     
8155      /**
8156      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8157      */
8158     minLength : 0,
8159     /**
8160      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8161      */
8162     maxLength : Number.MAX_VALUE,
8163     /**
8164      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8165      */
8166     minLengthText : "The minimum length for this field is {0}",
8167     /**
8168      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8169      */
8170     maxLengthText : "The maximum length for this field is {0}",
8171   
8172     
8173     /**
8174      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8175      * If available, this function will be called only after the basic validators all return true, and will be passed the
8176      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8177      */
8178     validator : null,
8179     /**
8180      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8181      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8182      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8183      */
8184     regex : null,
8185     /**
8186      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8187      */
8188     regexText : "",
8189     
8190     autocomplete: false,
8191     
8192     
8193     fieldLabel : '',
8194     inputType : 'text',
8195     
8196     name : false,
8197     placeholder: false,
8198     before : false,
8199     after : false,
8200     size : false,
8201     hasFocus : false,
8202     preventMark: false,
8203     isFormField : true,
8204     value : '',
8205     labelWidth : 2,
8206     labelAlign : false,
8207     readOnly : false,
8208     align : false,
8209     formatedValue : false,
8210     forceFeedback : false,
8211     
8212     parentLabelAlign : function()
8213     {
8214         var parent = this;
8215         while (parent.parent()) {
8216             parent = parent.parent();
8217             if (typeof(parent.labelAlign) !='undefined') {
8218                 return parent.labelAlign;
8219             }
8220         }
8221         return 'left';
8222         
8223     },
8224     
8225     getAutoCreate : function(){
8226         
8227         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8228         
8229         var id = Roo.id();
8230         
8231         var cfg = {};
8232         
8233        
8234         
8235         if(this.inputType != 'hidden'){
8236             cfg.cls = 'form-group' //input-group
8237         }
8238         
8239         var input =  {
8240             tag: 'input',
8241             id : id,
8242             type : this.inputType,
8243             value : this.value,
8244             cls : 'form-control',
8245             placeholder : this.placeholder || '',
8246             autocomplete : this.autocomplete || 'new-password'
8247         };
8248         
8249         
8250         if(this.align){
8251             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8252         }
8253         
8254         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8255             input.maxLength = this.maxLength;
8256         }
8257         
8258         if (this.disabled) {
8259             input.disabled=true;
8260         }
8261         
8262         if (this.readOnly) {
8263             input.readonly=true;
8264         }
8265         
8266         if (this.name) {
8267             input.name = this.name;
8268         }
8269         if (this.size) {
8270             input.cls += ' input-' + this.size;
8271         }
8272         var settings=this;
8273         ['xs','sm','md','lg'].map(function(size){
8274             if (settings[size]) {
8275                 cfg.cls += ' col-' + size + '-' + settings[size];
8276             }
8277         });
8278         
8279         var inputblock = input;
8280         
8281         var feedback = {
8282             tag: 'span',
8283             cls: 'glyphicon form-control-feedback'
8284         };
8285             
8286         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8287             
8288             inputblock = {
8289                 cls : 'has-feedback',
8290                 cn :  [
8291                     input,
8292                     feedback
8293                 ] 
8294             };  
8295         }
8296         
8297         if (this.before || this.after) {
8298             
8299             inputblock = {
8300                 cls : 'input-group',
8301                 cn :  [] 
8302             };
8303             
8304             if (this.before && typeof(this.before) == 'string') {
8305                 
8306                 inputblock.cn.push({
8307                     tag :'span',
8308                     cls : 'roo-input-before input-group-addon',
8309                     html : this.before
8310                 });
8311             }
8312             if (this.before && typeof(this.before) == 'object') {
8313                 this.before = Roo.factory(this.before);
8314                 
8315                 inputblock.cn.push({
8316                     tag :'span',
8317                     cls : 'roo-input-before input-group-' +
8318                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8319                 });
8320             }
8321             
8322             inputblock.cn.push(input);
8323             
8324             if (this.after && typeof(this.after) == 'string') {
8325                 inputblock.cn.push({
8326                     tag :'span',
8327                     cls : 'roo-input-after input-group-addon',
8328                     html : this.after
8329                 });
8330             }
8331             if (this.after && typeof(this.after) == 'object') {
8332                 this.after = Roo.factory(this.after);
8333                 
8334                 inputblock.cn.push({
8335                     tag :'span',
8336                     cls : 'roo-input-after input-group-' +
8337                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8338                 });
8339             }
8340             
8341             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8342                 inputblock.cls += ' has-feedback';
8343                 inputblock.cn.push(feedback);
8344             }
8345         };
8346         
8347         if (align ==='left' && this.fieldLabel.length) {
8348                 
8349                 cfg.cn = [
8350                     
8351                     {
8352                         tag: 'label',
8353                         'for' :  id,
8354                         cls : 'control-label col-sm-' + this.labelWidth,
8355                         html : this.fieldLabel
8356                         
8357                     },
8358                     {
8359                         cls : "col-sm-" + (12 - this.labelWidth), 
8360                         cn: [
8361                             inputblock
8362                         ]
8363                     }
8364                     
8365                 ];
8366         } else if ( this.fieldLabel.length) {
8367                 
8368                  cfg.cn = [
8369                    
8370                     {
8371                         tag: 'label',
8372                         //cls : 'input-group-addon',
8373                         html : this.fieldLabel
8374                         
8375                     },
8376                     
8377                     inputblock
8378                     
8379                 ];
8380
8381         } else {
8382             
8383                 cfg.cn = [
8384                     
8385                         inputblock
8386                     
8387                 ];
8388                 
8389                 
8390         };
8391         
8392         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8393            cfg.cls += ' navbar-form';
8394         }
8395         if (this.parentType === 'NavGroup') {
8396            cfg.cls += ' navbar-form';
8397            cfg.tag = 'li';
8398         }
8399         return cfg;
8400         
8401     },
8402     /**
8403      * return the real input element.
8404      */
8405     inputEl: function ()
8406     {
8407         return this.el.select('input.form-control',true).first();
8408     },
8409     
8410     tooltipEl : function()
8411     {
8412         return this.inputEl();
8413     },
8414     
8415     setDisabled : function(v)
8416     {
8417         var i  = this.inputEl().dom;
8418         if (!v) {
8419             i.removeAttribute('disabled');
8420             return;
8421             
8422         }
8423         i.setAttribute('disabled','true');
8424     },
8425     initEvents : function()
8426     {
8427           
8428         this.inputEl().on("keydown" , this.fireKey,  this);
8429         this.inputEl().on("focus", this.onFocus,  this);
8430         this.inputEl().on("blur", this.onBlur,  this);
8431         
8432         this.inputEl().relayEvent('keyup', this);
8433  
8434         // reference to original value for reset
8435         this.originalValue = this.getValue();
8436         //Roo.form.TextField.superclass.initEvents.call(this);
8437         if(this.validationEvent == 'keyup'){
8438             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8439             this.inputEl().on('keyup', this.filterValidation, this);
8440         }
8441         else if(this.validationEvent !== false){
8442             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8443         }
8444         
8445         if(this.selectOnFocus){
8446             this.on("focus", this.preFocus, this);
8447             
8448         }
8449         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8450             this.inputEl().on("keypress", this.filterKeys, this);
8451         }
8452        /* if(this.grow){
8453             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8454             this.el.on("click", this.autoSize,  this);
8455         }
8456         */
8457         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8458             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8459         }
8460         
8461         if (typeof(this.before) == 'object') {
8462             this.before.render(this.el.select('.roo-input-before',true).first());
8463         }
8464         if (typeof(this.after) == 'object') {
8465             this.after.render(this.el.select('.roo-input-after',true).first());
8466         }
8467         
8468         
8469     },
8470     filterValidation : function(e){
8471         if(!e.isNavKeyPress()){
8472             this.validationTask.delay(this.validationDelay);
8473         }
8474     },
8475      /**
8476      * Validates the field value
8477      * @return {Boolean} True if the value is valid, else false
8478      */
8479     validate : function(){
8480         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8481         if(this.disabled || this.validateValue(this.getRawValue())){
8482             this.markValid();
8483             return true;
8484         }
8485         
8486         this.markInvalid();
8487         return false;
8488     },
8489     
8490     
8491     /**
8492      * Validates a value according to the field's validation rules and marks the field as invalid
8493      * if the validation fails
8494      * @param {Mixed} value The value to validate
8495      * @return {Boolean} True if the value is valid, else false
8496      */
8497     validateValue : function(value){
8498         if(value.length < 1)  { // if it's blank
8499             if(this.allowBlank){
8500                 return true;
8501             }
8502             return false;
8503         }
8504         
8505         if(value.length < this.minLength){
8506             return false;
8507         }
8508         if(value.length > this.maxLength){
8509             return false;
8510         }
8511         if(this.vtype){
8512             var vt = Roo.form.VTypes;
8513             if(!vt[this.vtype](value, this)){
8514                 return false;
8515             }
8516         }
8517         if(typeof this.validator == "function"){
8518             var msg = this.validator(value);
8519             if(msg !== true){
8520                 return false;
8521             }
8522         }
8523         
8524         if(this.regex && !this.regex.test(value)){
8525             return false;
8526         }
8527         
8528         return true;
8529     },
8530
8531     
8532     
8533      // private
8534     fireKey : function(e){
8535         //Roo.log('field ' + e.getKey());
8536         if(e.isNavKeyPress()){
8537             this.fireEvent("specialkey", this, e);
8538         }
8539     },
8540     focus : function (selectText){
8541         if(this.rendered){
8542             this.inputEl().focus();
8543             if(selectText === true){
8544                 this.inputEl().dom.select();
8545             }
8546         }
8547         return this;
8548     } ,
8549     
8550     onFocus : function(){
8551         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8552            // this.el.addClass(this.focusClass);
8553         }
8554         if(!this.hasFocus){
8555             this.hasFocus = true;
8556             this.startValue = this.getValue();
8557             this.fireEvent("focus", this);
8558         }
8559     },
8560     
8561     beforeBlur : Roo.emptyFn,
8562
8563     
8564     // private
8565     onBlur : function(){
8566         this.beforeBlur();
8567         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8568             //this.el.removeClass(this.focusClass);
8569         }
8570         this.hasFocus = false;
8571         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8572             this.validate();
8573         }
8574         var v = this.getValue();
8575         if(String(v) !== String(this.startValue)){
8576             this.fireEvent('change', this, v, this.startValue);
8577         }
8578         this.fireEvent("blur", this);
8579     },
8580     
8581     /**
8582      * Resets the current field value to the originally loaded value and clears any validation messages
8583      */
8584     reset : function(){
8585         this.setValue(this.originalValue);
8586         this.validate();
8587     },
8588      /**
8589      * Returns the name of the field
8590      * @return {Mixed} name The name field
8591      */
8592     getName: function(){
8593         return this.name;
8594     },
8595      /**
8596      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8597      * @return {Mixed} value The field value
8598      */
8599     getValue : function(){
8600         
8601         var v = this.inputEl().getValue();
8602         
8603         return v;
8604     },
8605     /**
8606      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8607      * @return {Mixed} value The field value
8608      */
8609     getRawValue : function(){
8610         var v = this.inputEl().getValue();
8611         
8612         return v;
8613     },
8614     
8615     /**
8616      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8617      * @param {Mixed} value The value to set
8618      */
8619     setRawValue : function(v){
8620         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8621     },
8622     
8623     selectText : function(start, end){
8624         var v = this.getRawValue();
8625         if(v.length > 0){
8626             start = start === undefined ? 0 : start;
8627             end = end === undefined ? v.length : end;
8628             var d = this.inputEl().dom;
8629             if(d.setSelectionRange){
8630                 d.setSelectionRange(start, end);
8631             }else if(d.createTextRange){
8632                 var range = d.createTextRange();
8633                 range.moveStart("character", start);
8634                 range.moveEnd("character", v.length-end);
8635                 range.select();
8636             }
8637         }
8638     },
8639     
8640     /**
8641      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8642      * @param {Mixed} value The value to set
8643      */
8644     setValue : function(v){
8645         this.value = v;
8646         if(this.rendered){
8647             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8648             this.validate();
8649         }
8650     },
8651     
8652     /*
8653     processValue : function(value){
8654         if(this.stripCharsRe){
8655             var newValue = value.replace(this.stripCharsRe, '');
8656             if(newValue !== value){
8657                 this.setRawValue(newValue);
8658                 return newValue;
8659             }
8660         }
8661         return value;
8662     },
8663   */
8664     preFocus : function(){
8665         
8666         if(this.selectOnFocus){
8667             this.inputEl().dom.select();
8668         }
8669     },
8670     filterKeys : function(e){
8671         var k = e.getKey();
8672         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8673             return;
8674         }
8675         var c = e.getCharCode(), cc = String.fromCharCode(c);
8676         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8677             return;
8678         }
8679         if(!this.maskRe.test(cc)){
8680             e.stopEvent();
8681         }
8682     },
8683      /**
8684      * Clear any invalid styles/messages for this field
8685      */
8686     clearInvalid : function(){
8687         
8688         if(!this.el || this.preventMark){ // not rendered
8689             return;
8690         }
8691         
8692         var label = this.el.select('label', true).first();
8693         var icon = this.el.select('i.fa-star', true).first();
8694         
8695         if(label && icon){
8696             icon.remove();
8697         }
8698         
8699         this.el.removeClass(this.invalidClass);
8700         
8701         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8702             
8703             var feedback = this.el.select('.form-control-feedback', true).first();
8704             
8705             if(feedback){
8706                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8707             }
8708             
8709         }
8710         
8711         this.fireEvent('valid', this);
8712     },
8713     
8714      /**
8715      * Mark this field as valid
8716      */
8717     markValid : function()
8718     {
8719         if(!this.el  || this.preventMark){ // not rendered
8720             return;
8721         }
8722         
8723         this.el.removeClass([this.invalidClass, this.validClass]);
8724         
8725         var feedback = this.el.select('.form-control-feedback', true).first();
8726             
8727         if(feedback){
8728             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8729         }
8730
8731         if(this.disabled || this.allowBlank){
8732             return;
8733         }
8734         
8735         var formGroup = this.el.findParent('.form-group', false, true);
8736         
8737         if(formGroup){
8738             
8739             var label = formGroup.select('label', true).first();
8740             var icon = formGroup.select('i.fa-star', true).first();
8741             
8742             if(label && icon){
8743                 icon.remove();
8744             }
8745         }
8746         
8747         this.el.addClass(this.validClass);
8748         
8749         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8750             
8751             var feedback = this.el.select('.form-control-feedback', true).first();
8752             
8753             if(feedback){
8754                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8755                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8756             }
8757             
8758         }
8759         
8760         this.fireEvent('valid', this);
8761     },
8762     
8763      /**
8764      * Mark this field as invalid
8765      * @param {String} msg The validation message
8766      */
8767     markInvalid : function(msg)
8768     {
8769         if(!this.el  || this.preventMark){ // not rendered
8770             return;
8771         }
8772         
8773         this.el.removeClass([this.invalidClass, this.validClass]);
8774         
8775         var feedback = this.el.select('.form-control-feedback', true).first();
8776             
8777         if(feedback){
8778             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8779         }
8780
8781         if(this.disabled || this.allowBlank){
8782             return;
8783         }
8784         
8785         var formGroup = this.el.findParent('.form-group', false, true);
8786         
8787         if(formGroup){
8788             var label = formGroup.select('label', true).first();
8789             var icon = formGroup.select('i.fa-star', true).first();
8790
8791             if(!this.getValue().length && label && !icon){
8792                 this.el.findParent('.form-group', false, true).createChild({
8793                     tag : 'i',
8794                     cls : 'text-danger fa fa-lg fa-star',
8795                     tooltip : 'This field is required',
8796                     style : 'margin-right:5px;'
8797                 }, label, true);
8798             }
8799         }
8800         
8801         
8802         this.el.addClass(this.invalidClass);
8803         
8804         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8805             
8806             var feedback = this.el.select('.form-control-feedback', true).first();
8807             
8808             if(feedback){
8809                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8810                 
8811                 if(this.getValue().length || this.forceFeedback){
8812                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8813                 }
8814                 
8815             }
8816             
8817         }
8818         
8819         this.fireEvent('invalid', this, msg);
8820     },
8821     // private
8822     SafariOnKeyDown : function(event)
8823     {
8824         // this is a workaround for a password hang bug on chrome/ webkit.
8825         
8826         var isSelectAll = false;
8827         
8828         if(this.inputEl().dom.selectionEnd > 0){
8829             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8830         }
8831         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8832             event.preventDefault();
8833             this.setValue('');
8834             return;
8835         }
8836         
8837         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8838             
8839             event.preventDefault();
8840             // this is very hacky as keydown always get's upper case.
8841             //
8842             var cc = String.fromCharCode(event.getCharCode());
8843             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8844             
8845         }
8846     },
8847     adjustWidth : function(tag, w){
8848         tag = tag.toLowerCase();
8849         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8850             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8851                 if(tag == 'input'){
8852                     return w + 2;
8853                 }
8854                 if(tag == 'textarea'){
8855                     return w-2;
8856                 }
8857             }else if(Roo.isOpera){
8858                 if(tag == 'input'){
8859                     return w + 2;
8860                 }
8861                 if(tag == 'textarea'){
8862                     return w-2;
8863                 }
8864             }
8865         }
8866         return w;
8867     }
8868     
8869 });
8870
8871  
8872 /*
8873  * - LGPL
8874  *
8875  * Input
8876  * 
8877  */
8878
8879 /**
8880  * @class Roo.bootstrap.TextArea
8881  * @extends Roo.bootstrap.Input
8882  * Bootstrap TextArea class
8883  * @cfg {Number} cols Specifies the visible width of a text area
8884  * @cfg {Number} rows Specifies the visible number of lines in a text area
8885  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8886  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8887  * @cfg {string} html text
8888  * 
8889  * @constructor
8890  * Create a new TextArea
8891  * @param {Object} config The config object
8892  */
8893
8894 Roo.bootstrap.TextArea = function(config){
8895     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8896    
8897 };
8898
8899 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8900      
8901     cols : false,
8902     rows : 5,
8903     readOnly : false,
8904     warp : 'soft',
8905     resize : false,
8906     value: false,
8907     html: false,
8908     
8909     getAutoCreate : function(){
8910         
8911         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8912         
8913         var id = Roo.id();
8914         
8915         var cfg = {};
8916         
8917         var input =  {
8918             tag: 'textarea',
8919             id : id,
8920             warp : this.warp,
8921             rows : this.rows,
8922             value : this.value || '',
8923             html: this.html || '',
8924             cls : 'form-control',
8925             placeholder : this.placeholder || '' 
8926             
8927         };
8928         
8929         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8930             input.maxLength = this.maxLength;
8931         }
8932         
8933         if(this.resize){
8934             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8935         }
8936         
8937         if(this.cols){
8938             input.cols = this.cols;
8939         }
8940         
8941         if (this.readOnly) {
8942             input.readonly = true;
8943         }
8944         
8945         if (this.name) {
8946             input.name = this.name;
8947         }
8948         
8949         if (this.size) {
8950             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8951         }
8952         
8953         var settings=this;
8954         ['xs','sm','md','lg'].map(function(size){
8955             if (settings[size]) {
8956                 cfg.cls += ' col-' + size + '-' + settings[size];
8957             }
8958         });
8959         
8960         var inputblock = input;
8961         
8962         if(this.hasFeedback && !this.allowBlank){
8963             
8964             var feedback = {
8965                 tag: 'span',
8966                 cls: 'glyphicon form-control-feedback'
8967             };
8968
8969             inputblock = {
8970                 cls : 'has-feedback',
8971                 cn :  [
8972                     input,
8973                     feedback
8974                 ] 
8975             };  
8976         }
8977         
8978         
8979         if (this.before || this.after) {
8980             
8981             inputblock = {
8982                 cls : 'input-group',
8983                 cn :  [] 
8984             };
8985             if (this.before) {
8986                 inputblock.cn.push({
8987                     tag :'span',
8988                     cls : 'input-group-addon',
8989                     html : this.before
8990                 });
8991             }
8992             
8993             inputblock.cn.push(input);
8994             
8995             if(this.hasFeedback && !this.allowBlank){
8996                 inputblock.cls += ' has-feedback';
8997                 inputblock.cn.push(feedback);
8998             }
8999             
9000             if (this.after) {
9001                 inputblock.cn.push({
9002                     tag :'span',
9003                     cls : 'input-group-addon',
9004                     html : this.after
9005                 });
9006             }
9007             
9008         }
9009         
9010         if (align ==='left' && this.fieldLabel.length) {
9011 //                Roo.log("left and has label");
9012                 cfg.cn = [
9013                     
9014                     {
9015                         tag: 'label',
9016                         'for' :  id,
9017                         cls : 'control-label col-sm-' + this.labelWidth,
9018                         html : this.fieldLabel
9019                         
9020                     },
9021                     {
9022                         cls : "col-sm-" + (12 - this.labelWidth), 
9023                         cn: [
9024                             inputblock
9025                         ]
9026                     }
9027                     
9028                 ];
9029         } else if ( this.fieldLabel.length) {
9030 //                Roo.log(" label");
9031                  cfg.cn = [
9032                    
9033                     {
9034                         tag: 'label',
9035                         //cls : 'input-group-addon',
9036                         html : this.fieldLabel
9037                         
9038                     },
9039                     
9040                     inputblock
9041                     
9042                 ];
9043
9044         } else {
9045             
9046 //                   Roo.log(" no label && no align");
9047                 cfg.cn = [
9048                     
9049                         inputblock
9050                     
9051                 ];
9052                 
9053                 
9054         }
9055         
9056         if (this.disabled) {
9057             input.disabled=true;
9058         }
9059         
9060         return cfg;
9061         
9062     },
9063     /**
9064      * return the real textarea element.
9065      */
9066     inputEl: function ()
9067     {
9068         return this.el.select('textarea.form-control',true).first();
9069     },
9070     
9071     /**
9072      * Clear any invalid styles/messages for this field
9073      */
9074     clearInvalid : function()
9075     {
9076         
9077         if(!this.el || this.preventMark){ // not rendered
9078             return;
9079         }
9080         
9081         var label = this.el.select('label', true).first();
9082         var icon = this.el.select('i.fa-star', true).first();
9083         
9084         if(label && icon){
9085             icon.remove();
9086         }
9087         
9088         this.el.removeClass(this.invalidClass);
9089         
9090         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9091             
9092             var feedback = this.el.select('.form-control-feedback', true).first();
9093             
9094             if(feedback){
9095                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9096             }
9097             
9098         }
9099         
9100         this.fireEvent('valid', this);
9101     },
9102     
9103      /**
9104      * Mark this field as valid
9105      */
9106     markValid : function()
9107     {
9108         if(!this.el  || this.preventMark){ // not rendered
9109             return;
9110         }
9111         
9112         this.el.removeClass([this.invalidClass, this.validClass]);
9113         
9114         var feedback = this.el.select('.form-control-feedback', true).first();
9115             
9116         if(feedback){
9117             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9118         }
9119
9120         if(this.disabled || this.allowBlank){
9121             return;
9122         }
9123         
9124         var label = this.el.select('label', true).first();
9125         var icon = this.el.select('i.fa-star', true).first();
9126         
9127         if(label && icon){
9128             icon.remove();
9129         }
9130         
9131         this.el.addClass(this.validClass);
9132         
9133         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9134             
9135             var feedback = this.el.select('.form-control-feedback', true).first();
9136             
9137             if(feedback){
9138                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9139                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9140             }
9141             
9142         }
9143         
9144         this.fireEvent('valid', this);
9145     },
9146     
9147      /**
9148      * Mark this field as invalid
9149      * @param {String} msg The validation message
9150      */
9151     markInvalid : function(msg)
9152     {
9153         if(!this.el  || this.preventMark){ // not rendered
9154             return;
9155         }
9156         
9157         this.el.removeClass([this.invalidClass, this.validClass]);
9158         
9159         var feedback = this.el.select('.form-control-feedback', true).first();
9160             
9161         if(feedback){
9162             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9163         }
9164
9165         if(this.disabled || this.allowBlank){
9166             return;
9167         }
9168         
9169         var label = this.el.select('label', true).first();
9170         var icon = this.el.select('i.fa-star', true).first();
9171         
9172         if(!this.getValue().length && label && !icon){
9173             this.el.createChild({
9174                 tag : 'i',
9175                 cls : 'text-danger fa fa-lg fa-star',
9176                 tooltip : 'This field is required',
9177                 style : 'margin-right:5px;'
9178             }, label, true);
9179         }
9180
9181         this.el.addClass(this.invalidClass);
9182         
9183         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9184             
9185             var feedback = this.el.select('.form-control-feedback', true).first();
9186             
9187             if(feedback){
9188                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9189                 
9190                 if(this.getValue().length || this.forceFeedback){
9191                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9192                 }
9193                 
9194             }
9195             
9196         }
9197         
9198         this.fireEvent('invalid', this, msg);
9199     }
9200 });
9201
9202  
9203 /*
9204  * - LGPL
9205  *
9206  * trigger field - base class for combo..
9207  * 
9208  */
9209  
9210 /**
9211  * @class Roo.bootstrap.TriggerField
9212  * @extends Roo.bootstrap.Input
9213  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9214  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9215  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9216  * for which you can provide a custom implementation.  For example:
9217  * <pre><code>
9218 var trigger = new Roo.bootstrap.TriggerField();
9219 trigger.onTriggerClick = myTriggerFn;
9220 trigger.applyTo('my-field');
9221 </code></pre>
9222  *
9223  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9224  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9225  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9226  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9227  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9228
9229  * @constructor
9230  * Create a new TriggerField.
9231  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9232  * to the base TextField)
9233  */
9234 Roo.bootstrap.TriggerField = function(config){
9235     this.mimicing = false;
9236     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9237 };
9238
9239 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9240     /**
9241      * @cfg {String} triggerClass A CSS class to apply to the trigger
9242      */
9243      /**
9244      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9245      */
9246     hideTrigger:false,
9247
9248     /**
9249      * @cfg {Boolean} removable (true|false) special filter default false
9250      */
9251     removable : false,
9252     
9253     /** @cfg {Boolean} grow @hide */
9254     /** @cfg {Number} growMin @hide */
9255     /** @cfg {Number} growMax @hide */
9256
9257     /**
9258      * @hide 
9259      * @method
9260      */
9261     autoSize: Roo.emptyFn,
9262     // private
9263     monitorTab : true,
9264     // private
9265     deferHeight : true,
9266
9267     
9268     actionMode : 'wrap',
9269     
9270     caret : false,
9271     
9272     
9273     getAutoCreate : function(){
9274        
9275         var align = this.labelAlign || this.parentLabelAlign();
9276         
9277         var id = Roo.id();
9278         
9279         var cfg = {
9280             cls: 'form-group' //input-group
9281         };
9282         
9283         
9284         var input =  {
9285             tag: 'input',
9286             id : id,
9287             type : this.inputType,
9288             cls : 'form-control',
9289             autocomplete: 'new-password',
9290             placeholder : this.placeholder || '' 
9291             
9292         };
9293         if (this.name) {
9294             input.name = this.name;
9295         }
9296         if (this.size) {
9297             input.cls += ' input-' + this.size;
9298         }
9299         
9300         if (this.disabled) {
9301             input.disabled=true;
9302         }
9303         
9304         var inputblock = input;
9305         
9306         if(this.hasFeedback && !this.allowBlank){
9307             
9308             var feedback = {
9309                 tag: 'span',
9310                 cls: 'glyphicon form-control-feedback'
9311             };
9312             
9313             if(this.removable && !this.editable && !this.tickable){
9314                 inputblock = {
9315                     cls : 'has-feedback',
9316                     cn :  [
9317                         inputblock,
9318                         {
9319                             tag: 'button',
9320                             html : 'x',
9321                             cls : 'roo-combo-removable-btn close'
9322                         },
9323                         feedback
9324                     ] 
9325                 };
9326             } else {
9327                 inputblock = {
9328                     cls : 'has-feedback',
9329                     cn :  [
9330                         inputblock,
9331                         feedback
9332                     ] 
9333                 };
9334             }
9335
9336         } else {
9337             if(this.removable && !this.editable && !this.tickable){
9338                 inputblock = {
9339                     cls : 'roo-removable',
9340                     cn :  [
9341                         inputblock,
9342                         {
9343                             tag: 'button',
9344                             html : 'x',
9345                             cls : 'roo-combo-removable-btn close'
9346                         }
9347                     ] 
9348                 };
9349             }
9350         }
9351         
9352         if (this.before || this.after) {
9353             
9354             inputblock = {
9355                 cls : 'input-group',
9356                 cn :  [] 
9357             };
9358             if (this.before) {
9359                 inputblock.cn.push({
9360                     tag :'span',
9361                     cls : 'input-group-addon',
9362                     html : this.before
9363                 });
9364             }
9365             
9366             inputblock.cn.push(input);
9367             
9368             if(this.hasFeedback && !this.allowBlank){
9369                 inputblock.cls += ' has-feedback';
9370                 inputblock.cn.push(feedback);
9371             }
9372             
9373             if (this.after) {
9374                 inputblock.cn.push({
9375                     tag :'span',
9376                     cls : 'input-group-addon',
9377                     html : this.after
9378                 });
9379             }
9380             
9381         };
9382         
9383         var box = {
9384             tag: 'div',
9385             cn: [
9386                 {
9387                     tag: 'input',
9388                     type : 'hidden',
9389                     cls: 'form-hidden-field'
9390                 },
9391                 inputblock
9392             ]
9393             
9394         };
9395         
9396         if(this.multiple){
9397             box = {
9398                 tag: 'div',
9399                 cn: [
9400                     {
9401                         tag: 'input',
9402                         type : 'hidden',
9403                         cls: 'form-hidden-field'
9404                     },
9405                     {
9406                         tag: 'ul',
9407                         cls: 'roo-select2-choices',
9408                         cn:[
9409                             {
9410                                 tag: 'li',
9411                                 cls: 'roo-select2-search-field',
9412                                 cn: [
9413
9414                                     inputblock
9415                                 ]
9416                             }
9417                         ]
9418                     }
9419                 ]
9420             }
9421         };
9422         
9423         var combobox = {
9424             cls: 'roo-select2-container input-group',
9425             cn: [
9426                 box
9427 //                {
9428 //                    tag: 'ul',
9429 //                    cls: 'typeahead typeahead-long dropdown-menu',
9430 //                    style: 'display:none'
9431 //                }
9432             ]
9433         };
9434         
9435         if(!this.multiple && this.showToggleBtn){
9436             
9437             var caret = {
9438                         tag: 'span',
9439                         cls: 'caret'
9440              };
9441             if (this.caret != false) {
9442                 caret = {
9443                      tag: 'i',
9444                      cls: 'fa fa-' + this.caret
9445                 };
9446                 
9447             }
9448             
9449             combobox.cn.push({
9450                 tag :'span',
9451                 cls : 'input-group-addon btn dropdown-toggle',
9452                 cn : [
9453                     caret,
9454                     {
9455                         tag: 'span',
9456                         cls: 'combobox-clear',
9457                         cn  : [
9458                             {
9459                                 tag : 'i',
9460                                 cls: 'icon-remove'
9461                             }
9462                         ]
9463                     }
9464                 ]
9465
9466             })
9467         }
9468         
9469         if(this.multiple){
9470             combobox.cls += ' roo-select2-container-multi';
9471         }
9472         
9473         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9474             
9475 //                Roo.log("left and has label");
9476                 cfg.cn = [
9477                     
9478                     {
9479                         tag: 'label',
9480                         'for' :  id,
9481                         cls : 'control-label col-sm-' + this.labelWidth,
9482                         html : this.fieldLabel
9483                         
9484                     },
9485                     {
9486                         cls : "col-sm-" + (12 - this.labelWidth), 
9487                         cn: [
9488                             combobox
9489                         ]
9490                     }
9491                     
9492                 ];
9493         } else if ( this.fieldLabel.length) {
9494 //                Roo.log(" label");
9495                  cfg.cn = [
9496                    
9497                     {
9498                         tag: 'label',
9499                         //cls : 'input-group-addon',
9500                         html : this.fieldLabel
9501                         
9502                     },
9503                     
9504                     combobox
9505                     
9506                 ];
9507
9508         } else {
9509             
9510 //                Roo.log(" no label && no align");
9511                 cfg = combobox
9512                      
9513                 
9514         }
9515          
9516         var settings=this;
9517         ['xs','sm','md','lg'].map(function(size){
9518             if (settings[size]) {
9519                 cfg.cls += ' col-' + size + '-' + settings[size];
9520             }
9521         });
9522         
9523         return cfg;
9524         
9525     },
9526     
9527     
9528     
9529     // private
9530     onResize : function(w, h){
9531 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9532 //        if(typeof w == 'number'){
9533 //            var x = w - this.trigger.getWidth();
9534 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9535 //            this.trigger.setStyle('left', x+'px');
9536 //        }
9537     },
9538
9539     // private
9540     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9541
9542     // private
9543     getResizeEl : function(){
9544         return this.inputEl();
9545     },
9546
9547     // private
9548     getPositionEl : function(){
9549         return this.inputEl();
9550     },
9551
9552     // private
9553     alignErrorIcon : function(){
9554         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9555     },
9556
9557     // private
9558     initEvents : function(){
9559         
9560         this.createList();
9561         
9562         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9563         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9564         if(!this.multiple && this.showToggleBtn){
9565             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9566             if(this.hideTrigger){
9567                 this.trigger.setDisplayed(false);
9568             }
9569             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9570         }
9571         
9572         if(this.multiple){
9573             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9574         }
9575         
9576         if(this.removable && !this.editable && !this.tickable){
9577             var close = this.closeTriggerEl();
9578             
9579             if(close){
9580                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9581                 close.on('click', this.removeBtnClick, this, close);
9582             }
9583         }
9584         
9585         //this.trigger.addClassOnOver('x-form-trigger-over');
9586         //this.trigger.addClassOnClick('x-form-trigger-click');
9587         
9588         //if(!this.width){
9589         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9590         //}
9591     },
9592     
9593     closeTriggerEl : function()
9594     {
9595         var close = this.el.select('.roo-combo-removable-btn', true).first();
9596         return close ? close : false;
9597     },
9598     
9599     removeBtnClick : function(e, h, el)
9600     {
9601         e.preventDefault();
9602         
9603         if(this.fireEvent("remove", this) !== false){
9604             this.reset();
9605             this.fireEvent("afterremove", this)
9606         }
9607     },
9608     
9609     createList : function()
9610     {
9611         this.list = Roo.get(document.body).createChild({
9612             tag: 'ul',
9613             cls: 'typeahead typeahead-long dropdown-menu',
9614             style: 'display:none'
9615         });
9616         
9617         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9618         
9619     },
9620
9621     // private
9622     initTrigger : function(){
9623        
9624     },
9625
9626     // private
9627     onDestroy : function(){
9628         if(this.trigger){
9629             this.trigger.removeAllListeners();
9630           //  this.trigger.remove();
9631         }
9632         //if(this.wrap){
9633         //    this.wrap.remove();
9634         //}
9635         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9636     },
9637
9638     // private
9639     onFocus : function(){
9640         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9641         /*
9642         if(!this.mimicing){
9643             this.wrap.addClass('x-trigger-wrap-focus');
9644             this.mimicing = true;
9645             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9646             if(this.monitorTab){
9647                 this.el.on("keydown", this.checkTab, this);
9648             }
9649         }
9650         */
9651     },
9652
9653     // private
9654     checkTab : function(e){
9655         if(e.getKey() == e.TAB){
9656             this.triggerBlur();
9657         }
9658     },
9659
9660     // private
9661     onBlur : function(){
9662         // do nothing
9663     },
9664
9665     // private
9666     mimicBlur : function(e, t){
9667         /*
9668         if(!this.wrap.contains(t) && this.validateBlur()){
9669             this.triggerBlur();
9670         }
9671         */
9672     },
9673
9674     // private
9675     triggerBlur : function(){
9676         this.mimicing = false;
9677         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9678         if(this.monitorTab){
9679             this.el.un("keydown", this.checkTab, this);
9680         }
9681         //this.wrap.removeClass('x-trigger-wrap-focus');
9682         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9683     },
9684
9685     // private
9686     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9687     validateBlur : function(e, t){
9688         return true;
9689     },
9690
9691     // private
9692     onDisable : function(){
9693         this.inputEl().dom.disabled = true;
9694         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9695         //if(this.wrap){
9696         //    this.wrap.addClass('x-item-disabled');
9697         //}
9698     },
9699
9700     // private
9701     onEnable : function(){
9702         this.inputEl().dom.disabled = false;
9703         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9704         //if(this.wrap){
9705         //    this.el.removeClass('x-item-disabled');
9706         //}
9707     },
9708
9709     // private
9710     onShow : function(){
9711         var ae = this.getActionEl();
9712         
9713         if(ae){
9714             ae.dom.style.display = '';
9715             ae.dom.style.visibility = 'visible';
9716         }
9717     },
9718
9719     // private
9720     
9721     onHide : function(){
9722         var ae = this.getActionEl();
9723         ae.dom.style.display = 'none';
9724     },
9725
9726     /**
9727      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9728      * by an implementing function.
9729      * @method
9730      * @param {EventObject} e
9731      */
9732     onTriggerClick : Roo.emptyFn
9733 });
9734  /*
9735  * Based on:
9736  * Ext JS Library 1.1.1
9737  * Copyright(c) 2006-2007, Ext JS, LLC.
9738  *
9739  * Originally Released Under LGPL - original licence link has changed is not relivant.
9740  *
9741  * Fork - LGPL
9742  * <script type="text/javascript">
9743  */
9744
9745
9746 /**
9747  * @class Roo.data.SortTypes
9748  * @singleton
9749  * Defines the default sorting (casting?) comparison functions used when sorting data.
9750  */
9751 Roo.data.SortTypes = {
9752     /**
9753      * Default sort that does nothing
9754      * @param {Mixed} s The value being converted
9755      * @return {Mixed} The comparison value
9756      */
9757     none : function(s){
9758         return s;
9759     },
9760     
9761     /**
9762      * The regular expression used to strip tags
9763      * @type {RegExp}
9764      * @property
9765      */
9766     stripTagsRE : /<\/?[^>]+>/gi,
9767     
9768     /**
9769      * Strips all HTML tags to sort on text only
9770      * @param {Mixed} s The value being converted
9771      * @return {String} The comparison value
9772      */
9773     asText : function(s){
9774         return String(s).replace(this.stripTagsRE, "");
9775     },
9776     
9777     /**
9778      * Strips all HTML tags to sort on text only - Case insensitive
9779      * @param {Mixed} s The value being converted
9780      * @return {String} The comparison value
9781      */
9782     asUCText : function(s){
9783         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9784     },
9785     
9786     /**
9787      * Case insensitive string
9788      * @param {Mixed} s The value being converted
9789      * @return {String} The comparison value
9790      */
9791     asUCString : function(s) {
9792         return String(s).toUpperCase();
9793     },
9794     
9795     /**
9796      * Date sorting
9797      * @param {Mixed} s The value being converted
9798      * @return {Number} The comparison value
9799      */
9800     asDate : function(s) {
9801         if(!s){
9802             return 0;
9803         }
9804         if(s instanceof Date){
9805             return s.getTime();
9806         }
9807         return Date.parse(String(s));
9808     },
9809     
9810     /**
9811      * Float sorting
9812      * @param {Mixed} s The value being converted
9813      * @return {Float} The comparison value
9814      */
9815     asFloat : function(s) {
9816         var val = parseFloat(String(s).replace(/,/g, ""));
9817         if(isNaN(val)) {
9818             val = 0;
9819         }
9820         return val;
9821     },
9822     
9823     /**
9824      * Integer sorting
9825      * @param {Mixed} s The value being converted
9826      * @return {Number} The comparison value
9827      */
9828     asInt : function(s) {
9829         var val = parseInt(String(s).replace(/,/g, ""));
9830         if(isNaN(val)) {
9831             val = 0;
9832         }
9833         return val;
9834     }
9835 };/*
9836  * Based on:
9837  * Ext JS Library 1.1.1
9838  * Copyright(c) 2006-2007, Ext JS, LLC.
9839  *
9840  * Originally Released Under LGPL - original licence link has changed is not relivant.
9841  *
9842  * Fork - LGPL
9843  * <script type="text/javascript">
9844  */
9845
9846 /**
9847 * @class Roo.data.Record
9848  * Instances of this class encapsulate both record <em>definition</em> information, and record
9849  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9850  * to access Records cached in an {@link Roo.data.Store} object.<br>
9851  * <p>
9852  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9853  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9854  * objects.<br>
9855  * <p>
9856  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9857  * @constructor
9858  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9859  * {@link #create}. The parameters are the same.
9860  * @param {Array} data An associative Array of data values keyed by the field name.
9861  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9862  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9863  * not specified an integer id is generated.
9864  */
9865 Roo.data.Record = function(data, id){
9866     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9867     this.data = data;
9868 };
9869
9870 /**
9871  * Generate a constructor for a specific record layout.
9872  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9873  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9874  * Each field definition object may contain the following properties: <ul>
9875  * <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,
9876  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9877  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9878  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9879  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9880  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9881  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9882  * this may be omitted.</p></li>
9883  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9884  * <ul><li>auto (Default, implies no conversion)</li>
9885  * <li>string</li>
9886  * <li>int</li>
9887  * <li>float</li>
9888  * <li>boolean</li>
9889  * <li>date</li></ul></p></li>
9890  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9891  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9892  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9893  * by the Reader into an object that will be stored in the Record. It is passed the
9894  * following parameters:<ul>
9895  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9896  * </ul></p></li>
9897  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9898  * </ul>
9899  * <br>usage:<br><pre><code>
9900 var TopicRecord = Roo.data.Record.create(
9901     {name: 'title', mapping: 'topic_title'},
9902     {name: 'author', mapping: 'username'},
9903     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9904     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9905     {name: 'lastPoster', mapping: 'user2'},
9906     {name: 'excerpt', mapping: 'post_text'}
9907 );
9908
9909 var myNewRecord = new TopicRecord({
9910     title: 'Do my job please',
9911     author: 'noobie',
9912     totalPosts: 1,
9913     lastPost: new Date(),
9914     lastPoster: 'Animal',
9915     excerpt: 'No way dude!'
9916 });
9917 myStore.add(myNewRecord);
9918 </code></pre>
9919  * @method create
9920  * @static
9921  */
9922 Roo.data.Record.create = function(o){
9923     var f = function(){
9924         f.superclass.constructor.apply(this, arguments);
9925     };
9926     Roo.extend(f, Roo.data.Record);
9927     var p = f.prototype;
9928     p.fields = new Roo.util.MixedCollection(false, function(field){
9929         return field.name;
9930     });
9931     for(var i = 0, len = o.length; i < len; i++){
9932         p.fields.add(new Roo.data.Field(o[i]));
9933     }
9934     f.getField = function(name){
9935         return p.fields.get(name);  
9936     };
9937     return f;
9938 };
9939
9940 Roo.data.Record.AUTO_ID = 1000;
9941 Roo.data.Record.EDIT = 'edit';
9942 Roo.data.Record.REJECT = 'reject';
9943 Roo.data.Record.COMMIT = 'commit';
9944
9945 Roo.data.Record.prototype = {
9946     /**
9947      * Readonly flag - true if this record has been modified.
9948      * @type Boolean
9949      */
9950     dirty : false,
9951     editing : false,
9952     error: null,
9953     modified: null,
9954
9955     // private
9956     join : function(store){
9957         this.store = store;
9958     },
9959
9960     /**
9961      * Set the named field to the specified value.
9962      * @param {String} name The name of the field to set.
9963      * @param {Object} value The value to set the field to.
9964      */
9965     set : function(name, value){
9966         if(this.data[name] == value){
9967             return;
9968         }
9969         this.dirty = true;
9970         if(!this.modified){
9971             this.modified = {};
9972         }
9973         if(typeof this.modified[name] == 'undefined'){
9974             this.modified[name] = this.data[name];
9975         }
9976         this.data[name] = value;
9977         if(!this.editing && this.store){
9978             this.store.afterEdit(this);
9979         }       
9980     },
9981
9982     /**
9983      * Get the value of the named field.
9984      * @param {String} name The name of the field to get the value of.
9985      * @return {Object} The value of the field.
9986      */
9987     get : function(name){
9988         return this.data[name]; 
9989     },
9990
9991     // private
9992     beginEdit : function(){
9993         this.editing = true;
9994         this.modified = {}; 
9995     },
9996
9997     // private
9998     cancelEdit : function(){
9999         this.editing = false;
10000         delete this.modified;
10001     },
10002
10003     // private
10004     endEdit : function(){
10005         this.editing = false;
10006         if(this.dirty && this.store){
10007             this.store.afterEdit(this);
10008         }
10009     },
10010
10011     /**
10012      * Usually called by the {@link Roo.data.Store} which owns the Record.
10013      * Rejects all changes made to the Record since either creation, or the last commit operation.
10014      * Modified fields are reverted to their original values.
10015      * <p>
10016      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10017      * of reject operations.
10018      */
10019     reject : function(){
10020         var m = this.modified;
10021         for(var n in m){
10022             if(typeof m[n] != "function"){
10023                 this.data[n] = m[n];
10024             }
10025         }
10026         this.dirty = false;
10027         delete this.modified;
10028         this.editing = false;
10029         if(this.store){
10030             this.store.afterReject(this);
10031         }
10032     },
10033
10034     /**
10035      * Usually called by the {@link Roo.data.Store} which owns the Record.
10036      * Commits all changes made to the Record since either creation, or the last commit operation.
10037      * <p>
10038      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10039      * of commit operations.
10040      */
10041     commit : function(){
10042         this.dirty = false;
10043         delete this.modified;
10044         this.editing = false;
10045         if(this.store){
10046             this.store.afterCommit(this);
10047         }
10048     },
10049
10050     // private
10051     hasError : function(){
10052         return this.error != null;
10053     },
10054
10055     // private
10056     clearError : function(){
10057         this.error = null;
10058     },
10059
10060     /**
10061      * Creates a copy of this record.
10062      * @param {String} id (optional) A new record id if you don't want to use this record's id
10063      * @return {Record}
10064      */
10065     copy : function(newId) {
10066         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10067     }
10068 };/*
10069  * Based on:
10070  * Ext JS Library 1.1.1
10071  * Copyright(c) 2006-2007, Ext JS, LLC.
10072  *
10073  * Originally Released Under LGPL - original licence link has changed is not relivant.
10074  *
10075  * Fork - LGPL
10076  * <script type="text/javascript">
10077  */
10078
10079
10080
10081 /**
10082  * @class Roo.data.Store
10083  * @extends Roo.util.Observable
10084  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10085  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10086  * <p>
10087  * 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
10088  * has no knowledge of the format of the data returned by the Proxy.<br>
10089  * <p>
10090  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10091  * instances from the data object. These records are cached and made available through accessor functions.
10092  * @constructor
10093  * Creates a new Store.
10094  * @param {Object} config A config object containing the objects needed for the Store to access data,
10095  * and read the data into Records.
10096  */
10097 Roo.data.Store = function(config){
10098     this.data = new Roo.util.MixedCollection(false);
10099     this.data.getKey = function(o){
10100         return o.id;
10101     };
10102     this.baseParams = {};
10103     // private
10104     this.paramNames = {
10105         "start" : "start",
10106         "limit" : "limit",
10107         "sort" : "sort",
10108         "dir" : "dir",
10109         "multisort" : "_multisort"
10110     };
10111
10112     if(config && config.data){
10113         this.inlineData = config.data;
10114         delete config.data;
10115     }
10116
10117     Roo.apply(this, config);
10118     
10119     if(this.reader){ // reader passed
10120         this.reader = Roo.factory(this.reader, Roo.data);
10121         this.reader.xmodule = this.xmodule || false;
10122         if(!this.recordType){
10123             this.recordType = this.reader.recordType;
10124         }
10125         if(this.reader.onMetaChange){
10126             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10127         }
10128     }
10129
10130     if(this.recordType){
10131         this.fields = this.recordType.prototype.fields;
10132     }
10133     this.modified = [];
10134
10135     this.addEvents({
10136         /**
10137          * @event datachanged
10138          * Fires when the data cache has changed, and a widget which is using this Store
10139          * as a Record cache should refresh its view.
10140          * @param {Store} this
10141          */
10142         datachanged : true,
10143         /**
10144          * @event metachange
10145          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10146          * @param {Store} this
10147          * @param {Object} meta The JSON metadata
10148          */
10149         metachange : true,
10150         /**
10151          * @event add
10152          * Fires when Records have been added to the Store
10153          * @param {Store} this
10154          * @param {Roo.data.Record[]} records The array of Records added
10155          * @param {Number} index The index at which the record(s) were added
10156          */
10157         add : true,
10158         /**
10159          * @event remove
10160          * Fires when a Record has been removed from the Store
10161          * @param {Store} this
10162          * @param {Roo.data.Record} record The Record that was removed
10163          * @param {Number} index The index at which the record was removed
10164          */
10165         remove : true,
10166         /**
10167          * @event update
10168          * Fires when a Record has been updated
10169          * @param {Store} this
10170          * @param {Roo.data.Record} record The Record that was updated
10171          * @param {String} operation The update operation being performed.  Value may be one of:
10172          * <pre><code>
10173  Roo.data.Record.EDIT
10174  Roo.data.Record.REJECT
10175  Roo.data.Record.COMMIT
10176          * </code></pre>
10177          */
10178         update : true,
10179         /**
10180          * @event clear
10181          * Fires when the data cache has been cleared.
10182          * @param {Store} this
10183          */
10184         clear : true,
10185         /**
10186          * @event beforeload
10187          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10188          * the load action will be canceled.
10189          * @param {Store} this
10190          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10191          */
10192         beforeload : true,
10193         /**
10194          * @event beforeloadadd
10195          * Fires after a new set of Records has been loaded.
10196          * @param {Store} this
10197          * @param {Roo.data.Record[]} records The Records that were loaded
10198          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10199          */
10200         beforeloadadd : true,
10201         /**
10202          * @event load
10203          * Fires after a new set of Records has been loaded, before they are added to the store.
10204          * @param {Store} this
10205          * @param {Roo.data.Record[]} records The Records that were loaded
10206          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10207          * @params {Object} return from reader
10208          */
10209         load : true,
10210         /**
10211          * @event loadexception
10212          * Fires if an exception occurs in the Proxy during loading.
10213          * Called with the signature of the Proxy's "loadexception" event.
10214          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10215          * 
10216          * @param {Proxy} 
10217          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10218          * @param {Object} load options 
10219          * @param {Object} jsonData from your request (normally this contains the Exception)
10220          */
10221         loadexception : true
10222     });
10223     
10224     if(this.proxy){
10225         this.proxy = Roo.factory(this.proxy, Roo.data);
10226         this.proxy.xmodule = this.xmodule || false;
10227         this.relayEvents(this.proxy,  ["loadexception"]);
10228     }
10229     this.sortToggle = {};
10230     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10231
10232     Roo.data.Store.superclass.constructor.call(this);
10233
10234     if(this.inlineData){
10235         this.loadData(this.inlineData);
10236         delete this.inlineData;
10237     }
10238 };
10239
10240 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10241      /**
10242     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10243     * without a remote query - used by combo/forms at present.
10244     */
10245     
10246     /**
10247     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10248     */
10249     /**
10250     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10251     */
10252     /**
10253     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10254     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10255     */
10256     /**
10257     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10258     * on any HTTP request
10259     */
10260     /**
10261     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10262     */
10263     /**
10264     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10265     */
10266     multiSort: false,
10267     /**
10268     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10269     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10270     */
10271     remoteSort : false,
10272
10273     /**
10274     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10275      * loaded or when a record is removed. (defaults to false).
10276     */
10277     pruneModifiedRecords : false,
10278
10279     // private
10280     lastOptions : null,
10281
10282     /**
10283      * Add Records to the Store and fires the add event.
10284      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10285      */
10286     add : function(records){
10287         records = [].concat(records);
10288         for(var i = 0, len = records.length; i < len; i++){
10289             records[i].join(this);
10290         }
10291         var index = this.data.length;
10292         this.data.addAll(records);
10293         this.fireEvent("add", this, records, index);
10294     },
10295
10296     /**
10297      * Remove a Record from the Store and fires the remove event.
10298      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10299      */
10300     remove : function(record){
10301         var index = this.data.indexOf(record);
10302         this.data.removeAt(index);
10303         if(this.pruneModifiedRecords){
10304             this.modified.remove(record);
10305         }
10306         this.fireEvent("remove", this, record, index);
10307     },
10308
10309     /**
10310      * Remove all Records from the Store and fires the clear event.
10311      */
10312     removeAll : function(){
10313         this.data.clear();
10314         if(this.pruneModifiedRecords){
10315             this.modified = [];
10316         }
10317         this.fireEvent("clear", this);
10318     },
10319
10320     /**
10321      * Inserts Records to the Store at the given index and fires the add event.
10322      * @param {Number} index The start index at which to insert the passed Records.
10323      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10324      */
10325     insert : function(index, records){
10326         records = [].concat(records);
10327         for(var i = 0, len = records.length; i < len; i++){
10328             this.data.insert(index, records[i]);
10329             records[i].join(this);
10330         }
10331         this.fireEvent("add", this, records, index);
10332     },
10333
10334     /**
10335      * Get the index within the cache of the passed Record.
10336      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10337      * @return {Number} The index of the passed Record. Returns -1 if not found.
10338      */
10339     indexOf : function(record){
10340         return this.data.indexOf(record);
10341     },
10342
10343     /**
10344      * Get the index within the cache of the Record with the passed id.
10345      * @param {String} id The id of the Record to find.
10346      * @return {Number} The index of the Record. Returns -1 if not found.
10347      */
10348     indexOfId : function(id){
10349         return this.data.indexOfKey(id);
10350     },
10351
10352     /**
10353      * Get the Record with the specified id.
10354      * @param {String} id The id of the Record to find.
10355      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10356      */
10357     getById : function(id){
10358         return this.data.key(id);
10359     },
10360
10361     /**
10362      * Get the Record at the specified index.
10363      * @param {Number} index The index of the Record to find.
10364      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10365      */
10366     getAt : function(index){
10367         return this.data.itemAt(index);
10368     },
10369
10370     /**
10371      * Returns a range of Records between specified indices.
10372      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10373      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10374      * @return {Roo.data.Record[]} An array of Records
10375      */
10376     getRange : function(start, end){
10377         return this.data.getRange(start, end);
10378     },
10379
10380     // private
10381     storeOptions : function(o){
10382         o = Roo.apply({}, o);
10383         delete o.callback;
10384         delete o.scope;
10385         this.lastOptions = o;
10386     },
10387
10388     /**
10389      * Loads the Record cache from the configured Proxy using the configured Reader.
10390      * <p>
10391      * If using remote paging, then the first load call must specify the <em>start</em>
10392      * and <em>limit</em> properties in the options.params property to establish the initial
10393      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10394      * <p>
10395      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10396      * and this call will return before the new data has been loaded. Perform any post-processing
10397      * in a callback function, or in a "load" event handler.</strong>
10398      * <p>
10399      * @param {Object} options An object containing properties which control loading options:<ul>
10400      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10401      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10402      * passed the following arguments:<ul>
10403      * <li>r : Roo.data.Record[]</li>
10404      * <li>options: Options object from the load call</li>
10405      * <li>success: Boolean success indicator</li></ul></li>
10406      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10407      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10408      * </ul>
10409      */
10410     load : function(options){
10411         options = options || {};
10412         if(this.fireEvent("beforeload", this, options) !== false){
10413             this.storeOptions(options);
10414             var p = Roo.apply(options.params || {}, this.baseParams);
10415             // if meta was not loaded from remote source.. try requesting it.
10416             if (!this.reader.metaFromRemote) {
10417                 p._requestMeta = 1;
10418             }
10419             if(this.sortInfo && this.remoteSort){
10420                 var pn = this.paramNames;
10421                 p[pn["sort"]] = this.sortInfo.field;
10422                 p[pn["dir"]] = this.sortInfo.direction;
10423             }
10424             if (this.multiSort) {
10425                 var pn = this.paramNames;
10426                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10427             }
10428             
10429             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10430         }
10431     },
10432
10433     /**
10434      * Reloads the Record cache from the configured Proxy using the configured Reader and
10435      * the options from the last load operation performed.
10436      * @param {Object} options (optional) An object containing properties which may override the options
10437      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10438      * the most recently used options are reused).
10439      */
10440     reload : function(options){
10441         this.load(Roo.applyIf(options||{}, this.lastOptions));
10442     },
10443
10444     // private
10445     // Called as a callback by the Reader during a load operation.
10446     loadRecords : function(o, options, success){
10447         if(!o || success === false){
10448             if(success !== false){
10449                 this.fireEvent("load", this, [], options, o);
10450             }
10451             if(options.callback){
10452                 options.callback.call(options.scope || this, [], options, false);
10453             }
10454             return;
10455         }
10456         // if data returned failure - throw an exception.
10457         if (o.success === false) {
10458             // show a message if no listener is registered.
10459             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10460                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10461             }
10462             // loadmask wil be hooked into this..
10463             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10464             return;
10465         }
10466         var r = o.records, t = o.totalRecords || r.length;
10467         
10468         this.fireEvent("beforeloadadd", this, r, options, o);
10469         
10470         if(!options || options.add !== true){
10471             if(this.pruneModifiedRecords){
10472                 this.modified = [];
10473             }
10474             for(var i = 0, len = r.length; i < len; i++){
10475                 r[i].join(this);
10476             }
10477             if(this.snapshot){
10478                 this.data = this.snapshot;
10479                 delete this.snapshot;
10480             }
10481             this.data.clear();
10482             this.data.addAll(r);
10483             this.totalLength = t;
10484             this.applySort();
10485             this.fireEvent("datachanged", this);
10486         }else{
10487             this.totalLength = Math.max(t, this.data.length+r.length);
10488             this.add(r);
10489         }
10490         this.fireEvent("load", this, r, options, o);
10491         if(options.callback){
10492             options.callback.call(options.scope || this, r, options, true);
10493         }
10494     },
10495
10496
10497     /**
10498      * Loads data from a passed data block. A Reader which understands the format of the data
10499      * must have been configured in the constructor.
10500      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10501      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10502      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10503      */
10504     loadData : function(o, append){
10505         var r = this.reader.readRecords(o);
10506         this.loadRecords(r, {add: append}, true);
10507     },
10508
10509     /**
10510      * Gets the number of cached records.
10511      * <p>
10512      * <em>If using paging, this may not be the total size of the dataset. If the data object
10513      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10514      * the data set size</em>
10515      */
10516     getCount : function(){
10517         return this.data.length || 0;
10518     },
10519
10520     /**
10521      * Gets the total number of records in the dataset as returned by the server.
10522      * <p>
10523      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10524      * the dataset size</em>
10525      */
10526     getTotalCount : function(){
10527         return this.totalLength || 0;
10528     },
10529
10530     /**
10531      * Returns the sort state of the Store as an object with two properties:
10532      * <pre><code>
10533  field {String} The name of the field by which the Records are sorted
10534  direction {String} The sort order, "ASC" or "DESC"
10535      * </code></pre>
10536      */
10537     getSortState : function(){
10538         return this.sortInfo;
10539     },
10540
10541     // private
10542     applySort : function(){
10543         if(this.sortInfo && !this.remoteSort){
10544             var s = this.sortInfo, f = s.field;
10545             var st = this.fields.get(f).sortType;
10546             var fn = function(r1, r2){
10547                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10548                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10549             };
10550             this.data.sort(s.direction, fn);
10551             if(this.snapshot && this.snapshot != this.data){
10552                 this.snapshot.sort(s.direction, fn);
10553             }
10554         }
10555     },
10556
10557     /**
10558      * Sets the default sort column and order to be used by the next load operation.
10559      * @param {String} fieldName The name of the field to sort by.
10560      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10561      */
10562     setDefaultSort : function(field, dir){
10563         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10564     },
10565
10566     /**
10567      * Sort the Records.
10568      * If remote sorting is used, the sort is performed on the server, and the cache is
10569      * reloaded. If local sorting is used, the cache is sorted internally.
10570      * @param {String} fieldName The name of the field to sort by.
10571      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10572      */
10573     sort : function(fieldName, dir){
10574         var f = this.fields.get(fieldName);
10575         if(!dir){
10576             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10577             
10578             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10579                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10580             }else{
10581                 dir = f.sortDir;
10582             }
10583         }
10584         this.sortToggle[f.name] = dir;
10585         this.sortInfo = {field: f.name, direction: dir};
10586         if(!this.remoteSort){
10587             this.applySort();
10588             this.fireEvent("datachanged", this);
10589         }else{
10590             this.load(this.lastOptions);
10591         }
10592     },
10593
10594     /**
10595      * Calls the specified function for each of the Records in the cache.
10596      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10597      * Returning <em>false</em> aborts and exits the iteration.
10598      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10599      */
10600     each : function(fn, scope){
10601         this.data.each(fn, scope);
10602     },
10603
10604     /**
10605      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10606      * (e.g., during paging).
10607      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10608      */
10609     getModifiedRecords : function(){
10610         return this.modified;
10611     },
10612
10613     // private
10614     createFilterFn : function(property, value, anyMatch){
10615         if(!value.exec){ // not a regex
10616             value = String(value);
10617             if(value.length == 0){
10618                 return false;
10619             }
10620             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10621         }
10622         return function(r){
10623             return value.test(r.data[property]);
10624         };
10625     },
10626
10627     /**
10628      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10629      * @param {String} property A field on your records
10630      * @param {Number} start The record index to start at (defaults to 0)
10631      * @param {Number} end The last record index to include (defaults to length - 1)
10632      * @return {Number} The sum
10633      */
10634     sum : function(property, start, end){
10635         var rs = this.data.items, v = 0;
10636         start = start || 0;
10637         end = (end || end === 0) ? end : rs.length-1;
10638
10639         for(var i = start; i <= end; i++){
10640             v += (rs[i].data[property] || 0);
10641         }
10642         return v;
10643     },
10644
10645     /**
10646      * Filter the records by a specified property.
10647      * @param {String} field A field on your records
10648      * @param {String/RegExp} value Either a string that the field
10649      * should start with or a RegExp to test against the field
10650      * @param {Boolean} anyMatch True to match any part not just the beginning
10651      */
10652     filter : function(property, value, anyMatch){
10653         var fn = this.createFilterFn(property, value, anyMatch);
10654         return fn ? this.filterBy(fn) : this.clearFilter();
10655     },
10656
10657     /**
10658      * Filter by a function. The specified function will be called with each
10659      * record in this data source. If the function returns true the record is included,
10660      * otherwise it is filtered.
10661      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10662      * @param {Object} scope (optional) The scope of the function (defaults to this)
10663      */
10664     filterBy : function(fn, scope){
10665         this.snapshot = this.snapshot || this.data;
10666         this.data = this.queryBy(fn, scope||this);
10667         this.fireEvent("datachanged", this);
10668     },
10669
10670     /**
10671      * Query the records by a specified property.
10672      * @param {String} field A field on your records
10673      * @param {String/RegExp} value Either a string that the field
10674      * should start with or a RegExp to test against the field
10675      * @param {Boolean} anyMatch True to match any part not just the beginning
10676      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10677      */
10678     query : function(property, value, anyMatch){
10679         var fn = this.createFilterFn(property, value, anyMatch);
10680         return fn ? this.queryBy(fn) : this.data.clone();
10681     },
10682
10683     /**
10684      * Query by a function. The specified function will be called with each
10685      * record in this data source. If the function returns true the record is included
10686      * in the results.
10687      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10688      * @param {Object} scope (optional) The scope of the function (defaults to this)
10689       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10690      **/
10691     queryBy : function(fn, scope){
10692         var data = this.snapshot || this.data;
10693         return data.filterBy(fn, scope||this);
10694     },
10695
10696     /**
10697      * Collects unique values for a particular dataIndex from this store.
10698      * @param {String} dataIndex The property to collect
10699      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10700      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10701      * @return {Array} An array of the unique values
10702      **/
10703     collect : function(dataIndex, allowNull, bypassFilter){
10704         var d = (bypassFilter === true && this.snapshot) ?
10705                 this.snapshot.items : this.data.items;
10706         var v, sv, r = [], l = {};
10707         for(var i = 0, len = d.length; i < len; i++){
10708             v = d[i].data[dataIndex];
10709             sv = String(v);
10710             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10711                 l[sv] = true;
10712                 r[r.length] = v;
10713             }
10714         }
10715         return r;
10716     },
10717
10718     /**
10719      * Revert to a view of the Record cache with no filtering applied.
10720      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10721      */
10722     clearFilter : function(suppressEvent){
10723         if(this.snapshot && this.snapshot != this.data){
10724             this.data = this.snapshot;
10725             delete this.snapshot;
10726             if(suppressEvent !== true){
10727                 this.fireEvent("datachanged", this);
10728             }
10729         }
10730     },
10731
10732     // private
10733     afterEdit : function(record){
10734         if(this.modified.indexOf(record) == -1){
10735             this.modified.push(record);
10736         }
10737         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10738     },
10739     
10740     // private
10741     afterReject : function(record){
10742         this.modified.remove(record);
10743         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10744     },
10745
10746     // private
10747     afterCommit : function(record){
10748         this.modified.remove(record);
10749         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10750     },
10751
10752     /**
10753      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10754      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10755      */
10756     commitChanges : function(){
10757         var m = this.modified.slice(0);
10758         this.modified = [];
10759         for(var i = 0, len = m.length; i < len; i++){
10760             m[i].commit();
10761         }
10762     },
10763
10764     /**
10765      * Cancel outstanding changes on all changed records.
10766      */
10767     rejectChanges : function(){
10768         var m = this.modified.slice(0);
10769         this.modified = [];
10770         for(var i = 0, len = m.length; i < len; i++){
10771             m[i].reject();
10772         }
10773     },
10774
10775     onMetaChange : function(meta, rtype, o){
10776         this.recordType = rtype;
10777         this.fields = rtype.prototype.fields;
10778         delete this.snapshot;
10779         this.sortInfo = meta.sortInfo || this.sortInfo;
10780         this.modified = [];
10781         this.fireEvent('metachange', this, this.reader.meta);
10782     },
10783     
10784     moveIndex : function(data, type)
10785     {
10786         var index = this.indexOf(data);
10787         
10788         var newIndex = index + type;
10789         
10790         this.remove(data);
10791         
10792         this.insert(newIndex, data);
10793         
10794     }
10795 });/*
10796  * Based on:
10797  * Ext JS Library 1.1.1
10798  * Copyright(c) 2006-2007, Ext JS, LLC.
10799  *
10800  * Originally Released Under LGPL - original licence link has changed is not relivant.
10801  *
10802  * Fork - LGPL
10803  * <script type="text/javascript">
10804  */
10805
10806 /**
10807  * @class Roo.data.SimpleStore
10808  * @extends Roo.data.Store
10809  * Small helper class to make creating Stores from Array data easier.
10810  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10811  * @cfg {Array} fields An array of field definition objects, or field name strings.
10812  * @cfg {Array} data The multi-dimensional array of data
10813  * @constructor
10814  * @param {Object} config
10815  */
10816 Roo.data.SimpleStore = function(config){
10817     Roo.data.SimpleStore.superclass.constructor.call(this, {
10818         isLocal : true,
10819         reader: new Roo.data.ArrayReader({
10820                 id: config.id
10821             },
10822             Roo.data.Record.create(config.fields)
10823         ),
10824         proxy : new Roo.data.MemoryProxy(config.data)
10825     });
10826     this.load();
10827 };
10828 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10829  * Based on:
10830  * Ext JS Library 1.1.1
10831  * Copyright(c) 2006-2007, Ext JS, LLC.
10832  *
10833  * Originally Released Under LGPL - original licence link has changed is not relivant.
10834  *
10835  * Fork - LGPL
10836  * <script type="text/javascript">
10837  */
10838
10839 /**
10840 /**
10841  * @extends Roo.data.Store
10842  * @class Roo.data.JsonStore
10843  * Small helper class to make creating Stores for JSON data easier. <br/>
10844 <pre><code>
10845 var store = new Roo.data.JsonStore({
10846     url: 'get-images.php',
10847     root: 'images',
10848     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10849 });
10850 </code></pre>
10851  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10852  * JsonReader and HttpProxy (unless inline data is provided).</b>
10853  * @cfg {Array} fields An array of field definition objects, or field name strings.
10854  * @constructor
10855  * @param {Object} config
10856  */
10857 Roo.data.JsonStore = function(c){
10858     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10859         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10860         reader: new Roo.data.JsonReader(c, c.fields)
10861     }));
10862 };
10863 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10864  * Based on:
10865  * Ext JS Library 1.1.1
10866  * Copyright(c) 2006-2007, Ext JS, LLC.
10867  *
10868  * Originally Released Under LGPL - original licence link has changed is not relivant.
10869  *
10870  * Fork - LGPL
10871  * <script type="text/javascript">
10872  */
10873
10874  
10875 Roo.data.Field = function(config){
10876     if(typeof config == "string"){
10877         config = {name: config};
10878     }
10879     Roo.apply(this, config);
10880     
10881     if(!this.type){
10882         this.type = "auto";
10883     }
10884     
10885     var st = Roo.data.SortTypes;
10886     // named sortTypes are supported, here we look them up
10887     if(typeof this.sortType == "string"){
10888         this.sortType = st[this.sortType];
10889     }
10890     
10891     // set default sortType for strings and dates
10892     if(!this.sortType){
10893         switch(this.type){
10894             case "string":
10895                 this.sortType = st.asUCString;
10896                 break;
10897             case "date":
10898                 this.sortType = st.asDate;
10899                 break;
10900             default:
10901                 this.sortType = st.none;
10902         }
10903     }
10904
10905     // define once
10906     var stripRe = /[\$,%]/g;
10907
10908     // prebuilt conversion function for this field, instead of
10909     // switching every time we're reading a value
10910     if(!this.convert){
10911         var cv, dateFormat = this.dateFormat;
10912         switch(this.type){
10913             case "":
10914             case "auto":
10915             case undefined:
10916                 cv = function(v){ return v; };
10917                 break;
10918             case "string":
10919                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10920                 break;
10921             case "int":
10922                 cv = function(v){
10923                     return v !== undefined && v !== null && v !== '' ?
10924                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10925                     };
10926                 break;
10927             case "float":
10928                 cv = function(v){
10929                     return v !== undefined && v !== null && v !== '' ?
10930                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10931                     };
10932                 break;
10933             case "bool":
10934             case "boolean":
10935                 cv = function(v){ return v === true || v === "true" || v == 1; };
10936                 break;
10937             case "date":
10938                 cv = function(v){
10939                     if(!v){
10940                         return '';
10941                     }
10942                     if(v instanceof Date){
10943                         return v;
10944                     }
10945                     if(dateFormat){
10946                         if(dateFormat == "timestamp"){
10947                             return new Date(v*1000);
10948                         }
10949                         return Date.parseDate(v, dateFormat);
10950                     }
10951                     var parsed = Date.parse(v);
10952                     return parsed ? new Date(parsed) : null;
10953                 };
10954              break;
10955             
10956         }
10957         this.convert = cv;
10958     }
10959 };
10960
10961 Roo.data.Field.prototype = {
10962     dateFormat: null,
10963     defaultValue: "",
10964     mapping: null,
10965     sortType : null,
10966     sortDir : "ASC"
10967 };/*
10968  * Based on:
10969  * Ext JS Library 1.1.1
10970  * Copyright(c) 2006-2007, Ext JS, LLC.
10971  *
10972  * Originally Released Under LGPL - original licence link has changed is not relivant.
10973  *
10974  * Fork - LGPL
10975  * <script type="text/javascript">
10976  */
10977  
10978 // Base class for reading structured data from a data source.  This class is intended to be
10979 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10980
10981 /**
10982  * @class Roo.data.DataReader
10983  * Base class for reading structured data from a data source.  This class is intended to be
10984  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10985  */
10986
10987 Roo.data.DataReader = function(meta, recordType){
10988     
10989     this.meta = meta;
10990     
10991     this.recordType = recordType instanceof Array ? 
10992         Roo.data.Record.create(recordType) : recordType;
10993 };
10994
10995 Roo.data.DataReader.prototype = {
10996      /**
10997      * Create an empty record
10998      * @param {Object} data (optional) - overlay some values
10999      * @return {Roo.data.Record} record created.
11000      */
11001     newRow :  function(d) {
11002         var da =  {};
11003         this.recordType.prototype.fields.each(function(c) {
11004             switch( c.type) {
11005                 case 'int' : da[c.name] = 0; break;
11006                 case 'date' : da[c.name] = new Date(); break;
11007                 case 'float' : da[c.name] = 0.0; break;
11008                 case 'boolean' : da[c.name] = false; break;
11009                 default : da[c.name] = ""; break;
11010             }
11011             
11012         });
11013         return new this.recordType(Roo.apply(da, d));
11014     }
11015     
11016 };/*
11017  * Based on:
11018  * Ext JS Library 1.1.1
11019  * Copyright(c) 2006-2007, Ext JS, LLC.
11020  *
11021  * Originally Released Under LGPL - original licence link has changed is not relivant.
11022  *
11023  * Fork - LGPL
11024  * <script type="text/javascript">
11025  */
11026
11027 /**
11028  * @class Roo.data.DataProxy
11029  * @extends Roo.data.Observable
11030  * This class is an abstract base class for implementations which provide retrieval of
11031  * unformatted data objects.<br>
11032  * <p>
11033  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11034  * (of the appropriate type which knows how to parse the data object) to provide a block of
11035  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11036  * <p>
11037  * Custom implementations must implement the load method as described in
11038  * {@link Roo.data.HttpProxy#load}.
11039  */
11040 Roo.data.DataProxy = function(){
11041     this.addEvents({
11042         /**
11043          * @event beforeload
11044          * Fires before a network request is made to retrieve a data object.
11045          * @param {Object} This DataProxy object.
11046          * @param {Object} params The params parameter to the load function.
11047          */
11048         beforeload : true,
11049         /**
11050          * @event load
11051          * Fires before the load method's callback is called.
11052          * @param {Object} This DataProxy object.
11053          * @param {Object} o The data object.
11054          * @param {Object} arg The callback argument object passed to the load function.
11055          */
11056         load : true,
11057         /**
11058          * @event loadexception
11059          * Fires if an Exception occurs during data retrieval.
11060          * @param {Object} This DataProxy object.
11061          * @param {Object} o The data object.
11062          * @param {Object} arg The callback argument object passed to the load function.
11063          * @param {Object} e The Exception.
11064          */
11065         loadexception : true
11066     });
11067     Roo.data.DataProxy.superclass.constructor.call(this);
11068 };
11069
11070 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11071
11072     /**
11073      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11074      */
11075 /*
11076  * Based on:
11077  * Ext JS Library 1.1.1
11078  * Copyright(c) 2006-2007, Ext JS, LLC.
11079  *
11080  * Originally Released Under LGPL - original licence link has changed is not relivant.
11081  *
11082  * Fork - LGPL
11083  * <script type="text/javascript">
11084  */
11085 /**
11086  * @class Roo.data.MemoryProxy
11087  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11088  * to the Reader when its load method is called.
11089  * @constructor
11090  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11091  */
11092 Roo.data.MemoryProxy = function(data){
11093     if (data.data) {
11094         data = data.data;
11095     }
11096     Roo.data.MemoryProxy.superclass.constructor.call(this);
11097     this.data = data;
11098 };
11099
11100 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11101     
11102     /**
11103      * Load data from the requested source (in this case an in-memory
11104      * data object passed to the constructor), read the data object into
11105      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11106      * process that block using the passed callback.
11107      * @param {Object} params This parameter is not used by the MemoryProxy class.
11108      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11109      * object into a block of Roo.data.Records.
11110      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11111      * The function must be passed <ul>
11112      * <li>The Record block object</li>
11113      * <li>The "arg" argument from the load function</li>
11114      * <li>A boolean success indicator</li>
11115      * </ul>
11116      * @param {Object} scope The scope in which to call the callback
11117      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11118      */
11119     load : function(params, reader, callback, scope, arg){
11120         params = params || {};
11121         var result;
11122         try {
11123             result = reader.readRecords(this.data);
11124         }catch(e){
11125             this.fireEvent("loadexception", this, arg, null, e);
11126             callback.call(scope, null, arg, false);
11127             return;
11128         }
11129         callback.call(scope, result, arg, true);
11130     },
11131     
11132     // private
11133     update : function(params, records){
11134         
11135     }
11136 });/*
11137  * Based on:
11138  * Ext JS Library 1.1.1
11139  * Copyright(c) 2006-2007, Ext JS, LLC.
11140  *
11141  * Originally Released Under LGPL - original licence link has changed is not relivant.
11142  *
11143  * Fork - LGPL
11144  * <script type="text/javascript">
11145  */
11146 /**
11147  * @class Roo.data.HttpProxy
11148  * @extends Roo.data.DataProxy
11149  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11150  * configured to reference a certain URL.<br><br>
11151  * <p>
11152  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11153  * from which the running page was served.<br><br>
11154  * <p>
11155  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11156  * <p>
11157  * Be aware that to enable the browser to parse an XML document, the server must set
11158  * the Content-Type header in the HTTP response to "text/xml".
11159  * @constructor
11160  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11161  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11162  * will be used to make the request.
11163  */
11164 Roo.data.HttpProxy = function(conn){
11165     Roo.data.HttpProxy.superclass.constructor.call(this);
11166     // is conn a conn config or a real conn?
11167     this.conn = conn;
11168     this.useAjax = !conn || !conn.events;
11169   
11170 };
11171
11172 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11173     // thse are take from connection...
11174     
11175     /**
11176      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11177      */
11178     /**
11179      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11180      * extra parameters to each request made by this object. (defaults to undefined)
11181      */
11182     /**
11183      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11184      *  to each request made by this object. (defaults to undefined)
11185      */
11186     /**
11187      * @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)
11188      */
11189     /**
11190      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11191      */
11192      /**
11193      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11194      * @type Boolean
11195      */
11196   
11197
11198     /**
11199      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11200      * @type Boolean
11201      */
11202     /**
11203      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11204      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11205      * a finer-grained basis than the DataProxy events.
11206      */
11207     getConnection : function(){
11208         return this.useAjax ? Roo.Ajax : this.conn;
11209     },
11210
11211     /**
11212      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11213      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11214      * process that block using the passed callback.
11215      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11216      * for the request to the remote server.
11217      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11218      * object into a block of Roo.data.Records.
11219      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11220      * The function must be passed <ul>
11221      * <li>The Record block object</li>
11222      * <li>The "arg" argument from the load function</li>
11223      * <li>A boolean success indicator</li>
11224      * </ul>
11225      * @param {Object} scope The scope in which to call the callback
11226      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11227      */
11228     load : function(params, reader, callback, scope, arg){
11229         if(this.fireEvent("beforeload", this, params) !== false){
11230             var  o = {
11231                 params : params || {},
11232                 request: {
11233                     callback : callback,
11234                     scope : scope,
11235                     arg : arg
11236                 },
11237                 reader: reader,
11238                 callback : this.loadResponse,
11239                 scope: this
11240             };
11241             if(this.useAjax){
11242                 Roo.applyIf(o, this.conn);
11243                 if(this.activeRequest){
11244                     Roo.Ajax.abort(this.activeRequest);
11245                 }
11246                 this.activeRequest = Roo.Ajax.request(o);
11247             }else{
11248                 this.conn.request(o);
11249             }
11250         }else{
11251             callback.call(scope||this, null, arg, false);
11252         }
11253     },
11254
11255     // private
11256     loadResponse : function(o, success, response){
11257         delete this.activeRequest;
11258         if(!success){
11259             this.fireEvent("loadexception", this, o, response);
11260             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11261             return;
11262         }
11263         var result;
11264         try {
11265             result = o.reader.read(response);
11266         }catch(e){
11267             this.fireEvent("loadexception", this, o, response, e);
11268             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11269             return;
11270         }
11271         
11272         this.fireEvent("load", this, o, o.request.arg);
11273         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11274     },
11275
11276     // private
11277     update : function(dataSet){
11278
11279     },
11280
11281     // private
11282     updateResponse : function(dataSet){
11283
11284     }
11285 });/*
11286  * Based on:
11287  * Ext JS Library 1.1.1
11288  * Copyright(c) 2006-2007, Ext JS, LLC.
11289  *
11290  * Originally Released Under LGPL - original licence link has changed is not relivant.
11291  *
11292  * Fork - LGPL
11293  * <script type="text/javascript">
11294  */
11295
11296 /**
11297  * @class Roo.data.ScriptTagProxy
11298  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11299  * other than the originating domain of the running page.<br><br>
11300  * <p>
11301  * <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
11302  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11303  * <p>
11304  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11305  * source code that is used as the source inside a &lt;script> tag.<br><br>
11306  * <p>
11307  * In order for the browser to process the returned data, the server must wrap the data object
11308  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11309  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11310  * depending on whether the callback name was passed:
11311  * <p>
11312  * <pre><code>
11313 boolean scriptTag = false;
11314 String cb = request.getParameter("callback");
11315 if (cb != null) {
11316     scriptTag = true;
11317     response.setContentType("text/javascript");
11318 } else {
11319     response.setContentType("application/x-json");
11320 }
11321 Writer out = response.getWriter();
11322 if (scriptTag) {
11323     out.write(cb + "(");
11324 }
11325 out.print(dataBlock.toJsonString());
11326 if (scriptTag) {
11327     out.write(");");
11328 }
11329 </pre></code>
11330  *
11331  * @constructor
11332  * @param {Object} config A configuration object.
11333  */
11334 Roo.data.ScriptTagProxy = function(config){
11335     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11336     Roo.apply(this, config);
11337     this.head = document.getElementsByTagName("head")[0];
11338 };
11339
11340 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11341
11342 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11343     /**
11344      * @cfg {String} url The URL from which to request the data object.
11345      */
11346     /**
11347      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11348      */
11349     timeout : 30000,
11350     /**
11351      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11352      * the server the name of the callback function set up by the load call to process the returned data object.
11353      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11354      * javascript output which calls this named function passing the data object as its only parameter.
11355      */
11356     callbackParam : "callback",
11357     /**
11358      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11359      * name to the request.
11360      */
11361     nocache : true,
11362
11363     /**
11364      * Load data from the configured URL, read the data object into
11365      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11366      * process that block using the passed callback.
11367      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11368      * for the request to the remote server.
11369      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11370      * object into a block of Roo.data.Records.
11371      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11372      * The function must be passed <ul>
11373      * <li>The Record block object</li>
11374      * <li>The "arg" argument from the load function</li>
11375      * <li>A boolean success indicator</li>
11376      * </ul>
11377      * @param {Object} scope The scope in which to call the callback
11378      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11379      */
11380     load : function(params, reader, callback, scope, arg){
11381         if(this.fireEvent("beforeload", this, params) !== false){
11382
11383             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11384
11385             var url = this.url;
11386             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11387             if(this.nocache){
11388                 url += "&_dc=" + (new Date().getTime());
11389             }
11390             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11391             var trans = {
11392                 id : transId,
11393                 cb : "stcCallback"+transId,
11394                 scriptId : "stcScript"+transId,
11395                 params : params,
11396                 arg : arg,
11397                 url : url,
11398                 callback : callback,
11399                 scope : scope,
11400                 reader : reader
11401             };
11402             var conn = this;
11403
11404             window[trans.cb] = function(o){
11405                 conn.handleResponse(o, trans);
11406             };
11407
11408             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11409
11410             if(this.autoAbort !== false){
11411                 this.abort();
11412             }
11413
11414             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11415
11416             var script = document.createElement("script");
11417             script.setAttribute("src", url);
11418             script.setAttribute("type", "text/javascript");
11419             script.setAttribute("id", trans.scriptId);
11420             this.head.appendChild(script);
11421
11422             this.trans = trans;
11423         }else{
11424             callback.call(scope||this, null, arg, false);
11425         }
11426     },
11427
11428     // private
11429     isLoading : function(){
11430         return this.trans ? true : false;
11431     },
11432
11433     /**
11434      * Abort the current server request.
11435      */
11436     abort : function(){
11437         if(this.isLoading()){
11438             this.destroyTrans(this.trans);
11439         }
11440     },
11441
11442     // private
11443     destroyTrans : function(trans, isLoaded){
11444         this.head.removeChild(document.getElementById(trans.scriptId));
11445         clearTimeout(trans.timeoutId);
11446         if(isLoaded){
11447             window[trans.cb] = undefined;
11448             try{
11449                 delete window[trans.cb];
11450             }catch(e){}
11451         }else{
11452             // if hasn't been loaded, wait for load to remove it to prevent script error
11453             window[trans.cb] = function(){
11454                 window[trans.cb] = undefined;
11455                 try{
11456                     delete window[trans.cb];
11457                 }catch(e){}
11458             };
11459         }
11460     },
11461
11462     // private
11463     handleResponse : function(o, trans){
11464         this.trans = false;
11465         this.destroyTrans(trans, true);
11466         var result;
11467         try {
11468             result = trans.reader.readRecords(o);
11469         }catch(e){
11470             this.fireEvent("loadexception", this, o, trans.arg, e);
11471             trans.callback.call(trans.scope||window, null, trans.arg, false);
11472             return;
11473         }
11474         this.fireEvent("load", this, o, trans.arg);
11475         trans.callback.call(trans.scope||window, result, trans.arg, true);
11476     },
11477
11478     // private
11479     handleFailure : function(trans){
11480         this.trans = false;
11481         this.destroyTrans(trans, false);
11482         this.fireEvent("loadexception", this, null, trans.arg);
11483         trans.callback.call(trans.scope||window, null, trans.arg, false);
11484     }
11485 });/*
11486  * Based on:
11487  * Ext JS Library 1.1.1
11488  * Copyright(c) 2006-2007, Ext JS, LLC.
11489  *
11490  * Originally Released Under LGPL - original licence link has changed is not relivant.
11491  *
11492  * Fork - LGPL
11493  * <script type="text/javascript">
11494  */
11495
11496 /**
11497  * @class Roo.data.JsonReader
11498  * @extends Roo.data.DataReader
11499  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11500  * based on mappings in a provided Roo.data.Record constructor.
11501  * 
11502  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11503  * in the reply previously. 
11504  * 
11505  * <p>
11506  * Example code:
11507  * <pre><code>
11508 var RecordDef = Roo.data.Record.create([
11509     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11510     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11511 ]);
11512 var myReader = new Roo.data.JsonReader({
11513     totalProperty: "results",    // The property which contains the total dataset size (optional)
11514     root: "rows",                // The property which contains an Array of row objects
11515     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11516 }, RecordDef);
11517 </code></pre>
11518  * <p>
11519  * This would consume a JSON file like this:
11520  * <pre><code>
11521 { 'results': 2, 'rows': [
11522     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11523     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11524 }
11525 </code></pre>
11526  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11527  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11528  * paged from the remote server.
11529  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11530  * @cfg {String} root name of the property which contains the Array of row objects.
11531  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11532  * @cfg {Array} fields Array of field definition objects
11533  * @constructor
11534  * Create a new JsonReader
11535  * @param {Object} meta Metadata configuration options
11536  * @param {Object} recordType Either an Array of field definition objects,
11537  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11538  */
11539 Roo.data.JsonReader = function(meta, recordType){
11540     
11541     meta = meta || {};
11542     // set some defaults:
11543     Roo.applyIf(meta, {
11544         totalProperty: 'total',
11545         successProperty : 'success',
11546         root : 'data',
11547         id : 'id'
11548     });
11549     
11550     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11551 };
11552 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11553     
11554     /**
11555      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11556      * Used by Store query builder to append _requestMeta to params.
11557      * 
11558      */
11559     metaFromRemote : false,
11560     /**
11561      * This method is only used by a DataProxy which has retrieved data from a remote server.
11562      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11563      * @return {Object} data A data block which is used by an Roo.data.Store object as
11564      * a cache of Roo.data.Records.
11565      */
11566     read : function(response){
11567         var json = response.responseText;
11568        
11569         var o = /* eval:var:o */ eval("("+json+")");
11570         if(!o) {
11571             throw {message: "JsonReader.read: Json object not found"};
11572         }
11573         
11574         if(o.metaData){
11575             
11576             delete this.ef;
11577             this.metaFromRemote = true;
11578             this.meta = o.metaData;
11579             this.recordType = Roo.data.Record.create(o.metaData.fields);
11580             this.onMetaChange(this.meta, this.recordType, o);
11581         }
11582         return this.readRecords(o);
11583     },
11584
11585     // private function a store will implement
11586     onMetaChange : function(meta, recordType, o){
11587
11588     },
11589
11590     /**
11591          * @ignore
11592          */
11593     simpleAccess: function(obj, subsc) {
11594         return obj[subsc];
11595     },
11596
11597         /**
11598          * @ignore
11599          */
11600     getJsonAccessor: function(){
11601         var re = /[\[\.]/;
11602         return function(expr) {
11603             try {
11604                 return(re.test(expr))
11605                     ? new Function("obj", "return obj." + expr)
11606                     : function(obj){
11607                         return obj[expr];
11608                     };
11609             } catch(e){}
11610             return Roo.emptyFn;
11611         };
11612     }(),
11613
11614     /**
11615      * Create a data block containing Roo.data.Records from an XML document.
11616      * @param {Object} o An object which contains an Array of row objects in the property specified
11617      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11618      * which contains the total size of the dataset.
11619      * @return {Object} data A data block which is used by an Roo.data.Store object as
11620      * a cache of Roo.data.Records.
11621      */
11622     readRecords : function(o){
11623         /**
11624          * After any data loads, the raw JSON data is available for further custom processing.
11625          * @type Object
11626          */
11627         this.o = o;
11628         var s = this.meta, Record = this.recordType,
11629             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11630
11631 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11632         if (!this.ef) {
11633             if(s.totalProperty) {
11634                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11635                 }
11636                 if(s.successProperty) {
11637                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11638                 }
11639                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11640                 if (s.id) {
11641                         var g = this.getJsonAccessor(s.id);
11642                         this.getId = function(rec) {
11643                                 var r = g(rec);  
11644                                 return (r === undefined || r === "") ? null : r;
11645                         };
11646                 } else {
11647                         this.getId = function(){return null;};
11648                 }
11649             this.ef = [];
11650             for(var jj = 0; jj < fl; jj++){
11651                 f = fi[jj];
11652                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11653                 this.ef[jj] = this.getJsonAccessor(map);
11654             }
11655         }
11656
11657         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11658         if(s.totalProperty){
11659             var vt = parseInt(this.getTotal(o), 10);
11660             if(!isNaN(vt)){
11661                 totalRecords = vt;
11662             }
11663         }
11664         if(s.successProperty){
11665             var vs = this.getSuccess(o);
11666             if(vs === false || vs === 'false'){
11667                 success = false;
11668             }
11669         }
11670         var records = [];
11671         for(var i = 0; i < c; i++){
11672                 var n = root[i];
11673             var values = {};
11674             var id = this.getId(n);
11675             for(var j = 0; j < fl; j++){
11676                 f = fi[j];
11677             var v = this.ef[j](n);
11678             if (!f.convert) {
11679                 Roo.log('missing convert for ' + f.name);
11680                 Roo.log(f);
11681                 continue;
11682             }
11683             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11684             }
11685             var record = new Record(values, id);
11686             record.json = n;
11687             records[i] = record;
11688         }
11689         return {
11690             raw : o,
11691             success : success,
11692             records : records,
11693             totalRecords : totalRecords
11694         };
11695     }
11696 });/*
11697  * Based on:
11698  * Ext JS Library 1.1.1
11699  * Copyright(c) 2006-2007, Ext JS, LLC.
11700  *
11701  * Originally Released Under LGPL - original licence link has changed is not relivant.
11702  *
11703  * Fork - LGPL
11704  * <script type="text/javascript">
11705  */
11706
11707 /**
11708  * @class Roo.data.ArrayReader
11709  * @extends Roo.data.DataReader
11710  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11711  * Each element of that Array represents a row of data fields. The
11712  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11713  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11714  * <p>
11715  * Example code:.
11716  * <pre><code>
11717 var RecordDef = Roo.data.Record.create([
11718     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11719     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11720 ]);
11721 var myReader = new Roo.data.ArrayReader({
11722     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11723 }, RecordDef);
11724 </code></pre>
11725  * <p>
11726  * This would consume an Array like this:
11727  * <pre><code>
11728 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11729   </code></pre>
11730  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11731  * @constructor
11732  * Create a new JsonReader
11733  * @param {Object} meta Metadata configuration options.
11734  * @param {Object} recordType Either an Array of field definition objects
11735  * as specified to {@link Roo.data.Record#create},
11736  * or an {@link Roo.data.Record} object
11737  * created using {@link Roo.data.Record#create}.
11738  */
11739 Roo.data.ArrayReader = function(meta, recordType){
11740     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11741 };
11742
11743 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11744     /**
11745      * Create a data block containing Roo.data.Records from an XML document.
11746      * @param {Object} o An Array of row objects which represents the dataset.
11747      * @return {Object} data A data block which is used by an Roo.data.Store object as
11748      * a cache of Roo.data.Records.
11749      */
11750     readRecords : function(o){
11751         var sid = this.meta ? this.meta.id : null;
11752         var recordType = this.recordType, fields = recordType.prototype.fields;
11753         var records = [];
11754         var root = o;
11755             for(var i = 0; i < root.length; i++){
11756                     var n = root[i];
11757                 var values = {};
11758                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11759                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11760                 var f = fields.items[j];
11761                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11762                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11763                 v = f.convert(v);
11764                 values[f.name] = v;
11765             }
11766                 var record = new recordType(values, id);
11767                 record.json = n;
11768                 records[records.length] = record;
11769             }
11770             return {
11771                 records : records,
11772                 totalRecords : records.length
11773             };
11774     }
11775 });/*
11776  * - LGPL
11777  * * 
11778  */
11779
11780 /**
11781  * @class Roo.bootstrap.ComboBox
11782  * @extends Roo.bootstrap.TriggerField
11783  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11784  * @cfg {Boolean} append (true|false) default false
11785  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11786  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11787  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11788  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11789  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11790  * @cfg {Boolean} animate default true
11791  * @cfg {Boolean} emptyResultText only for touch device
11792  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11793  * @constructor
11794  * Create a new ComboBox.
11795  * @param {Object} config Configuration options
11796  */
11797 Roo.bootstrap.ComboBox = function(config){
11798     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11799     this.addEvents({
11800         /**
11801          * @event expand
11802          * Fires when the dropdown list is expanded
11803              * @param {Roo.bootstrap.ComboBox} combo This combo box
11804              */
11805         'expand' : true,
11806         /**
11807          * @event collapse
11808          * Fires when the dropdown list is collapsed
11809              * @param {Roo.bootstrap.ComboBox} combo This combo box
11810              */
11811         'collapse' : true,
11812         /**
11813          * @event beforeselect
11814          * Fires before a list item is selected. Return false to cancel the selection.
11815              * @param {Roo.bootstrap.ComboBox} combo This combo box
11816              * @param {Roo.data.Record} record The data record returned from the underlying store
11817              * @param {Number} index The index of the selected item in the dropdown list
11818              */
11819         'beforeselect' : true,
11820         /**
11821          * @event select
11822          * Fires when a list item is selected
11823              * @param {Roo.bootstrap.ComboBox} combo This combo box
11824              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11825              * @param {Number} index The index of the selected item in the dropdown list
11826              */
11827         'select' : true,
11828         /**
11829          * @event beforequery
11830          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11831          * The event object passed has these properties:
11832              * @param {Roo.bootstrap.ComboBox} combo This combo box
11833              * @param {String} query The query
11834              * @param {Boolean} forceAll true to force "all" query
11835              * @param {Boolean} cancel true to cancel the query
11836              * @param {Object} e The query event object
11837              */
11838         'beforequery': true,
11839          /**
11840          * @event add
11841          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11842              * @param {Roo.bootstrap.ComboBox} combo This combo box
11843              */
11844         'add' : true,
11845         /**
11846          * @event edit
11847          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11848              * @param {Roo.bootstrap.ComboBox} combo This combo box
11849              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11850              */
11851         'edit' : true,
11852         /**
11853          * @event remove
11854          * Fires when the remove value from the combobox array
11855              * @param {Roo.bootstrap.ComboBox} combo This combo box
11856              */
11857         'remove' : true,
11858         /**
11859          * @event afterremove
11860          * Fires when the remove value from the combobox array
11861              * @param {Roo.bootstrap.ComboBox} combo This combo box
11862              */
11863         'afterremove' : true,
11864         /**
11865          * @event specialfilter
11866          * Fires when specialfilter
11867             * @param {Roo.bootstrap.ComboBox} combo This combo box
11868             */
11869         'specialfilter' : true,
11870         /**
11871          * @event tick
11872          * Fires when tick the element
11873             * @param {Roo.bootstrap.ComboBox} combo This combo box
11874             */
11875         'tick' : true,
11876         /**
11877          * @event touchviewdisplay
11878          * Fires when touch view require special display (default is using displayField)
11879             * @param {Roo.bootstrap.ComboBox} combo This combo box
11880             * @param {Object} cfg set html .
11881             */
11882         'touchviewdisplay' : true
11883         
11884     });
11885     
11886     this.item = [];
11887     this.tickItems = [];
11888     
11889     this.selectedIndex = -1;
11890     if(this.mode == 'local'){
11891         if(config.queryDelay === undefined){
11892             this.queryDelay = 10;
11893         }
11894         if(config.minChars === undefined){
11895             this.minChars = 0;
11896         }
11897     }
11898 };
11899
11900 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11901      
11902     /**
11903      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11904      * rendering into an Roo.Editor, defaults to false)
11905      */
11906     /**
11907      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11908      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11909      */
11910     /**
11911      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11912      */
11913     /**
11914      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11915      * the dropdown list (defaults to undefined, with no header element)
11916      */
11917
11918      /**
11919      * @cfg {String/Roo.Template} tpl The template to use to render the output
11920      */
11921      
11922      /**
11923      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11924      */
11925     listWidth: undefined,
11926     /**
11927      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11928      * mode = 'remote' or 'text' if mode = 'local')
11929      */
11930     displayField: undefined,
11931     
11932     /**
11933      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11934      * mode = 'remote' or 'value' if mode = 'local'). 
11935      * Note: use of a valueField requires the user make a selection
11936      * in order for a value to be mapped.
11937      */
11938     valueField: undefined,
11939     
11940     
11941     /**
11942      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11943      * field's data value (defaults to the underlying DOM element's name)
11944      */
11945     hiddenName: undefined,
11946     /**
11947      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11948      */
11949     listClass: '',
11950     /**
11951      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11952      */
11953     selectedClass: 'active',
11954     
11955     /**
11956      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11957      */
11958     shadow:'sides',
11959     /**
11960      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11961      * anchor positions (defaults to 'tl-bl')
11962      */
11963     listAlign: 'tl-bl?',
11964     /**
11965      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11966      */
11967     maxHeight: 300,
11968     /**
11969      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11970      * query specified by the allQuery config option (defaults to 'query')
11971      */
11972     triggerAction: 'query',
11973     /**
11974      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11975      * (defaults to 4, does not apply if editable = false)
11976      */
11977     minChars : 4,
11978     /**
11979      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11980      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11981      */
11982     typeAhead: false,
11983     /**
11984      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11985      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11986      */
11987     queryDelay: 500,
11988     /**
11989      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11990      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11991      */
11992     pageSize: 0,
11993     /**
11994      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11995      * when editable = true (defaults to false)
11996      */
11997     selectOnFocus:false,
11998     /**
11999      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12000      */
12001     queryParam: 'query',
12002     /**
12003      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12004      * when mode = 'remote' (defaults to 'Loading...')
12005      */
12006     loadingText: 'Loading...',
12007     /**
12008      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12009      */
12010     resizable: false,
12011     /**
12012      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12013      */
12014     handleHeight : 8,
12015     /**
12016      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12017      * traditional select (defaults to true)
12018      */
12019     editable: true,
12020     /**
12021      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12022      */
12023     allQuery: '',
12024     /**
12025      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12026      */
12027     mode: 'remote',
12028     /**
12029      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12030      * listWidth has a higher value)
12031      */
12032     minListWidth : 70,
12033     /**
12034      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12035      * allow the user to set arbitrary text into the field (defaults to false)
12036      */
12037     forceSelection:false,
12038     /**
12039      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12040      * if typeAhead = true (defaults to 250)
12041      */
12042     typeAheadDelay : 250,
12043     /**
12044      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12045      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12046      */
12047     valueNotFoundText : undefined,
12048     /**
12049      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12050      */
12051     blockFocus : false,
12052     
12053     /**
12054      * @cfg {Boolean} disableClear Disable showing of clear button.
12055      */
12056     disableClear : false,
12057     /**
12058      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12059      */
12060     alwaysQuery : false,
12061     
12062     /**
12063      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12064      */
12065     multiple : false,
12066     
12067     /**
12068      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12069      */
12070     invalidClass : "has-warning",
12071     
12072     /**
12073      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12074      */
12075     validClass : "has-success",
12076     
12077     /**
12078      * @cfg {Boolean} specialFilter (true|false) special filter default false
12079      */
12080     specialFilter : false,
12081     
12082     /**
12083      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12084      */
12085     mobileTouchView : true,
12086     
12087     //private
12088     addicon : false,
12089     editicon: false,
12090     
12091     page: 0,
12092     hasQuery: false,
12093     append: false,
12094     loadNext: false,
12095     autoFocus : true,
12096     tickable : false,
12097     btnPosition : 'right',
12098     triggerList : true,
12099     showToggleBtn : true,
12100     animate : true,
12101     emptyResultText: 'Empty',
12102     triggerText : 'Select',
12103     
12104     // element that contains real text value.. (when hidden is used..)
12105     
12106     getAutoCreate : function()
12107     {
12108         var cfg = false;
12109         
12110         /*
12111          * Touch Devices
12112          */
12113         
12114         if(Roo.isTouch && this.mobileTouchView){
12115             cfg = this.getAutoCreateTouchView();
12116             return cfg;;
12117         }
12118         
12119         /*
12120          *  Normal ComboBox
12121          */
12122         if(!this.tickable){
12123             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12124             return cfg;
12125         }
12126         
12127         /*
12128          *  ComboBox with tickable selections
12129          */
12130              
12131         var align = this.labelAlign || this.parentLabelAlign();
12132         
12133         cfg = {
12134             cls : 'form-group roo-combobox-tickable' //input-group
12135         };
12136         
12137         var buttons = {
12138             tag : 'div',
12139             cls : 'tickable-buttons',
12140             cn : [
12141                 {
12142                     tag : 'button',
12143                     type : 'button',
12144                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12145                     html : this.triggerText
12146                 },
12147                 {
12148                     tag : 'button',
12149                     type : 'button',
12150                     name : 'ok',
12151                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12152                     html : 'Done'
12153                 },
12154                 {
12155                     tag : 'button',
12156                     type : 'button',
12157                     name : 'cancel',
12158                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12159                     html : 'Cancel'
12160                 }
12161             ]
12162         };
12163         
12164         if(this.editable){
12165             buttons.cn.unshift({
12166                 tag: 'input',
12167                 cls: 'roo-select2-search-field-input'
12168             });
12169         }
12170         
12171         var _this = this;
12172         
12173         Roo.each(buttons.cn, function(c){
12174             if (_this.size) {
12175                 c.cls += ' btn-' + _this.size;
12176             }
12177
12178             if (_this.disabled) {
12179                 c.disabled = true;
12180             }
12181         });
12182         
12183         var box = {
12184             tag: 'div',
12185             cn: [
12186                 {
12187                     tag: 'input',
12188                     type : 'hidden',
12189                     cls: 'form-hidden-field'
12190                 },
12191                 {
12192                     tag: 'ul',
12193                     cls: 'roo-select2-choices',
12194                     cn:[
12195                         {
12196                             tag: 'li',
12197                             cls: 'roo-select2-search-field',
12198                             cn: [
12199
12200                                 buttons
12201                             ]
12202                         }
12203                     ]
12204                 }
12205             ]
12206         };
12207         
12208         var combobox = {
12209             cls: 'roo-select2-container input-group roo-select2-container-multi',
12210             cn: [
12211                 box
12212 //                {
12213 //                    tag: 'ul',
12214 //                    cls: 'typeahead typeahead-long dropdown-menu',
12215 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12216 //                }
12217             ]
12218         };
12219         
12220         if(this.hasFeedback && !this.allowBlank){
12221             
12222             var feedback = {
12223                 tag: 'span',
12224                 cls: 'glyphicon form-control-feedback'
12225             };
12226
12227             combobox.cn.push(feedback);
12228         }
12229         
12230         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12231             
12232 //                Roo.log("left and has label");
12233                 cfg.cn = [
12234                     
12235                     {
12236                         tag: 'label',
12237                         'for' :  id,
12238                         cls : 'control-label col-sm-' + this.labelWidth,
12239                         html : this.fieldLabel
12240                         
12241                     },
12242                     {
12243                         cls : "col-sm-" + (12 - this.labelWidth), 
12244                         cn: [
12245                             combobox
12246                         ]
12247                     }
12248                     
12249                 ];
12250         } else if ( this.fieldLabel.length) {
12251 //                Roo.log(" label");
12252                  cfg.cn = [
12253                    
12254                     {
12255                         tag: 'label',
12256                         //cls : 'input-group-addon',
12257                         html : this.fieldLabel
12258                         
12259                     },
12260                     
12261                     combobox
12262                     
12263                 ];
12264
12265         } else {
12266             
12267 //                Roo.log(" no label && no align");
12268                 cfg = combobox
12269                      
12270                 
12271         }
12272          
12273         var settings=this;
12274         ['xs','sm','md','lg'].map(function(size){
12275             if (settings[size]) {
12276                 cfg.cls += ' col-' + size + '-' + settings[size];
12277             }
12278         });
12279         
12280         return cfg;
12281         
12282     },
12283     
12284     _initEventsCalled : false,
12285     
12286     // private
12287     initEvents: function()
12288     {
12289         
12290         if (this._initEventsCalled) { // as we call render... prevent looping...
12291             return;
12292         }
12293         this._initEventsCalled = true;
12294         
12295         if (!this.store) {
12296             throw "can not find store for combo";
12297         }
12298         
12299         this.store = Roo.factory(this.store, Roo.data);
12300         
12301         // if we are building from html. then this element is so complex, that we can not really
12302         // use the rendered HTML.
12303         // so we have to trash and replace the previous code.
12304         if (Roo.XComponent.build_from_html) {
12305             
12306             // remove this element....
12307             var e = this.el.dom, k=0;
12308             while (e ) { e = e.previousSibling;  ++k;}
12309
12310             this.el.remove();
12311             
12312             this.el=false;
12313             this.rendered = false;
12314             
12315             this.render(this.parent().getChildContainer(true), k);
12316             
12317             
12318             
12319         }
12320         
12321         
12322         /*
12323          * Touch Devices
12324          */
12325         
12326         if(Roo.isTouch && this.mobileTouchView){
12327             this.initTouchView();
12328             return;
12329         }
12330         
12331         if(this.tickable){
12332             this.initTickableEvents();
12333             return;
12334         }
12335         
12336         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12337         
12338         if(this.hiddenName){
12339             
12340             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12341             
12342             this.hiddenField.dom.value =
12343                 this.hiddenValue !== undefined ? this.hiddenValue :
12344                 this.value !== undefined ? this.value : '';
12345
12346             // prevent input submission
12347             this.el.dom.removeAttribute('name');
12348             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12349              
12350              
12351         }
12352         //if(Roo.isGecko){
12353         //    this.el.dom.setAttribute('autocomplete', 'off');
12354         //}
12355         
12356         var cls = 'x-combo-list';
12357         
12358         //this.list = new Roo.Layer({
12359         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12360         //});
12361         
12362         var _this = this;
12363         
12364         (function(){
12365             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12366             _this.list.setWidth(lw);
12367         }).defer(100);
12368         
12369         this.list.on('mouseover', this.onViewOver, this);
12370         this.list.on('mousemove', this.onViewMove, this);
12371         
12372         this.list.on('scroll', this.onViewScroll, this);
12373         
12374         /*
12375         this.list.swallowEvent('mousewheel');
12376         this.assetHeight = 0;
12377
12378         if(this.title){
12379             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12380             this.assetHeight += this.header.getHeight();
12381         }
12382
12383         this.innerList = this.list.createChild({cls:cls+'-inner'});
12384         this.innerList.on('mouseover', this.onViewOver, this);
12385         this.innerList.on('mousemove', this.onViewMove, this);
12386         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12387         
12388         if(this.allowBlank && !this.pageSize && !this.disableClear){
12389             this.footer = this.list.createChild({cls:cls+'-ft'});
12390             this.pageTb = new Roo.Toolbar(this.footer);
12391            
12392         }
12393         if(this.pageSize){
12394             this.footer = this.list.createChild({cls:cls+'-ft'});
12395             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12396                     {pageSize: this.pageSize});
12397             
12398         }
12399         
12400         if (this.pageTb && this.allowBlank && !this.disableClear) {
12401             var _this = this;
12402             this.pageTb.add(new Roo.Toolbar.Fill(), {
12403                 cls: 'x-btn-icon x-btn-clear',
12404                 text: '&#160;',
12405                 handler: function()
12406                 {
12407                     _this.collapse();
12408                     _this.clearValue();
12409                     _this.onSelect(false, -1);
12410                 }
12411             });
12412         }
12413         if (this.footer) {
12414             this.assetHeight += this.footer.getHeight();
12415         }
12416         */
12417             
12418         if(!this.tpl){
12419             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12420         }
12421
12422         this.view = new Roo.View(this.list, this.tpl, {
12423             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12424         });
12425         //this.view.wrapEl.setDisplayed(false);
12426         this.view.on('click', this.onViewClick, this);
12427         
12428         
12429         
12430         this.store.on('beforeload', this.onBeforeLoad, this);
12431         this.store.on('load', this.onLoad, this);
12432         this.store.on('loadexception', this.onLoadException, this);
12433         /*
12434         if(this.resizable){
12435             this.resizer = new Roo.Resizable(this.list,  {
12436                pinned:true, handles:'se'
12437             });
12438             this.resizer.on('resize', function(r, w, h){
12439                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12440                 this.listWidth = w;
12441                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12442                 this.restrictHeight();
12443             }, this);
12444             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12445         }
12446         */
12447         if(!this.editable){
12448             this.editable = true;
12449             this.setEditable(false);
12450         }
12451         
12452         /*
12453         
12454         if (typeof(this.events.add.listeners) != 'undefined') {
12455             
12456             this.addicon = this.wrap.createChild(
12457                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12458        
12459             this.addicon.on('click', function(e) {
12460                 this.fireEvent('add', this);
12461             }, this);
12462         }
12463         if (typeof(this.events.edit.listeners) != 'undefined') {
12464             
12465             this.editicon = this.wrap.createChild(
12466                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12467             if (this.addicon) {
12468                 this.editicon.setStyle('margin-left', '40px');
12469             }
12470             this.editicon.on('click', function(e) {
12471                 
12472                 // we fire even  if inothing is selected..
12473                 this.fireEvent('edit', this, this.lastData );
12474                 
12475             }, this);
12476         }
12477         */
12478         
12479         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12480             "up" : function(e){
12481                 this.inKeyMode = true;
12482                 this.selectPrev();
12483             },
12484
12485             "down" : function(e){
12486                 if(!this.isExpanded()){
12487                     this.onTriggerClick();
12488                 }else{
12489                     this.inKeyMode = true;
12490                     this.selectNext();
12491                 }
12492             },
12493
12494             "enter" : function(e){
12495 //                this.onViewClick();
12496                 //return true;
12497                 this.collapse();
12498                 
12499                 if(this.fireEvent("specialkey", this, e)){
12500                     this.onViewClick(false);
12501                 }
12502                 
12503                 return true;
12504             },
12505
12506             "esc" : function(e){
12507                 this.collapse();
12508             },
12509
12510             "tab" : function(e){
12511                 this.collapse();
12512                 
12513                 if(this.fireEvent("specialkey", this, e)){
12514                     this.onViewClick(false);
12515                 }
12516                 
12517                 return true;
12518             },
12519
12520             scope : this,
12521
12522             doRelay : function(foo, bar, hname){
12523                 if(hname == 'down' || this.scope.isExpanded()){
12524                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12525                 }
12526                 return true;
12527             },
12528
12529             forceKeyDown: true
12530         });
12531         
12532         
12533         this.queryDelay = Math.max(this.queryDelay || 10,
12534                 this.mode == 'local' ? 10 : 250);
12535         
12536         
12537         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12538         
12539         if(this.typeAhead){
12540             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12541         }
12542         if(this.editable !== false){
12543             this.inputEl().on("keyup", this.onKeyUp, this);
12544         }
12545         if(this.forceSelection){
12546             this.inputEl().on('blur', this.doForce, this);
12547         }
12548         
12549         if(this.multiple){
12550             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12551             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12552         }
12553     },
12554     
12555     initTickableEvents: function()
12556     {   
12557         this.createList();
12558         
12559         if(this.hiddenName){
12560             
12561             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12562             
12563             this.hiddenField.dom.value =
12564                 this.hiddenValue !== undefined ? this.hiddenValue :
12565                 this.value !== undefined ? this.value : '';
12566
12567             // prevent input submission
12568             this.el.dom.removeAttribute('name');
12569             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12570              
12571              
12572         }
12573         
12574 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12575         
12576         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12577         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12578         if(this.triggerList){
12579             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12580         }
12581          
12582         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12583         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12584         
12585         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12586         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12587         
12588         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12589         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12590         
12591         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12592         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12593         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12594         
12595         this.okBtn.hide();
12596         this.cancelBtn.hide();
12597         
12598         var _this = this;
12599         
12600         (function(){
12601             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12602             _this.list.setWidth(lw);
12603         }).defer(100);
12604         
12605         this.list.on('mouseover', this.onViewOver, this);
12606         this.list.on('mousemove', this.onViewMove, this);
12607         
12608         this.list.on('scroll', this.onViewScroll, this);
12609         
12610         if(!this.tpl){
12611             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>';
12612         }
12613
12614         this.view = new Roo.View(this.list, this.tpl, {
12615             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12616         });
12617         
12618         //this.view.wrapEl.setDisplayed(false);
12619         this.view.on('click', this.onViewClick, this);
12620         
12621         
12622         
12623         this.store.on('beforeload', this.onBeforeLoad, this);
12624         this.store.on('load', this.onLoad, this);
12625         this.store.on('loadexception', this.onLoadException, this);
12626         
12627         if(this.editable){
12628             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12629                 "up" : function(e){
12630                     this.inKeyMode = true;
12631                     this.selectPrev();
12632                 },
12633
12634                 "down" : function(e){
12635                     this.inKeyMode = true;
12636                     this.selectNext();
12637                 },
12638
12639                 "enter" : function(e){
12640                     if(this.fireEvent("specialkey", this, e)){
12641                         this.onViewClick(false);
12642                     }
12643                     
12644                     return true;
12645                 },
12646
12647                 "esc" : function(e){
12648                     this.onTickableFooterButtonClick(e, false, false);
12649                 },
12650
12651                 "tab" : function(e){
12652                     this.fireEvent("specialkey", this, e);
12653                     
12654                     this.onTickableFooterButtonClick(e, false, false);
12655                     
12656                     return true;
12657                 },
12658
12659                 scope : this,
12660
12661                 doRelay : function(e, fn, key){
12662                     if(this.scope.isExpanded()){
12663                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12664                     }
12665                     return true;
12666                 },
12667
12668                 forceKeyDown: true
12669             });
12670         }
12671         
12672         this.queryDelay = Math.max(this.queryDelay || 10,
12673                 this.mode == 'local' ? 10 : 250);
12674         
12675         
12676         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12677         
12678         if(this.typeAhead){
12679             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12680         }
12681         
12682         if(this.editable !== false){
12683             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12684         }
12685         
12686     },
12687
12688     onDestroy : function(){
12689         if(this.view){
12690             this.view.setStore(null);
12691             this.view.el.removeAllListeners();
12692             this.view.el.remove();
12693             this.view.purgeListeners();
12694         }
12695         if(this.list){
12696             this.list.dom.innerHTML  = '';
12697         }
12698         
12699         if(this.store){
12700             this.store.un('beforeload', this.onBeforeLoad, this);
12701             this.store.un('load', this.onLoad, this);
12702             this.store.un('loadexception', this.onLoadException, this);
12703         }
12704         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12705     },
12706
12707     // private
12708     fireKey : function(e){
12709         if(e.isNavKeyPress() && !this.list.isVisible()){
12710             this.fireEvent("specialkey", this, e);
12711         }
12712     },
12713
12714     // private
12715     onResize: function(w, h){
12716 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12717 //        
12718 //        if(typeof w != 'number'){
12719 //            // we do not handle it!?!?
12720 //            return;
12721 //        }
12722 //        var tw = this.trigger.getWidth();
12723 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12724 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12725 //        var x = w - tw;
12726 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12727 //            
12728 //        //this.trigger.setStyle('left', x+'px');
12729 //        
12730 //        if(this.list && this.listWidth === undefined){
12731 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12732 //            this.list.setWidth(lw);
12733 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12734 //        }
12735         
12736     
12737         
12738     },
12739
12740     /**
12741      * Allow or prevent the user from directly editing the field text.  If false is passed,
12742      * the user will only be able to select from the items defined in the dropdown list.  This method
12743      * is the runtime equivalent of setting the 'editable' config option at config time.
12744      * @param {Boolean} value True to allow the user to directly edit the field text
12745      */
12746     setEditable : function(value){
12747         if(value == this.editable){
12748             return;
12749         }
12750         this.editable = value;
12751         if(!value){
12752             this.inputEl().dom.setAttribute('readOnly', true);
12753             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12754             this.inputEl().addClass('x-combo-noedit');
12755         }else{
12756             this.inputEl().dom.setAttribute('readOnly', false);
12757             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12758             this.inputEl().removeClass('x-combo-noedit');
12759         }
12760     },
12761
12762     // private
12763     
12764     onBeforeLoad : function(combo,opts){
12765         if(!this.hasFocus){
12766             return;
12767         }
12768          if (!opts.add) {
12769             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12770          }
12771         this.restrictHeight();
12772         this.selectedIndex = -1;
12773     },
12774
12775     // private
12776     onLoad : function(){
12777         
12778         this.hasQuery = false;
12779         
12780         if(!this.hasFocus){
12781             return;
12782         }
12783         
12784         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12785             this.loading.hide();
12786         }
12787              
12788         if(this.store.getCount() > 0){
12789             this.expand();
12790             this.restrictHeight();
12791             if(this.lastQuery == this.allQuery){
12792                 if(this.editable && !this.tickable){
12793                     this.inputEl().dom.select();
12794                 }
12795                 
12796                 if(
12797                     !this.selectByValue(this.value, true) &&
12798                     this.autoFocus && 
12799                     (
12800                         !this.store.lastOptions ||
12801                         typeof(this.store.lastOptions.add) == 'undefined' || 
12802                         this.store.lastOptions.add != true
12803                     )
12804                 ){
12805                     this.select(0, true);
12806                 }
12807             }else{
12808                 if(this.autoFocus){
12809                     this.selectNext();
12810                 }
12811                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12812                     this.taTask.delay(this.typeAheadDelay);
12813                 }
12814             }
12815         }else{
12816             this.onEmptyResults();
12817         }
12818         
12819         //this.el.focus();
12820     },
12821     // private
12822     onLoadException : function()
12823     {
12824         this.hasQuery = false;
12825         
12826         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12827             this.loading.hide();
12828         }
12829         
12830         if(this.tickable && this.editable){
12831             return;
12832         }
12833         
12834         this.collapse();
12835         // only causes errors at present
12836         //Roo.log(this.store.reader.jsonData);
12837         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12838             // fixme
12839             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12840         //}
12841         
12842         
12843     },
12844     // private
12845     onTypeAhead : function(){
12846         if(this.store.getCount() > 0){
12847             var r = this.store.getAt(0);
12848             var newValue = r.data[this.displayField];
12849             var len = newValue.length;
12850             var selStart = this.getRawValue().length;
12851             
12852             if(selStart != len){
12853                 this.setRawValue(newValue);
12854                 this.selectText(selStart, newValue.length);
12855             }
12856         }
12857     },
12858
12859     // private
12860     onSelect : function(record, index){
12861         
12862         if(this.fireEvent('beforeselect', this, record, index) !== false){
12863         
12864             this.setFromData(index > -1 ? record.data : false);
12865             
12866             this.collapse();
12867             this.fireEvent('select', this, record, index);
12868         }
12869     },
12870
12871     /**
12872      * Returns the currently selected field value or empty string if no value is set.
12873      * @return {String} value The selected value
12874      */
12875     getValue : function(){
12876         
12877         if(this.multiple){
12878             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12879         }
12880         
12881         if(this.valueField){
12882             return typeof this.value != 'undefined' ? this.value : '';
12883         }else{
12884             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12885         }
12886     },
12887
12888     /**
12889      * Clears any text/value currently set in the field
12890      */
12891     clearValue : function(){
12892         if(this.hiddenField){
12893             this.hiddenField.dom.value = '';
12894         }
12895         this.value = '';
12896         this.setRawValue('');
12897         this.lastSelectionText = '';
12898         this.lastData = false;
12899         
12900         var close = this.closeTriggerEl();
12901         
12902         if(close){
12903             close.hide();
12904         }
12905         
12906     },
12907
12908     /**
12909      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12910      * will be displayed in the field.  If the value does not match the data value of an existing item,
12911      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12912      * Otherwise the field will be blank (although the value will still be set).
12913      * @param {String} value The value to match
12914      */
12915     setValue : function(v){
12916         if(this.multiple){
12917             this.syncValue();
12918             return;
12919         }
12920         
12921         var text = v;
12922         if(this.valueField){
12923             var r = this.findRecord(this.valueField, v);
12924             if(r){
12925                 text = r.data[this.displayField];
12926             }else if(this.valueNotFoundText !== undefined){
12927                 text = this.valueNotFoundText;
12928             }
12929         }
12930         this.lastSelectionText = text;
12931         if(this.hiddenField){
12932             this.hiddenField.dom.value = v;
12933         }
12934         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12935         this.value = v;
12936         
12937         var close = this.closeTriggerEl();
12938         
12939         if(close){
12940             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12941         }
12942     },
12943     /**
12944      * @property {Object} the last set data for the element
12945      */
12946     
12947     lastData : false,
12948     /**
12949      * Sets the value of the field based on a object which is related to the record format for the store.
12950      * @param {Object} value the value to set as. or false on reset?
12951      */
12952     setFromData : function(o){
12953         
12954         if(this.multiple){
12955             this.addItem(o);
12956             return;
12957         }
12958             
12959         var dv = ''; // display value
12960         var vv = ''; // value value..
12961         this.lastData = o;
12962         if (this.displayField) {
12963             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12964         } else {
12965             // this is an error condition!!!
12966             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12967         }
12968         
12969         if(this.valueField){
12970             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12971         }
12972         
12973         var close = this.closeTriggerEl();
12974         
12975         if(close){
12976             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12977         }
12978         
12979         if(this.hiddenField){
12980             this.hiddenField.dom.value = vv;
12981             
12982             this.lastSelectionText = dv;
12983             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12984             this.value = vv;
12985             return;
12986         }
12987         // no hidden field.. - we store the value in 'value', but still display
12988         // display field!!!!
12989         this.lastSelectionText = dv;
12990         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12991         this.value = vv;
12992         
12993         
12994         
12995     },
12996     // private
12997     reset : function(){
12998         // overridden so that last data is reset..
12999         
13000         if(this.multiple){
13001             this.clearItem();
13002             return;
13003         }
13004         
13005         this.setValue(this.originalValue);
13006         this.clearInvalid();
13007         this.lastData = false;
13008         if (this.view) {
13009             this.view.clearSelections();
13010         }
13011     },
13012     // private
13013     findRecord : function(prop, value){
13014         var record;
13015         if(this.store.getCount() > 0){
13016             this.store.each(function(r){
13017                 if(r.data[prop] == value){
13018                     record = r;
13019                     return false;
13020                 }
13021                 return true;
13022             });
13023         }
13024         return record;
13025     },
13026     
13027     getName: function()
13028     {
13029         // returns hidden if it's set..
13030         if (!this.rendered) {return ''};
13031         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13032         
13033     },
13034     // private
13035     onViewMove : function(e, t){
13036         this.inKeyMode = false;
13037     },
13038
13039     // private
13040     onViewOver : function(e, t){
13041         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13042             return;
13043         }
13044         var item = this.view.findItemFromChild(t);
13045         
13046         if(item){
13047             var index = this.view.indexOf(item);
13048             this.select(index, false);
13049         }
13050     },
13051
13052     // private
13053     onViewClick : function(view, doFocus, el, e)
13054     {
13055         var index = this.view.getSelectedIndexes()[0];
13056         
13057         var r = this.store.getAt(index);
13058         
13059         if(this.tickable){
13060             
13061             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13062                 return;
13063             }
13064             
13065             var rm = false;
13066             var _this = this;
13067             
13068             Roo.each(this.tickItems, function(v,k){
13069                 
13070                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13071                     Roo.log(v);
13072                     _this.tickItems.splice(k, 1);
13073                     
13074                     if(typeof(e) == 'undefined' && view == false){
13075                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13076                     }
13077                     
13078                     rm = true;
13079                     return;
13080                 }
13081             });
13082             
13083             if(rm){
13084                 return;
13085             }
13086             
13087             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13088                 this.tickItems.push(r.data);
13089             }
13090             
13091             if(typeof(e) == 'undefined' && view == false){
13092                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13093             }
13094                     
13095             return;
13096         }
13097         
13098         if(r){
13099             this.onSelect(r, index);
13100         }
13101         if(doFocus !== false && !this.blockFocus){
13102             this.inputEl().focus();
13103         }
13104     },
13105
13106     // private
13107     restrictHeight : function(){
13108         //this.innerList.dom.style.height = '';
13109         //var inner = this.innerList.dom;
13110         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13111         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13112         //this.list.beginUpdate();
13113         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13114         this.list.alignTo(this.inputEl(), this.listAlign);
13115         this.list.alignTo(this.inputEl(), this.listAlign);
13116         //this.list.endUpdate();
13117     },
13118
13119     // private
13120     onEmptyResults : function(){
13121         
13122         if(this.tickable && this.editable){
13123             this.restrictHeight();
13124             return;
13125         }
13126         
13127         this.collapse();
13128     },
13129
13130     /**
13131      * Returns true if the dropdown list is expanded, else false.
13132      */
13133     isExpanded : function(){
13134         return this.list.isVisible();
13135     },
13136
13137     /**
13138      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13139      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13140      * @param {String} value The data value of the item to select
13141      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13142      * selected item if it is not currently in view (defaults to true)
13143      * @return {Boolean} True if the value matched an item in the list, else false
13144      */
13145     selectByValue : function(v, scrollIntoView){
13146         if(v !== undefined && v !== null){
13147             var r = this.findRecord(this.valueField || this.displayField, v);
13148             if(r){
13149                 this.select(this.store.indexOf(r), scrollIntoView);
13150                 return true;
13151             }
13152         }
13153         return false;
13154     },
13155
13156     /**
13157      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13158      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13159      * @param {Number} index The zero-based index of the list item to select
13160      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13161      * selected item if it is not currently in view (defaults to true)
13162      */
13163     select : function(index, scrollIntoView){
13164         this.selectedIndex = index;
13165         this.view.select(index);
13166         if(scrollIntoView !== false){
13167             var el = this.view.getNode(index);
13168             /*
13169              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13170              */
13171             if(el){
13172                 this.list.scrollChildIntoView(el, false);
13173             }
13174         }
13175     },
13176
13177     // private
13178     selectNext : function(){
13179         var ct = this.store.getCount();
13180         if(ct > 0){
13181             if(this.selectedIndex == -1){
13182                 this.select(0);
13183             }else if(this.selectedIndex < ct-1){
13184                 this.select(this.selectedIndex+1);
13185             }
13186         }
13187     },
13188
13189     // private
13190     selectPrev : function(){
13191         var ct = this.store.getCount();
13192         if(ct > 0){
13193             if(this.selectedIndex == -1){
13194                 this.select(0);
13195             }else if(this.selectedIndex != 0){
13196                 this.select(this.selectedIndex-1);
13197             }
13198         }
13199     },
13200
13201     // private
13202     onKeyUp : function(e){
13203         if(this.editable !== false && !e.isSpecialKey()){
13204             this.lastKey = e.getKey();
13205             this.dqTask.delay(this.queryDelay);
13206         }
13207     },
13208
13209     // private
13210     validateBlur : function(){
13211         return !this.list || !this.list.isVisible();   
13212     },
13213
13214     // private
13215     initQuery : function(){
13216         
13217         var v = this.getRawValue();
13218         
13219         if(this.tickable && this.editable){
13220             v = this.tickableInputEl().getValue();
13221         }
13222         
13223         this.doQuery(v);
13224     },
13225
13226     // private
13227     doForce : function(){
13228         if(this.inputEl().dom.value.length > 0){
13229             this.inputEl().dom.value =
13230                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13231              
13232         }
13233     },
13234
13235     /**
13236      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13237      * query allowing the query action to be canceled if needed.
13238      * @param {String} query The SQL query to execute
13239      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13240      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13241      * saved in the current store (defaults to false)
13242      */
13243     doQuery : function(q, forceAll){
13244         
13245         if(q === undefined || q === null){
13246             q = '';
13247         }
13248         var qe = {
13249             query: q,
13250             forceAll: forceAll,
13251             combo: this,
13252             cancel:false
13253         };
13254         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13255             return false;
13256         }
13257         q = qe.query;
13258         
13259         forceAll = qe.forceAll;
13260         if(forceAll === true || (q.length >= this.minChars)){
13261             
13262             this.hasQuery = true;
13263             
13264             if(this.lastQuery != q || this.alwaysQuery){
13265                 this.lastQuery = q;
13266                 if(this.mode == 'local'){
13267                     this.selectedIndex = -1;
13268                     if(forceAll){
13269                         this.store.clearFilter();
13270                     }else{
13271                         
13272                         if(this.specialFilter){
13273                             this.fireEvent('specialfilter', this);
13274                             this.onLoad();
13275                             return;
13276                         }
13277                         
13278                         this.store.filter(this.displayField, q);
13279                     }
13280                     
13281                     this.store.fireEvent("datachanged", this.store);
13282                     
13283                     this.onLoad();
13284                     
13285                     
13286                 }else{
13287                     
13288                     this.store.baseParams[this.queryParam] = q;
13289                     
13290                     var options = {params : this.getParams(q)};
13291                     
13292                     if(this.loadNext){
13293                         options.add = true;
13294                         options.params.start = this.page * this.pageSize;
13295                     }
13296                     
13297                     this.store.load(options);
13298                     
13299                     /*
13300                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13301                      *  we should expand the list on onLoad
13302                      *  so command out it
13303                      */
13304 //                    this.expand();
13305                 }
13306             }else{
13307                 this.selectedIndex = -1;
13308                 this.onLoad();   
13309             }
13310         }
13311         
13312         this.loadNext = false;
13313     },
13314     
13315     // private
13316     getParams : function(q){
13317         var p = {};
13318         //p[this.queryParam] = q;
13319         
13320         if(this.pageSize){
13321             p.start = 0;
13322             p.limit = this.pageSize;
13323         }
13324         return p;
13325     },
13326
13327     /**
13328      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13329      */
13330     collapse : function(){
13331         if(!this.isExpanded()){
13332             return;
13333         }
13334         
13335         this.list.hide();
13336         
13337         if(this.tickable){
13338             this.hasFocus = false;
13339             this.okBtn.hide();
13340             this.cancelBtn.hide();
13341             this.trigger.show();
13342             
13343             if(this.editable){
13344                 this.tickableInputEl().dom.value = '';
13345                 this.tickableInputEl().blur();
13346             }
13347             
13348         }
13349         
13350         Roo.get(document).un('mousedown', this.collapseIf, this);
13351         Roo.get(document).un('mousewheel', this.collapseIf, this);
13352         if (!this.editable) {
13353             Roo.get(document).un('keydown', this.listKeyPress, this);
13354         }
13355         this.fireEvent('collapse', this);
13356     },
13357
13358     // private
13359     collapseIf : function(e){
13360         var in_combo  = e.within(this.el);
13361         var in_list =  e.within(this.list);
13362         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13363         
13364         if (in_combo || in_list || is_list) {
13365             //e.stopPropagation();
13366             return;
13367         }
13368         
13369         if(this.tickable){
13370             this.onTickableFooterButtonClick(e, false, false);
13371         }
13372
13373         this.collapse();
13374         
13375     },
13376
13377     /**
13378      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13379      */
13380     expand : function(){
13381        
13382         if(this.isExpanded() || !this.hasFocus){
13383             return;
13384         }
13385         
13386         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13387         this.list.setWidth(lw);
13388         
13389         
13390          Roo.log('expand');
13391         
13392         this.list.show();
13393         
13394         this.restrictHeight();
13395         
13396         if(this.tickable){
13397             
13398             this.tickItems = Roo.apply([], this.item);
13399             
13400             this.okBtn.show();
13401             this.cancelBtn.show();
13402             this.trigger.hide();
13403             
13404             if(this.editable){
13405                 this.tickableInputEl().focus();
13406             }
13407             
13408         }
13409         
13410         Roo.get(document).on('mousedown', this.collapseIf, this);
13411         Roo.get(document).on('mousewheel', this.collapseIf, this);
13412         if (!this.editable) {
13413             Roo.get(document).on('keydown', this.listKeyPress, this);
13414         }
13415         
13416         this.fireEvent('expand', this);
13417     },
13418
13419     // private
13420     // Implements the default empty TriggerField.onTriggerClick function
13421     onTriggerClick : function(e)
13422     {
13423         Roo.log('trigger click');
13424         
13425         if(this.disabled || !this.triggerList){
13426             return;
13427         }
13428         
13429         this.page = 0;
13430         this.loadNext = false;
13431         
13432         if(this.isExpanded()){
13433             this.collapse();
13434             if (!this.blockFocus) {
13435                 this.inputEl().focus();
13436             }
13437             
13438         }else {
13439             this.hasFocus = true;
13440             if(this.triggerAction == 'all') {
13441                 this.doQuery(this.allQuery, true);
13442             } else {
13443                 this.doQuery(this.getRawValue());
13444             }
13445             if (!this.blockFocus) {
13446                 this.inputEl().focus();
13447             }
13448         }
13449     },
13450     
13451     onTickableTriggerClick : function(e)
13452     {
13453         if(this.disabled){
13454             return;
13455         }
13456         
13457         this.page = 0;
13458         this.loadNext = false;
13459         this.hasFocus = true;
13460         
13461         if(this.triggerAction == 'all') {
13462             this.doQuery(this.allQuery, true);
13463         } else {
13464             this.doQuery(this.getRawValue());
13465         }
13466     },
13467     
13468     onSearchFieldClick : function(e)
13469     {
13470         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13471             this.onTickableFooterButtonClick(e, false, false);
13472             return;
13473         }
13474         
13475         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13476             return;
13477         }
13478         
13479         this.page = 0;
13480         this.loadNext = false;
13481         this.hasFocus = true;
13482         
13483         if(this.triggerAction == 'all') {
13484             this.doQuery(this.allQuery, true);
13485         } else {
13486             this.doQuery(this.getRawValue());
13487         }
13488     },
13489     
13490     listKeyPress : function(e)
13491     {
13492         //Roo.log('listkeypress');
13493         // scroll to first matching element based on key pres..
13494         if (e.isSpecialKey()) {
13495             return false;
13496         }
13497         var k = String.fromCharCode(e.getKey()).toUpperCase();
13498         //Roo.log(k);
13499         var match  = false;
13500         var csel = this.view.getSelectedNodes();
13501         var cselitem = false;
13502         if (csel.length) {
13503             var ix = this.view.indexOf(csel[0]);
13504             cselitem  = this.store.getAt(ix);
13505             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13506                 cselitem = false;
13507             }
13508             
13509         }
13510         
13511         this.store.each(function(v) { 
13512             if (cselitem) {
13513                 // start at existing selection.
13514                 if (cselitem.id == v.id) {
13515                     cselitem = false;
13516                 }
13517                 return true;
13518             }
13519                 
13520             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13521                 match = this.store.indexOf(v);
13522                 return false;
13523             }
13524             return true;
13525         }, this);
13526         
13527         if (match === false) {
13528             return true; // no more action?
13529         }
13530         // scroll to?
13531         this.view.select(match);
13532         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13533         sn.scrollIntoView(sn.dom.parentNode, false);
13534     },
13535     
13536     onViewScroll : function(e, t){
13537         
13538         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){
13539             return;
13540         }
13541         
13542         this.hasQuery = true;
13543         
13544         this.loading = this.list.select('.loading', true).first();
13545         
13546         if(this.loading === null){
13547             this.list.createChild({
13548                 tag: 'div',
13549                 cls: 'loading roo-select2-more-results roo-select2-active',
13550                 html: 'Loading more results...'
13551             });
13552             
13553             this.loading = this.list.select('.loading', true).first();
13554             
13555             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13556             
13557             this.loading.hide();
13558         }
13559         
13560         this.loading.show();
13561         
13562         var _combo = this;
13563         
13564         this.page++;
13565         this.loadNext = true;
13566         
13567         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13568         
13569         return;
13570     },
13571     
13572     addItem : function(o)
13573     {   
13574         var dv = ''; // display value
13575         
13576         if (this.displayField) {
13577             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13578         } else {
13579             // this is an error condition!!!
13580             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13581         }
13582         
13583         if(!dv.length){
13584             return;
13585         }
13586         
13587         var choice = this.choices.createChild({
13588             tag: 'li',
13589             cls: 'roo-select2-search-choice',
13590             cn: [
13591                 {
13592                     tag: 'div',
13593                     html: dv
13594                 },
13595                 {
13596                     tag: 'a',
13597                     href: '#',
13598                     cls: 'roo-select2-search-choice-close',
13599                     tabindex: '-1'
13600                 }
13601             ]
13602             
13603         }, this.searchField);
13604         
13605         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13606         
13607         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13608         
13609         this.item.push(o);
13610         
13611         this.lastData = o;
13612         
13613         this.syncValue();
13614         
13615         this.inputEl().dom.value = '';
13616         
13617         this.validate();
13618     },
13619     
13620     onRemoveItem : function(e, _self, o)
13621     {
13622         e.preventDefault();
13623         
13624         this.lastItem = Roo.apply([], this.item);
13625         
13626         var index = this.item.indexOf(o.data) * 1;
13627         
13628         if( index < 0){
13629             Roo.log('not this item?!');
13630             return;
13631         }
13632         
13633         this.item.splice(index, 1);
13634         o.item.remove();
13635         
13636         this.syncValue();
13637         
13638         this.fireEvent('remove', this, e);
13639         
13640         this.validate();
13641         
13642     },
13643     
13644     syncValue : function()
13645     {
13646         if(!this.item.length){
13647             this.clearValue();
13648             return;
13649         }
13650             
13651         var value = [];
13652         var _this = this;
13653         Roo.each(this.item, function(i){
13654             if(_this.valueField){
13655                 value.push(i[_this.valueField]);
13656                 return;
13657             }
13658
13659             value.push(i);
13660         });
13661
13662         this.value = value.join(',');
13663
13664         if(this.hiddenField){
13665             this.hiddenField.dom.value = this.value;
13666         }
13667         
13668         this.store.fireEvent("datachanged", this.store);
13669     },
13670     
13671     clearItem : function()
13672     {
13673         if(!this.multiple){
13674             return;
13675         }
13676         
13677         this.item = [];
13678         
13679         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13680            c.remove();
13681         });
13682         
13683         this.syncValue();
13684         
13685         this.validate();
13686         
13687         if(this.tickable && !Roo.isTouch){
13688             this.view.refresh();
13689         }
13690     },
13691     
13692     inputEl: function ()
13693     {
13694         if(Roo.isTouch && this.mobileTouchView){
13695             return this.el.select('input.form-control',true).first();
13696         }
13697         
13698         if(this.tickable){
13699             return this.searchField;
13700         }
13701         
13702         return this.el.select('input.form-control',true).first();
13703     },
13704     
13705     
13706     onTickableFooterButtonClick : function(e, btn, el)
13707     {
13708         e.preventDefault();
13709         
13710         this.lastItem = Roo.apply([], this.item);
13711         
13712         if(btn && btn.name == 'cancel'){
13713             this.tickItems = Roo.apply([], this.item);
13714             this.collapse();
13715             return;
13716         }
13717         
13718         this.clearItem();
13719         
13720         var _this = this;
13721         
13722         Roo.each(this.tickItems, function(o){
13723             _this.addItem(o);
13724         });
13725         
13726         this.collapse();
13727         
13728     },
13729     
13730     validate : function()
13731     {
13732         var v = this.getRawValue();
13733         
13734         if(this.multiple){
13735             v = this.getValue();
13736         }
13737         
13738         if(this.disabled || this.allowBlank || v.length){
13739             this.markValid();
13740             return true;
13741         }
13742         
13743         this.markInvalid();
13744         return false;
13745     },
13746     
13747     tickableInputEl : function()
13748     {
13749         if(!this.tickable || !this.editable){
13750             return this.inputEl();
13751         }
13752         
13753         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13754     },
13755     
13756     
13757     getAutoCreateTouchView : function()
13758     {
13759         var id = Roo.id();
13760         
13761         var cfg = {
13762             cls: 'form-group' //input-group
13763         };
13764         
13765         var input =  {
13766             tag: 'input',
13767             id : id,
13768             type : this.inputType,
13769             cls : 'form-control x-combo-noedit',
13770             autocomplete: 'new-password',
13771             placeholder : this.placeholder || '',
13772             readonly : true
13773         };
13774         
13775         if (this.name) {
13776             input.name = this.name;
13777         }
13778         
13779         if (this.size) {
13780             input.cls += ' input-' + this.size;
13781         }
13782         
13783         if (this.disabled) {
13784             input.disabled = true;
13785         }
13786         
13787         var inputblock = {
13788             cls : '',
13789             cn : [
13790                 input
13791             ]
13792         };
13793         
13794         if(this.before){
13795             inputblock.cls += ' input-group';
13796             
13797             inputblock.cn.unshift({
13798                 tag :'span',
13799                 cls : 'input-group-addon',
13800                 html : this.before
13801             });
13802         }
13803         
13804         if(this.removable && !this.multiple){
13805             inputblock.cls += ' roo-removable';
13806             
13807             inputblock.cn.push({
13808                 tag: 'button',
13809                 html : 'x',
13810                 cls : 'roo-combo-removable-btn close'
13811             });
13812         }
13813
13814         if(this.hasFeedback && !this.allowBlank){
13815             
13816             inputblock.cls += ' has-feedback';
13817             
13818             inputblock.cn.push({
13819                 tag: 'span',
13820                 cls: 'glyphicon form-control-feedback'
13821             });
13822             
13823         }
13824         
13825         if (this.after) {
13826             
13827             inputblock.cls += (this.before) ? '' : ' input-group';
13828             
13829             inputblock.cn.push({
13830                 tag :'span',
13831                 cls : 'input-group-addon',
13832                 html : this.after
13833             });
13834         }
13835
13836         var box = {
13837             tag: 'div',
13838             cn: [
13839                 {
13840                     tag: 'input',
13841                     type : 'hidden',
13842                     cls: 'form-hidden-field'
13843                 },
13844                 inputblock
13845             ]
13846             
13847         };
13848         
13849         if(this.multiple){
13850             box = {
13851                 tag: 'div',
13852                 cn: [
13853                     {
13854                         tag: 'input',
13855                         type : 'hidden',
13856                         cls: 'form-hidden-field'
13857                     },
13858                     {
13859                         tag: 'ul',
13860                         cls: 'roo-select2-choices',
13861                         cn:[
13862                             {
13863                                 tag: 'li',
13864                                 cls: 'roo-select2-search-field',
13865                                 cn: [
13866
13867                                     inputblock
13868                                 ]
13869                             }
13870                         ]
13871                     }
13872                 ]
13873             }
13874         };
13875         
13876         var combobox = {
13877             cls: 'roo-select2-container input-group',
13878             cn: [
13879                 box
13880             ]
13881         };
13882         
13883 //        if(!this.multiple && this.showToggleBtn){
13884 //            
13885 //            var caret = {
13886 //                        tag: 'span',
13887 //                        cls: 'caret'
13888 //            };
13889 //            
13890 //            if (this.caret != false) {
13891 //                caret = {
13892 //                     tag: 'i',
13893 //                     cls: 'fa fa-' + this.caret
13894 //                };
13895 //                
13896 //            }
13897 //            
13898 //            combobox.cn.push({
13899 //                tag :'span',
13900 //                cls : 'input-group-addon btn dropdown-toggle',
13901 //                cn : [
13902 //                    caret,
13903 //                    {
13904 //                        tag: 'span',
13905 //                        cls: 'combobox-clear',
13906 //                        cn  : [
13907 //                            {
13908 //                                tag : 'i',
13909 //                                cls: 'icon-remove'
13910 //                            }
13911 //                        ]
13912 //                    }
13913 //                ]
13914 //
13915 //            })
13916 //        }
13917         
13918         if(this.multiple){
13919             combobox.cls += ' roo-select2-container-multi';
13920         }
13921         
13922         var align = this.labelAlign || this.parentLabelAlign();
13923         
13924         cfg.cn = combobox;
13925         
13926         if(this.fieldLabel.length && this.labelWidth){
13927             
13928             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13929             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13930             
13931             cfg.cn = [
13932                 {
13933                     tag: 'label',
13934                     cls : 'control-label ' + lw,
13935                     html : this.fieldLabel
13936
13937                 },
13938                 {
13939                     cls : cw, 
13940                     cn: [
13941                         combobox
13942                     ]
13943                 }
13944             ];
13945         }
13946         
13947         var settings = this;
13948         
13949         ['xs','sm','md','lg'].map(function(size){
13950             if (settings[size]) {
13951                 cfg.cls += ' col-' + size + '-' + settings[size];
13952             }
13953         });
13954         
13955         return cfg;
13956     },
13957     
13958     initTouchView : function()
13959     {
13960         this.renderTouchView();
13961         
13962         this.touchViewEl.on('scroll', function(){
13963             this.el.dom.scrollTop = 0;
13964         }, this);
13965         
13966         this.originalValue = this.getValue();
13967         
13968         this.inputEl().on("touch", this.showTouchView, this);
13969         
13970         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13971         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13972         
13973         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13974         
13975         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13976         this.store.on('load', this.onTouchViewLoad, this);
13977         this.store.on('loadexception', this.onTouchViewLoadException, this);
13978         
13979         if(this.hiddenName){
13980             
13981             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13982             
13983             this.hiddenField.dom.value =
13984                 this.hiddenValue !== undefined ? this.hiddenValue :
13985                 this.value !== undefined ? this.value : '';
13986         
13987             this.el.dom.removeAttribute('name');
13988             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13989         }
13990         
13991         if(this.multiple){
13992             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13993             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13994         }
13995         
13996         if(this.removable && !this.multiple){
13997             var close = this.closeTriggerEl();
13998             if(close){
13999                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14000                 close.on('click', this.removeBtnClick, this, close);
14001             }
14002         }
14003         /*
14004          * fix the bug in Safari iOS8
14005          */
14006         this.inputEl().on("focus", function(e){
14007             document.activeElement.blur();
14008         }, this);
14009         
14010         return;
14011         
14012         
14013     },
14014     
14015     renderTouchView : function()
14016     {
14017         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14018         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14019         
14020         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14021         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14022         
14023         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14024         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14025         this.touchViewBodyEl.setStyle('overflow', 'auto');
14026         
14027         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14028         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14029         
14030         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14031         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14032         
14033     },
14034     
14035     showTouchView : function()
14036     {
14037         if(this.disabled){
14038             return;
14039         }
14040         
14041         this.touchViewHeaderEl.hide();
14042
14043         if(this.fieldLabel.length){
14044             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
14045             this.touchViewHeaderEl.show();
14046         }
14047
14048         this.touchViewEl.show();
14049
14050         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14051         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14052                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14053
14054         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14055
14056         if(this.fieldLabel.length){
14057             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14058         }
14059         
14060         this.touchViewBodyEl.setHeight(bodyHeight);
14061
14062         if(this.animate){
14063             var _this = this;
14064             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14065         }else{
14066             this.touchViewEl.addClass('in');
14067         }
14068
14069         this.doTouchViewQuery();
14070         
14071     },
14072     
14073     hideTouchView : function()
14074     {
14075         this.touchViewEl.removeClass('in');
14076
14077         if(this.animate){
14078             var _this = this;
14079             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14080         }else{
14081             this.touchViewEl.setStyle('display', 'none');
14082         }
14083         
14084     },
14085     
14086     setTouchViewValue : function()
14087     {
14088         if(this.multiple){
14089             this.clearItem();
14090         
14091             var _this = this;
14092
14093             Roo.each(this.tickItems, function(o){
14094                 this.addItem(o);
14095             }, this);
14096         }
14097         
14098         this.hideTouchView();
14099     },
14100     
14101     doTouchViewQuery : function()
14102     {
14103         var qe = {
14104             query: '',
14105             forceAll: true,
14106             combo: this,
14107             cancel:false
14108         };
14109         
14110         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14111             return false;
14112         }
14113         
14114         if(!this.alwaysQuery || this.mode == 'local'){
14115             this.onTouchViewLoad();
14116             return;
14117         }
14118         
14119         this.store.load();
14120     },
14121     
14122     onTouchViewBeforeLoad : function(combo,opts)
14123     {
14124         return;
14125     },
14126
14127     // private
14128     onTouchViewLoad : function()
14129     {
14130         if(this.store.getCount() < 1){
14131             this.onTouchViewEmptyResults();
14132             return;
14133         }
14134         
14135         this.clearTouchView();
14136         
14137         var rawValue = this.getRawValue();
14138         
14139         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14140         
14141         this.tickItems = [];
14142         
14143         this.store.data.each(function(d, rowIndex){
14144             var row = this.touchViewListGroup.createChild(template);
14145             
14146             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14147                 row.addClass(d.data.cls);
14148             }
14149             
14150             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14151                 var cfg = {
14152                     data : d.data,
14153                     html : d.data[this.displayField]
14154                 };
14155                 
14156                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14157                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14158                 }
14159             }
14160             row.removeClass('selected');
14161             if(!this.multiple && this.valueField &&
14162                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14163             {
14164                 // radio buttons..
14165                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14166                 row.addClass('selected');
14167             }
14168             
14169             if(this.multiple && this.valueField &&
14170                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14171             {
14172                 
14173                 // checkboxes...
14174                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14175                 this.tickItems.push(d.data);
14176             }
14177             
14178             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14179             
14180         }, this);
14181         
14182         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14183         
14184         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14185
14186         if(this.fieldLabel.length){
14187             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14188         }
14189
14190         var listHeight = this.touchViewListGroup.getHeight();
14191         
14192         var _this = this;
14193         
14194         if(firstChecked && listHeight > bodyHeight){
14195             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14196         }
14197         
14198     },
14199     
14200     onTouchViewLoadException : function()
14201     {
14202         this.hideTouchView();
14203     },
14204     
14205     onTouchViewEmptyResults : function()
14206     {
14207         this.clearTouchView();
14208         
14209         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14210         
14211         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14212         
14213     },
14214     
14215     clearTouchView : function()
14216     {
14217         this.touchViewListGroup.dom.innerHTML = '';
14218     },
14219     
14220     onTouchViewClick : function(e, el, o)
14221     {
14222         e.preventDefault();
14223         
14224         var row = o.row;
14225         var rowIndex = o.rowIndex;
14226         
14227         var r = this.store.getAt(rowIndex);
14228         
14229         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14230             
14231             if(!this.multiple){
14232                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14233                     c.dom.removeAttribute('checked');
14234                 }, this);
14235
14236                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14237
14238                 this.setFromData(r.data);
14239
14240                 var close = this.closeTriggerEl();
14241
14242                 if(close){
14243                     close.show();
14244                 }
14245
14246                 this.hideTouchView();
14247
14248                 this.fireEvent('select', this, r, rowIndex);
14249
14250                 return;
14251             }
14252
14253             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14254                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14255                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14256                 return;
14257             }
14258
14259             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14260             this.addItem(r.data);
14261             this.tickItems.push(r.data);
14262         }
14263     }
14264     
14265
14266     /** 
14267     * @cfg {Boolean} grow 
14268     * @hide 
14269     */
14270     /** 
14271     * @cfg {Number} growMin 
14272     * @hide 
14273     */
14274     /** 
14275     * @cfg {Number} growMax 
14276     * @hide 
14277     */
14278     /**
14279      * @hide
14280      * @method autoSize
14281      */
14282 });
14283
14284 Roo.apply(Roo.bootstrap.ComboBox,  {
14285     
14286     header : {
14287         tag: 'div',
14288         cls: 'modal-header',
14289         cn: [
14290             {
14291                 tag: 'h4',
14292                 cls: 'modal-title'
14293             }
14294         ]
14295     },
14296     
14297     body : {
14298         tag: 'div',
14299         cls: 'modal-body',
14300         cn: [
14301             {
14302                 tag: 'ul',
14303                 cls: 'list-group'
14304             }
14305         ]
14306     },
14307     
14308     listItemRadio : {
14309         tag: 'li',
14310         cls: 'list-group-item',
14311         cn: [
14312             {
14313                 tag: 'span',
14314                 cls: 'roo-combobox-list-group-item-value'
14315             },
14316             {
14317                 tag: 'div',
14318                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14319                 cn: [
14320                     {
14321                         tag: 'input',
14322                         type: 'radio'
14323                     },
14324                     {
14325                         tag: 'label'
14326                     }
14327                 ]
14328             }
14329         ]
14330     },
14331     
14332     listItemCheckbox : {
14333         tag: 'li',
14334         cls: 'list-group-item',
14335         cn: [
14336             {
14337                 tag: 'span',
14338                 cls: 'roo-combobox-list-group-item-value'
14339             },
14340             {
14341                 tag: 'div',
14342                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14343                 cn: [
14344                     {
14345                         tag: 'input',
14346                         type: 'checkbox'
14347                     },
14348                     {
14349                         tag: 'label'
14350                     }
14351                 ]
14352             }
14353         ]
14354     },
14355     
14356     emptyResult : {
14357         tag: 'div',
14358         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14359     },
14360     
14361     footer : {
14362         tag: 'div',
14363         cls: 'modal-footer',
14364         cn: [
14365             {
14366                 tag: 'div',
14367                 cls: 'row',
14368                 cn: [
14369                     {
14370                         tag: 'div',
14371                         cls: 'col-xs-6 text-left',
14372                         cn: {
14373                             tag: 'button',
14374                             cls: 'btn btn-danger roo-touch-view-cancel',
14375                             html: 'Cancel'
14376                         }
14377                     },
14378                     {
14379                         tag: 'div',
14380                         cls: 'col-xs-6 text-right',
14381                         cn: {
14382                             tag: 'button',
14383                             cls: 'btn btn-success roo-touch-view-ok',
14384                             html: 'OK'
14385                         }
14386                     }
14387                 ]
14388             }
14389         ]
14390         
14391     }
14392 });
14393
14394 Roo.apply(Roo.bootstrap.ComboBox,  {
14395     
14396     touchViewTemplate : {
14397         tag: 'div',
14398         cls: 'modal fade roo-combobox-touch-view',
14399         cn: [
14400             {
14401                 tag: 'div',
14402                 cls: 'modal-dialog',
14403                 style : 'position:fixed', // we have to fix position....
14404                 cn: [
14405                     {
14406                         tag: 'div',
14407                         cls: 'modal-content',
14408                         cn: [
14409                             Roo.bootstrap.ComboBox.header,
14410                             Roo.bootstrap.ComboBox.body,
14411                             Roo.bootstrap.ComboBox.footer
14412                         ]
14413                     }
14414                 ]
14415             }
14416         ]
14417     }
14418 });/*
14419  * Based on:
14420  * Ext JS Library 1.1.1
14421  * Copyright(c) 2006-2007, Ext JS, LLC.
14422  *
14423  * Originally Released Under LGPL - original licence link has changed is not relivant.
14424  *
14425  * Fork - LGPL
14426  * <script type="text/javascript">
14427  */
14428
14429 /**
14430  * @class Roo.View
14431  * @extends Roo.util.Observable
14432  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14433  * This class also supports single and multi selection modes. <br>
14434  * Create a data model bound view:
14435  <pre><code>
14436  var store = new Roo.data.Store(...);
14437
14438  var view = new Roo.View({
14439     el : "my-element",
14440     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14441  
14442     singleSelect: true,
14443     selectedClass: "ydataview-selected",
14444     store: store
14445  });
14446
14447  // listen for node click?
14448  view.on("click", function(vw, index, node, e){
14449  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14450  });
14451
14452  // load XML data
14453  dataModel.load("foobar.xml");
14454  </code></pre>
14455  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14456  * <br><br>
14457  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14458  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14459  * 
14460  * Note: old style constructor is still suported (container, template, config)
14461  * 
14462  * @constructor
14463  * Create a new View
14464  * @param {Object} config The config object
14465  * 
14466  */
14467 Roo.View = function(config, depreciated_tpl, depreciated_config){
14468     
14469     this.parent = false;
14470     
14471     if (typeof(depreciated_tpl) == 'undefined') {
14472         // new way.. - universal constructor.
14473         Roo.apply(this, config);
14474         this.el  = Roo.get(this.el);
14475     } else {
14476         // old format..
14477         this.el  = Roo.get(config);
14478         this.tpl = depreciated_tpl;
14479         Roo.apply(this, depreciated_config);
14480     }
14481     this.wrapEl  = this.el.wrap().wrap();
14482     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14483     
14484     
14485     if(typeof(this.tpl) == "string"){
14486         this.tpl = new Roo.Template(this.tpl);
14487     } else {
14488         // support xtype ctors..
14489         this.tpl = new Roo.factory(this.tpl, Roo);
14490     }
14491     
14492     
14493     this.tpl.compile();
14494     
14495     /** @private */
14496     this.addEvents({
14497         /**
14498          * @event beforeclick
14499          * Fires before a click is processed. Returns false to cancel the default action.
14500          * @param {Roo.View} this
14501          * @param {Number} index The index of the target node
14502          * @param {HTMLElement} node The target node
14503          * @param {Roo.EventObject} e The raw event object
14504          */
14505             "beforeclick" : true,
14506         /**
14507          * @event click
14508          * Fires when a template node is clicked.
14509          * @param {Roo.View} this
14510          * @param {Number} index The index of the target node
14511          * @param {HTMLElement} node The target node
14512          * @param {Roo.EventObject} e The raw event object
14513          */
14514             "click" : true,
14515         /**
14516          * @event dblclick
14517          * Fires when a template node is double clicked.
14518          * @param {Roo.View} this
14519          * @param {Number} index The index of the target node
14520          * @param {HTMLElement} node The target node
14521          * @param {Roo.EventObject} e The raw event object
14522          */
14523             "dblclick" : true,
14524         /**
14525          * @event contextmenu
14526          * Fires when a template node is right clicked.
14527          * @param {Roo.View} this
14528          * @param {Number} index The index of the target node
14529          * @param {HTMLElement} node The target node
14530          * @param {Roo.EventObject} e The raw event object
14531          */
14532             "contextmenu" : true,
14533         /**
14534          * @event selectionchange
14535          * Fires when the selected nodes change.
14536          * @param {Roo.View} this
14537          * @param {Array} selections Array of the selected nodes
14538          */
14539             "selectionchange" : true,
14540     
14541         /**
14542          * @event beforeselect
14543          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14544          * @param {Roo.View} this
14545          * @param {HTMLElement} node The node to be selected
14546          * @param {Array} selections Array of currently selected nodes
14547          */
14548             "beforeselect" : true,
14549         /**
14550          * @event preparedata
14551          * Fires on every row to render, to allow you to change the data.
14552          * @param {Roo.View} this
14553          * @param {Object} data to be rendered (change this)
14554          */
14555           "preparedata" : true
14556           
14557           
14558         });
14559
14560
14561
14562     this.el.on({
14563         "click": this.onClick,
14564         "dblclick": this.onDblClick,
14565         "contextmenu": this.onContextMenu,
14566         scope:this
14567     });
14568
14569     this.selections = [];
14570     this.nodes = [];
14571     this.cmp = new Roo.CompositeElementLite([]);
14572     if(this.store){
14573         this.store = Roo.factory(this.store, Roo.data);
14574         this.setStore(this.store, true);
14575     }
14576     
14577     if ( this.footer && this.footer.xtype) {
14578            
14579          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14580         
14581         this.footer.dataSource = this.store;
14582         this.footer.container = fctr;
14583         this.footer = Roo.factory(this.footer, Roo);
14584         fctr.insertFirst(this.el);
14585         
14586         // this is a bit insane - as the paging toolbar seems to detach the el..
14587 //        dom.parentNode.parentNode.parentNode
14588          // they get detached?
14589     }
14590     
14591     
14592     Roo.View.superclass.constructor.call(this);
14593     
14594     
14595 };
14596
14597 Roo.extend(Roo.View, Roo.util.Observable, {
14598     
14599      /**
14600      * @cfg {Roo.data.Store} store Data store to load data from.
14601      */
14602     store : false,
14603     
14604     /**
14605      * @cfg {String|Roo.Element} el The container element.
14606      */
14607     el : '',
14608     
14609     /**
14610      * @cfg {String|Roo.Template} tpl The template used by this View 
14611      */
14612     tpl : false,
14613     /**
14614      * @cfg {String} dataName the named area of the template to use as the data area
14615      *                          Works with domtemplates roo-name="name"
14616      */
14617     dataName: false,
14618     /**
14619      * @cfg {String} selectedClass The css class to add to selected nodes
14620      */
14621     selectedClass : "x-view-selected",
14622      /**
14623      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14624      */
14625     emptyText : "",
14626     
14627     /**
14628      * @cfg {String} text to display on mask (default Loading)
14629      */
14630     mask : false,
14631     /**
14632      * @cfg {Boolean} multiSelect Allow multiple selection
14633      */
14634     multiSelect : false,
14635     /**
14636      * @cfg {Boolean} singleSelect Allow single selection
14637      */
14638     singleSelect:  false,
14639     
14640     /**
14641      * @cfg {Boolean} toggleSelect - selecting 
14642      */
14643     toggleSelect : false,
14644     
14645     /**
14646      * @cfg {Boolean} tickable - selecting 
14647      */
14648     tickable : false,
14649     
14650     /**
14651      * Returns the element this view is bound to.
14652      * @return {Roo.Element}
14653      */
14654     getEl : function(){
14655         return this.wrapEl;
14656     },
14657     
14658     
14659
14660     /**
14661      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14662      */
14663     refresh : function(){
14664         //Roo.log('refresh');
14665         var t = this.tpl;
14666         
14667         // if we are using something like 'domtemplate', then
14668         // the what gets used is:
14669         // t.applySubtemplate(NAME, data, wrapping data..)
14670         // the outer template then get' applied with
14671         //     the store 'extra data'
14672         // and the body get's added to the
14673         //      roo-name="data" node?
14674         //      <span class='roo-tpl-{name}'></span> ?????
14675         
14676         
14677         
14678         this.clearSelections();
14679         this.el.update("");
14680         var html = [];
14681         var records = this.store.getRange();
14682         if(records.length < 1) {
14683             
14684             // is this valid??  = should it render a template??
14685             
14686             this.el.update(this.emptyText);
14687             return;
14688         }
14689         var el = this.el;
14690         if (this.dataName) {
14691             this.el.update(t.apply(this.store.meta)); //????
14692             el = this.el.child('.roo-tpl-' + this.dataName);
14693         }
14694         
14695         for(var i = 0, len = records.length; i < len; i++){
14696             var data = this.prepareData(records[i].data, i, records[i]);
14697             this.fireEvent("preparedata", this, data, i, records[i]);
14698             
14699             var d = Roo.apply({}, data);
14700             
14701             if(this.tickable){
14702                 Roo.apply(d, {'roo-id' : Roo.id()});
14703                 
14704                 var _this = this;
14705             
14706                 Roo.each(this.parent.item, function(item){
14707                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14708                         return;
14709                     }
14710                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14711                 });
14712             }
14713             
14714             html[html.length] = Roo.util.Format.trim(
14715                 this.dataName ?
14716                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14717                     t.apply(d)
14718             );
14719         }
14720         
14721         
14722         
14723         el.update(html.join(""));
14724         this.nodes = el.dom.childNodes;
14725         this.updateIndexes(0);
14726     },
14727     
14728
14729     /**
14730      * Function to override to reformat the data that is sent to
14731      * the template for each node.
14732      * DEPRICATED - use the preparedata event handler.
14733      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14734      * a JSON object for an UpdateManager bound view).
14735      */
14736     prepareData : function(data, index, record)
14737     {
14738         this.fireEvent("preparedata", this, data, index, record);
14739         return data;
14740     },
14741
14742     onUpdate : function(ds, record){
14743         // Roo.log('on update');   
14744         this.clearSelections();
14745         var index = this.store.indexOf(record);
14746         var n = this.nodes[index];
14747         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14748         n.parentNode.removeChild(n);
14749         this.updateIndexes(index, index);
14750     },
14751
14752     
14753     
14754 // --------- FIXME     
14755     onAdd : function(ds, records, index)
14756     {
14757         //Roo.log(['on Add', ds, records, index] );        
14758         this.clearSelections();
14759         if(this.nodes.length == 0){
14760             this.refresh();
14761             return;
14762         }
14763         var n = this.nodes[index];
14764         for(var i = 0, len = records.length; i < len; i++){
14765             var d = this.prepareData(records[i].data, i, records[i]);
14766             if(n){
14767                 this.tpl.insertBefore(n, d);
14768             }else{
14769                 
14770                 this.tpl.append(this.el, d);
14771             }
14772         }
14773         this.updateIndexes(index);
14774     },
14775
14776     onRemove : function(ds, record, index){
14777        // Roo.log('onRemove');
14778         this.clearSelections();
14779         var el = this.dataName  ?
14780             this.el.child('.roo-tpl-' + this.dataName) :
14781             this.el; 
14782         
14783         el.dom.removeChild(this.nodes[index]);
14784         this.updateIndexes(index);
14785     },
14786
14787     /**
14788      * Refresh an individual node.
14789      * @param {Number} index
14790      */
14791     refreshNode : function(index){
14792         this.onUpdate(this.store, this.store.getAt(index));
14793     },
14794
14795     updateIndexes : function(startIndex, endIndex){
14796         var ns = this.nodes;
14797         startIndex = startIndex || 0;
14798         endIndex = endIndex || ns.length - 1;
14799         for(var i = startIndex; i <= endIndex; i++){
14800             ns[i].nodeIndex = i;
14801         }
14802     },
14803
14804     /**
14805      * Changes the data store this view uses and refresh the view.
14806      * @param {Store} store
14807      */
14808     setStore : function(store, initial){
14809         if(!initial && this.store){
14810             this.store.un("datachanged", this.refresh);
14811             this.store.un("add", this.onAdd);
14812             this.store.un("remove", this.onRemove);
14813             this.store.un("update", this.onUpdate);
14814             this.store.un("clear", this.refresh);
14815             this.store.un("beforeload", this.onBeforeLoad);
14816             this.store.un("load", this.onLoad);
14817             this.store.un("loadexception", this.onLoad);
14818         }
14819         if(store){
14820           
14821             store.on("datachanged", this.refresh, this);
14822             store.on("add", this.onAdd, this);
14823             store.on("remove", this.onRemove, this);
14824             store.on("update", this.onUpdate, this);
14825             store.on("clear", this.refresh, this);
14826             store.on("beforeload", this.onBeforeLoad, this);
14827             store.on("load", this.onLoad, this);
14828             store.on("loadexception", this.onLoad, this);
14829         }
14830         
14831         if(store){
14832             this.refresh();
14833         }
14834     },
14835     /**
14836      * onbeforeLoad - masks the loading area.
14837      *
14838      */
14839     onBeforeLoad : function(store,opts)
14840     {
14841          //Roo.log('onBeforeLoad');   
14842         if (!opts.add) {
14843             this.el.update("");
14844         }
14845         this.el.mask(this.mask ? this.mask : "Loading" ); 
14846     },
14847     onLoad : function ()
14848     {
14849         this.el.unmask();
14850     },
14851     
14852
14853     /**
14854      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14855      * @param {HTMLElement} node
14856      * @return {HTMLElement} The template node
14857      */
14858     findItemFromChild : function(node){
14859         var el = this.dataName  ?
14860             this.el.child('.roo-tpl-' + this.dataName,true) :
14861             this.el.dom; 
14862         
14863         if(!node || node.parentNode == el){
14864                     return node;
14865             }
14866             var p = node.parentNode;
14867             while(p && p != el){
14868             if(p.parentNode == el){
14869                 return p;
14870             }
14871             p = p.parentNode;
14872         }
14873             return null;
14874     },
14875
14876     /** @ignore */
14877     onClick : function(e){
14878         var item = this.findItemFromChild(e.getTarget());
14879         if(item){
14880             var index = this.indexOf(item);
14881             if(this.onItemClick(item, index, e) !== false){
14882                 this.fireEvent("click", this, index, item, e);
14883             }
14884         }else{
14885             this.clearSelections();
14886         }
14887     },
14888
14889     /** @ignore */
14890     onContextMenu : function(e){
14891         var item = this.findItemFromChild(e.getTarget());
14892         if(item){
14893             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14894         }
14895     },
14896
14897     /** @ignore */
14898     onDblClick : function(e){
14899         var item = this.findItemFromChild(e.getTarget());
14900         if(item){
14901             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14902         }
14903     },
14904
14905     onItemClick : function(item, index, e)
14906     {
14907         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14908             return false;
14909         }
14910         if (this.toggleSelect) {
14911             var m = this.isSelected(item) ? 'unselect' : 'select';
14912             //Roo.log(m);
14913             var _t = this;
14914             _t[m](item, true, false);
14915             return true;
14916         }
14917         if(this.multiSelect || this.singleSelect){
14918             if(this.multiSelect && e.shiftKey && this.lastSelection){
14919                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14920             }else{
14921                 this.select(item, this.multiSelect && e.ctrlKey);
14922                 this.lastSelection = item;
14923             }
14924             
14925             if(!this.tickable){
14926                 e.preventDefault();
14927             }
14928             
14929         }
14930         return true;
14931     },
14932
14933     /**
14934      * Get the number of selected nodes.
14935      * @return {Number}
14936      */
14937     getSelectionCount : function(){
14938         return this.selections.length;
14939     },
14940
14941     /**
14942      * Get the currently selected nodes.
14943      * @return {Array} An array of HTMLElements
14944      */
14945     getSelectedNodes : function(){
14946         return this.selections;
14947     },
14948
14949     /**
14950      * Get the indexes of the selected nodes.
14951      * @return {Array}
14952      */
14953     getSelectedIndexes : function(){
14954         var indexes = [], s = this.selections;
14955         for(var i = 0, len = s.length; i < len; i++){
14956             indexes.push(s[i].nodeIndex);
14957         }
14958         return indexes;
14959     },
14960
14961     /**
14962      * Clear all selections
14963      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14964      */
14965     clearSelections : function(suppressEvent){
14966         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14967             this.cmp.elements = this.selections;
14968             this.cmp.removeClass(this.selectedClass);
14969             this.selections = [];
14970             if(!suppressEvent){
14971                 this.fireEvent("selectionchange", this, this.selections);
14972             }
14973         }
14974     },
14975
14976     /**
14977      * Returns true if the passed node is selected
14978      * @param {HTMLElement/Number} node The node or node index
14979      * @return {Boolean}
14980      */
14981     isSelected : function(node){
14982         var s = this.selections;
14983         if(s.length < 1){
14984             return false;
14985         }
14986         node = this.getNode(node);
14987         return s.indexOf(node) !== -1;
14988     },
14989
14990     /**
14991      * Selects nodes.
14992      * @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
14993      * @param {Boolean} keepExisting (optional) true to keep existing selections
14994      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14995      */
14996     select : function(nodeInfo, keepExisting, suppressEvent){
14997         if(nodeInfo instanceof Array){
14998             if(!keepExisting){
14999                 this.clearSelections(true);
15000             }
15001             for(var i = 0, len = nodeInfo.length; i < len; i++){
15002                 this.select(nodeInfo[i], true, true);
15003             }
15004             return;
15005         } 
15006         var node = this.getNode(nodeInfo);
15007         if(!node || this.isSelected(node)){
15008             return; // already selected.
15009         }
15010         if(!keepExisting){
15011             this.clearSelections(true);
15012         }
15013         
15014         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15015             Roo.fly(node).addClass(this.selectedClass);
15016             this.selections.push(node);
15017             if(!suppressEvent){
15018                 this.fireEvent("selectionchange", this, this.selections);
15019             }
15020         }
15021         
15022         
15023     },
15024       /**
15025      * Unselects nodes.
15026      * @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
15027      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15028      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15029      */
15030     unselect : function(nodeInfo, keepExisting, suppressEvent)
15031     {
15032         if(nodeInfo instanceof Array){
15033             Roo.each(this.selections, function(s) {
15034                 this.unselect(s, nodeInfo);
15035             }, this);
15036             return;
15037         }
15038         var node = this.getNode(nodeInfo);
15039         if(!node || !this.isSelected(node)){
15040             //Roo.log("not selected");
15041             return; // not selected.
15042         }
15043         // fireevent???
15044         var ns = [];
15045         Roo.each(this.selections, function(s) {
15046             if (s == node ) {
15047                 Roo.fly(node).removeClass(this.selectedClass);
15048
15049                 return;
15050             }
15051             ns.push(s);
15052         },this);
15053         
15054         this.selections= ns;
15055         this.fireEvent("selectionchange", this, this.selections);
15056     },
15057
15058     /**
15059      * Gets a template node.
15060      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15061      * @return {HTMLElement} The node or null if it wasn't found
15062      */
15063     getNode : function(nodeInfo){
15064         if(typeof nodeInfo == "string"){
15065             return document.getElementById(nodeInfo);
15066         }else if(typeof nodeInfo == "number"){
15067             return this.nodes[nodeInfo];
15068         }
15069         return nodeInfo;
15070     },
15071
15072     /**
15073      * Gets a range template nodes.
15074      * @param {Number} startIndex
15075      * @param {Number} endIndex
15076      * @return {Array} An array of nodes
15077      */
15078     getNodes : function(start, end){
15079         var ns = this.nodes;
15080         start = start || 0;
15081         end = typeof end == "undefined" ? ns.length - 1 : end;
15082         var nodes = [];
15083         if(start <= end){
15084             for(var i = start; i <= end; i++){
15085                 nodes.push(ns[i]);
15086             }
15087         } else{
15088             for(var i = start; i >= end; i--){
15089                 nodes.push(ns[i]);
15090             }
15091         }
15092         return nodes;
15093     },
15094
15095     /**
15096      * Finds the index of the passed node
15097      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15098      * @return {Number} The index of the node or -1
15099      */
15100     indexOf : function(node){
15101         node = this.getNode(node);
15102         if(typeof node.nodeIndex == "number"){
15103             return node.nodeIndex;
15104         }
15105         var ns = this.nodes;
15106         for(var i = 0, len = ns.length; i < len; i++){
15107             if(ns[i] == node){
15108                 return i;
15109             }
15110         }
15111         return -1;
15112     }
15113 });
15114 /*
15115  * - LGPL
15116  *
15117  * based on jquery fullcalendar
15118  * 
15119  */
15120
15121 Roo.bootstrap = Roo.bootstrap || {};
15122 /**
15123  * @class Roo.bootstrap.Calendar
15124  * @extends Roo.bootstrap.Component
15125  * Bootstrap Calendar class
15126  * @cfg {Boolean} loadMask (true|false) default false
15127  * @cfg {Object} header generate the user specific header of the calendar, default false
15128
15129  * @constructor
15130  * Create a new Container
15131  * @param {Object} config The config object
15132  */
15133
15134
15135
15136 Roo.bootstrap.Calendar = function(config){
15137     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15138      this.addEvents({
15139         /**
15140              * @event select
15141              * Fires when a date is selected
15142              * @param {DatePicker} this
15143              * @param {Date} date The selected date
15144              */
15145         'select': true,
15146         /**
15147              * @event monthchange
15148              * Fires when the displayed month changes 
15149              * @param {DatePicker} this
15150              * @param {Date} date The selected month
15151              */
15152         'monthchange': true,
15153         /**
15154              * @event evententer
15155              * Fires when mouse over an event
15156              * @param {Calendar} this
15157              * @param {event} Event
15158              */
15159         'evententer': true,
15160         /**
15161              * @event eventleave
15162              * Fires when the mouse leaves an
15163              * @param {Calendar} this
15164              * @param {event}
15165              */
15166         'eventleave': true,
15167         /**
15168              * @event eventclick
15169              * Fires when the mouse click an
15170              * @param {Calendar} this
15171              * @param {event}
15172              */
15173         'eventclick': true
15174         
15175     });
15176
15177 };
15178
15179 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15180     
15181      /**
15182      * @cfg {Number} startDay
15183      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15184      */
15185     startDay : 0,
15186     
15187     loadMask : false,
15188     
15189     header : false,
15190       
15191     getAutoCreate : function(){
15192         
15193         
15194         var fc_button = function(name, corner, style, content ) {
15195             return Roo.apply({},{
15196                 tag : 'span',
15197                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15198                          (corner.length ?
15199                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15200                             ''
15201                         ),
15202                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15203                 unselectable: 'on'
15204             });
15205         };
15206         
15207         var header = {};
15208         
15209         if(!this.header){
15210             header = {
15211                 tag : 'table',
15212                 cls : 'fc-header',
15213                 style : 'width:100%',
15214                 cn : [
15215                     {
15216                         tag: 'tr',
15217                         cn : [
15218                             {
15219                                 tag : 'td',
15220                                 cls : 'fc-header-left',
15221                                 cn : [
15222                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15223                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15224                                     { tag: 'span', cls: 'fc-header-space' },
15225                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15226
15227
15228                                 ]
15229                             },
15230
15231                             {
15232                                 tag : 'td',
15233                                 cls : 'fc-header-center',
15234                                 cn : [
15235                                     {
15236                                         tag: 'span',
15237                                         cls: 'fc-header-title',
15238                                         cn : {
15239                                             tag: 'H2',
15240                                             html : 'month / year'
15241                                         }
15242                                     }
15243
15244                                 ]
15245                             },
15246                             {
15247                                 tag : 'td',
15248                                 cls : 'fc-header-right',
15249                                 cn : [
15250                               /*      fc_button('month', 'left', '', 'month' ),
15251                                     fc_button('week', '', '', 'week' ),
15252                                     fc_button('day', 'right', '', 'day' )
15253                                 */    
15254
15255                                 ]
15256                             }
15257
15258                         ]
15259                     }
15260                 ]
15261             };
15262         }
15263         
15264         header = this.header;
15265         
15266        
15267         var cal_heads = function() {
15268             var ret = [];
15269             // fixme - handle this.
15270             
15271             for (var i =0; i < Date.dayNames.length; i++) {
15272                 var d = Date.dayNames[i];
15273                 ret.push({
15274                     tag: 'th',
15275                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15276                     html : d.substring(0,3)
15277                 });
15278                 
15279             }
15280             ret[0].cls += ' fc-first';
15281             ret[6].cls += ' fc-last';
15282             return ret;
15283         };
15284         var cal_cell = function(n) {
15285             return  {
15286                 tag: 'td',
15287                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15288                 cn : [
15289                     {
15290                         cn : [
15291                             {
15292                                 cls: 'fc-day-number',
15293                                 html: 'D'
15294                             },
15295                             {
15296                                 cls: 'fc-day-content',
15297                              
15298                                 cn : [
15299                                      {
15300                                         style: 'position: relative;' // height: 17px;
15301                                     }
15302                                 ]
15303                             }
15304                             
15305                             
15306                         ]
15307                     }
15308                 ]
15309                 
15310             }
15311         };
15312         var cal_rows = function() {
15313             
15314             var ret = [];
15315             for (var r = 0; r < 6; r++) {
15316                 var row= {
15317                     tag : 'tr',
15318                     cls : 'fc-week',
15319                     cn : []
15320                 };
15321                 
15322                 for (var i =0; i < Date.dayNames.length; i++) {
15323                     var d = Date.dayNames[i];
15324                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15325
15326                 }
15327                 row.cn[0].cls+=' fc-first';
15328                 row.cn[0].cn[0].style = 'min-height:90px';
15329                 row.cn[6].cls+=' fc-last';
15330                 ret.push(row);
15331                 
15332             }
15333             ret[0].cls += ' fc-first';
15334             ret[4].cls += ' fc-prev-last';
15335             ret[5].cls += ' fc-last';
15336             return ret;
15337             
15338         };
15339         
15340         var cal_table = {
15341             tag: 'table',
15342             cls: 'fc-border-separate',
15343             style : 'width:100%',
15344             cellspacing  : 0,
15345             cn : [
15346                 { 
15347                     tag: 'thead',
15348                     cn : [
15349                         { 
15350                             tag: 'tr',
15351                             cls : 'fc-first fc-last',
15352                             cn : cal_heads()
15353                         }
15354                     ]
15355                 },
15356                 { 
15357                     tag: 'tbody',
15358                     cn : cal_rows()
15359                 }
15360                   
15361             ]
15362         };
15363          
15364          var cfg = {
15365             cls : 'fc fc-ltr',
15366             cn : [
15367                 header,
15368                 {
15369                     cls : 'fc-content',
15370                     style : "position: relative;",
15371                     cn : [
15372                         {
15373                             cls : 'fc-view fc-view-month fc-grid',
15374                             style : 'position: relative',
15375                             unselectable : 'on',
15376                             cn : [
15377                                 {
15378                                     cls : 'fc-event-container',
15379                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15380                                 },
15381                                 cal_table
15382                             ]
15383                         }
15384                     ]
15385     
15386                 }
15387            ] 
15388             
15389         };
15390         
15391          
15392         
15393         return cfg;
15394     },
15395     
15396     
15397     initEvents : function()
15398     {
15399         if(!this.store){
15400             throw "can not find store for calendar";
15401         }
15402         
15403         var mark = {
15404             tag: "div",
15405             cls:"x-dlg-mask",
15406             style: "text-align:center",
15407             cn: [
15408                 {
15409                     tag: "div",
15410                     style: "background-color:white;width:50%;margin:250 auto",
15411                     cn: [
15412                         {
15413                             tag: "img",
15414                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15415                         },
15416                         {
15417                             tag: "span",
15418                             html: "Loading"
15419                         }
15420                         
15421                     ]
15422                 }
15423             ]
15424         };
15425         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15426         
15427         var size = this.el.select('.fc-content', true).first().getSize();
15428         this.maskEl.setSize(size.width, size.height);
15429         this.maskEl.enableDisplayMode("block");
15430         if(!this.loadMask){
15431             this.maskEl.hide();
15432         }
15433         
15434         this.store = Roo.factory(this.store, Roo.data);
15435         this.store.on('load', this.onLoad, this);
15436         this.store.on('beforeload', this.onBeforeLoad, this);
15437         
15438         this.resize();
15439         
15440         this.cells = this.el.select('.fc-day',true);
15441         //Roo.log(this.cells);
15442         this.textNodes = this.el.query('.fc-day-number');
15443         this.cells.addClassOnOver('fc-state-hover');
15444         
15445         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15446         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15447         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15448         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15449         
15450         this.on('monthchange', this.onMonthChange, this);
15451         
15452         this.update(new Date().clearTime());
15453     },
15454     
15455     resize : function() {
15456         var sz  = this.el.getSize();
15457         
15458         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15459         this.el.select('.fc-day-content div',true).setHeight(34);
15460     },
15461     
15462     
15463     // private
15464     showPrevMonth : function(e){
15465         this.update(this.activeDate.add("mo", -1));
15466     },
15467     showToday : function(e){
15468         this.update(new Date().clearTime());
15469     },
15470     // private
15471     showNextMonth : function(e){
15472         this.update(this.activeDate.add("mo", 1));
15473     },
15474
15475     // private
15476     showPrevYear : function(){
15477         this.update(this.activeDate.add("y", -1));
15478     },
15479
15480     // private
15481     showNextYear : function(){
15482         this.update(this.activeDate.add("y", 1));
15483     },
15484
15485     
15486    // private
15487     update : function(date)
15488     {
15489         var vd = this.activeDate;
15490         this.activeDate = date;
15491 //        if(vd && this.el){
15492 //            var t = date.getTime();
15493 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15494 //                Roo.log('using add remove');
15495 //                
15496 //                this.fireEvent('monthchange', this, date);
15497 //                
15498 //                this.cells.removeClass("fc-state-highlight");
15499 //                this.cells.each(function(c){
15500 //                   if(c.dateValue == t){
15501 //                       c.addClass("fc-state-highlight");
15502 //                       setTimeout(function(){
15503 //                            try{c.dom.firstChild.focus();}catch(e){}
15504 //                       }, 50);
15505 //                       return false;
15506 //                   }
15507 //                   return true;
15508 //                });
15509 //                return;
15510 //            }
15511 //        }
15512         
15513         var days = date.getDaysInMonth();
15514         
15515         var firstOfMonth = date.getFirstDateOfMonth();
15516         var startingPos = firstOfMonth.getDay()-this.startDay;
15517         
15518         if(startingPos < this.startDay){
15519             startingPos += 7;
15520         }
15521         
15522         var pm = date.add(Date.MONTH, -1);
15523         var prevStart = pm.getDaysInMonth()-startingPos;
15524 //        
15525         this.cells = this.el.select('.fc-day',true);
15526         this.textNodes = this.el.query('.fc-day-number');
15527         this.cells.addClassOnOver('fc-state-hover');
15528         
15529         var cells = this.cells.elements;
15530         var textEls = this.textNodes;
15531         
15532         Roo.each(cells, function(cell){
15533             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15534         });
15535         
15536         days += startingPos;
15537
15538         // convert everything to numbers so it's fast
15539         var day = 86400000;
15540         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15541         //Roo.log(d);
15542         //Roo.log(pm);
15543         //Roo.log(prevStart);
15544         
15545         var today = new Date().clearTime().getTime();
15546         var sel = date.clearTime().getTime();
15547         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15548         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15549         var ddMatch = this.disabledDatesRE;
15550         var ddText = this.disabledDatesText;
15551         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15552         var ddaysText = this.disabledDaysText;
15553         var format = this.format;
15554         
15555         var setCellClass = function(cal, cell){
15556             cell.row = 0;
15557             cell.events = [];
15558             cell.more = [];
15559             //Roo.log('set Cell Class');
15560             cell.title = "";
15561             var t = d.getTime();
15562             
15563             //Roo.log(d);
15564             
15565             cell.dateValue = t;
15566             if(t == today){
15567                 cell.className += " fc-today";
15568                 cell.className += " fc-state-highlight";
15569                 cell.title = cal.todayText;
15570             }
15571             if(t == sel){
15572                 // disable highlight in other month..
15573                 //cell.className += " fc-state-highlight";
15574                 
15575             }
15576             // disabling
15577             if(t < min) {
15578                 cell.className = " fc-state-disabled";
15579                 cell.title = cal.minText;
15580                 return;
15581             }
15582             if(t > max) {
15583                 cell.className = " fc-state-disabled";
15584                 cell.title = cal.maxText;
15585                 return;
15586             }
15587             if(ddays){
15588                 if(ddays.indexOf(d.getDay()) != -1){
15589                     cell.title = ddaysText;
15590                     cell.className = " fc-state-disabled";
15591                 }
15592             }
15593             if(ddMatch && format){
15594                 var fvalue = d.dateFormat(format);
15595                 if(ddMatch.test(fvalue)){
15596                     cell.title = ddText.replace("%0", fvalue);
15597                     cell.className = " fc-state-disabled";
15598                 }
15599             }
15600             
15601             if (!cell.initialClassName) {
15602                 cell.initialClassName = cell.dom.className;
15603             }
15604             
15605             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15606         };
15607
15608         var i = 0;
15609         
15610         for(; i < startingPos; i++) {
15611             textEls[i].innerHTML = (++prevStart);
15612             d.setDate(d.getDate()+1);
15613             
15614             cells[i].className = "fc-past fc-other-month";
15615             setCellClass(this, cells[i]);
15616         }
15617         
15618         var intDay = 0;
15619         
15620         for(; i < days; i++){
15621             intDay = i - startingPos + 1;
15622             textEls[i].innerHTML = (intDay);
15623             d.setDate(d.getDate()+1);
15624             
15625             cells[i].className = ''; // "x-date-active";
15626             setCellClass(this, cells[i]);
15627         }
15628         var extraDays = 0;
15629         
15630         for(; i < 42; i++) {
15631             textEls[i].innerHTML = (++extraDays);
15632             d.setDate(d.getDate()+1);
15633             
15634             cells[i].className = "fc-future fc-other-month";
15635             setCellClass(this, cells[i]);
15636         }
15637         
15638         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15639         
15640         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15641         
15642         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15643         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15644         
15645         if(totalRows != 6){
15646             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15647             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15648         }
15649         
15650         this.fireEvent('monthchange', this, date);
15651         
15652         
15653         /*
15654         if(!this.internalRender){
15655             var main = this.el.dom.firstChild;
15656             var w = main.offsetWidth;
15657             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15658             Roo.fly(main).setWidth(w);
15659             this.internalRender = true;
15660             // opera does not respect the auto grow header center column
15661             // then, after it gets a width opera refuses to recalculate
15662             // without a second pass
15663             if(Roo.isOpera && !this.secondPass){
15664                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15665                 this.secondPass = true;
15666                 this.update.defer(10, this, [date]);
15667             }
15668         }
15669         */
15670         
15671     },
15672     
15673     findCell : function(dt) {
15674         dt = dt.clearTime().getTime();
15675         var ret = false;
15676         this.cells.each(function(c){
15677             //Roo.log("check " +c.dateValue + '?=' + dt);
15678             if(c.dateValue == dt){
15679                 ret = c;
15680                 return false;
15681             }
15682             return true;
15683         });
15684         
15685         return ret;
15686     },
15687     
15688     findCells : function(ev) {
15689         var s = ev.start.clone().clearTime().getTime();
15690        // Roo.log(s);
15691         var e= ev.end.clone().clearTime().getTime();
15692        // Roo.log(e);
15693         var ret = [];
15694         this.cells.each(function(c){
15695              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15696             
15697             if(c.dateValue > e){
15698                 return ;
15699             }
15700             if(c.dateValue < s){
15701                 return ;
15702             }
15703             ret.push(c);
15704         });
15705         
15706         return ret;    
15707     },
15708     
15709 //    findBestRow: function(cells)
15710 //    {
15711 //        var ret = 0;
15712 //        
15713 //        for (var i =0 ; i < cells.length;i++) {
15714 //            ret  = Math.max(cells[i].rows || 0,ret);
15715 //        }
15716 //        return ret;
15717 //        
15718 //    },
15719     
15720     
15721     addItem : function(ev)
15722     {
15723         // look for vertical location slot in
15724         var cells = this.findCells(ev);
15725         
15726 //        ev.row = this.findBestRow(cells);
15727         
15728         // work out the location.
15729         
15730         var crow = false;
15731         var rows = [];
15732         for(var i =0; i < cells.length; i++) {
15733             
15734             cells[i].row = cells[0].row;
15735             
15736             if(i == 0){
15737                 cells[i].row = cells[i].row + 1;
15738             }
15739             
15740             if (!crow) {
15741                 crow = {
15742                     start : cells[i],
15743                     end :  cells[i]
15744                 };
15745                 continue;
15746             }
15747             if (crow.start.getY() == cells[i].getY()) {
15748                 // on same row.
15749                 crow.end = cells[i];
15750                 continue;
15751             }
15752             // different row.
15753             rows.push(crow);
15754             crow = {
15755                 start: cells[i],
15756                 end : cells[i]
15757             };
15758             
15759         }
15760         
15761         rows.push(crow);
15762         ev.els = [];
15763         ev.rows = rows;
15764         ev.cells = cells;
15765         
15766         cells[0].events.push(ev);
15767         
15768         this.calevents.push(ev);
15769     },
15770     
15771     clearEvents: function() {
15772         
15773         if(!this.calevents){
15774             return;
15775         }
15776         
15777         Roo.each(this.cells.elements, function(c){
15778             c.row = 0;
15779             c.events = [];
15780             c.more = [];
15781         });
15782         
15783         Roo.each(this.calevents, function(e) {
15784             Roo.each(e.els, function(el) {
15785                 el.un('mouseenter' ,this.onEventEnter, this);
15786                 el.un('mouseleave' ,this.onEventLeave, this);
15787                 el.remove();
15788             },this);
15789         },this);
15790         
15791         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15792             e.remove();
15793         });
15794         
15795     },
15796     
15797     renderEvents: function()
15798     {   
15799         var _this = this;
15800         
15801         this.cells.each(function(c) {
15802             
15803             if(c.row < 5){
15804                 return;
15805             }
15806             
15807             var ev = c.events;
15808             
15809             var r = 4;
15810             if(c.row != c.events.length){
15811                 r = 4 - (4 - (c.row - c.events.length));
15812             }
15813             
15814             c.events = ev.slice(0, r);
15815             c.more = ev.slice(r);
15816             
15817             if(c.more.length && c.more.length == 1){
15818                 c.events.push(c.more.pop());
15819             }
15820             
15821             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15822             
15823         });
15824             
15825         this.cells.each(function(c) {
15826             
15827             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15828             
15829             
15830             for (var e = 0; e < c.events.length; e++){
15831                 var ev = c.events[e];
15832                 var rows = ev.rows;
15833                 
15834                 for(var i = 0; i < rows.length; i++) {
15835                 
15836                     // how many rows should it span..
15837
15838                     var  cfg = {
15839                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15840                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15841
15842                         unselectable : "on",
15843                         cn : [
15844                             {
15845                                 cls: 'fc-event-inner',
15846                                 cn : [
15847     //                                {
15848     //                                  tag:'span',
15849     //                                  cls: 'fc-event-time',
15850     //                                  html : cells.length > 1 ? '' : ev.time
15851     //                                },
15852                                     {
15853                                       tag:'span',
15854                                       cls: 'fc-event-title',
15855                                       html : String.format('{0}', ev.title)
15856                                     }
15857
15858
15859                                 ]
15860                             },
15861                             {
15862                                 cls: 'ui-resizable-handle ui-resizable-e',
15863                                 html : '&nbsp;&nbsp;&nbsp'
15864                             }
15865
15866                         ]
15867                     };
15868
15869                     if (i == 0) {
15870                         cfg.cls += ' fc-event-start';
15871                     }
15872                     if ((i+1) == rows.length) {
15873                         cfg.cls += ' fc-event-end';
15874                     }
15875
15876                     var ctr = _this.el.select('.fc-event-container',true).first();
15877                     var cg = ctr.createChild(cfg);
15878
15879                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15880                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15881
15882                     var r = (c.more.length) ? 1 : 0;
15883                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15884                     cg.setWidth(ebox.right - sbox.x -2);
15885
15886                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15887                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15888                     cg.on('click', _this.onEventClick, _this, ev);
15889
15890                     ev.els.push(cg);
15891                     
15892                 }
15893                 
15894             }
15895             
15896             
15897             if(c.more.length){
15898                 var  cfg = {
15899                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15900                     style : 'position: absolute',
15901                     unselectable : "on",
15902                     cn : [
15903                         {
15904                             cls: 'fc-event-inner',
15905                             cn : [
15906                                 {
15907                                   tag:'span',
15908                                   cls: 'fc-event-title',
15909                                   html : 'More'
15910                                 }
15911
15912
15913                             ]
15914                         },
15915                         {
15916                             cls: 'ui-resizable-handle ui-resizable-e',
15917                             html : '&nbsp;&nbsp;&nbsp'
15918                         }
15919
15920                     ]
15921                 };
15922
15923                 var ctr = _this.el.select('.fc-event-container',true).first();
15924                 var cg = ctr.createChild(cfg);
15925
15926                 var sbox = c.select('.fc-day-content',true).first().getBox();
15927                 var ebox = c.select('.fc-day-content',true).first().getBox();
15928                 //Roo.log(cg);
15929                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15930                 cg.setWidth(ebox.right - sbox.x -2);
15931
15932                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15933                 
15934             }
15935             
15936         });
15937         
15938         
15939         
15940     },
15941     
15942     onEventEnter: function (e, el,event,d) {
15943         this.fireEvent('evententer', this, el, event);
15944     },
15945     
15946     onEventLeave: function (e, el,event,d) {
15947         this.fireEvent('eventleave', this, el, event);
15948     },
15949     
15950     onEventClick: function (e, el,event,d) {
15951         this.fireEvent('eventclick', this, el, event);
15952     },
15953     
15954     onMonthChange: function () {
15955         this.store.load();
15956     },
15957     
15958     onMoreEventClick: function(e, el, more)
15959     {
15960         var _this = this;
15961         
15962         this.calpopover.placement = 'right';
15963         this.calpopover.setTitle('More');
15964         
15965         this.calpopover.setContent('');
15966         
15967         var ctr = this.calpopover.el.select('.popover-content', true).first();
15968         
15969         Roo.each(more, function(m){
15970             var cfg = {
15971                 cls : 'fc-event-hori fc-event-draggable',
15972                 html : m.title
15973             };
15974             var cg = ctr.createChild(cfg);
15975             
15976             cg.on('click', _this.onEventClick, _this, m);
15977         });
15978         
15979         this.calpopover.show(el);
15980         
15981         
15982     },
15983     
15984     onLoad: function () 
15985     {   
15986         this.calevents = [];
15987         var cal = this;
15988         
15989         if(this.store.getCount() > 0){
15990             this.store.data.each(function(d){
15991                cal.addItem({
15992                     id : d.data.id,
15993                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15994                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15995                     time : d.data.start_time,
15996                     title : d.data.title,
15997                     description : d.data.description,
15998                     venue : d.data.venue
15999                 });
16000             });
16001         }
16002         
16003         this.renderEvents();
16004         
16005         if(this.calevents.length && this.loadMask){
16006             this.maskEl.hide();
16007         }
16008     },
16009     
16010     onBeforeLoad: function()
16011     {
16012         this.clearEvents();
16013         if(this.loadMask){
16014             this.maskEl.show();
16015         }
16016     }
16017 });
16018
16019  
16020  /*
16021  * - LGPL
16022  *
16023  * element
16024  * 
16025  */
16026
16027 /**
16028  * @class Roo.bootstrap.Popover
16029  * @extends Roo.bootstrap.Component
16030  * Bootstrap Popover class
16031  * @cfg {String} html contents of the popover   (or false to use children..)
16032  * @cfg {String} title of popover (or false to hide)
16033  * @cfg {String} placement how it is placed
16034  * @cfg {String} trigger click || hover (or false to trigger manually)
16035  * @cfg {String} over what (parent or false to trigger manually.)
16036  * @cfg {Number} delay - delay before showing
16037  
16038  * @constructor
16039  * Create a new Popover
16040  * @param {Object} config The config object
16041  */
16042
16043 Roo.bootstrap.Popover = function(config){
16044     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16045     
16046     this.addEvents({
16047         // raw events
16048          /**
16049          * @event show
16050          * After the popover show
16051          * 
16052          * @param {Roo.bootstrap.Popover} this
16053          */
16054         "show" : true,
16055         /**
16056          * @event hide
16057          * After the popover hide
16058          * 
16059          * @param {Roo.bootstrap.Popover} this
16060          */
16061         "hide" : true
16062     });
16063 };
16064
16065 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16066     
16067     title: 'Fill in a title',
16068     html: false,
16069     
16070     placement : 'right',
16071     trigger : 'hover', // hover
16072     
16073     delay : 0,
16074     
16075     over: 'parent',
16076     
16077     can_build_overlaid : false,
16078     
16079     getChildContainer : function()
16080     {
16081         return this.el.select('.popover-content',true).first();
16082     },
16083     
16084     getAutoCreate : function(){
16085          
16086         var cfg = {
16087            cls : 'popover roo-dynamic',
16088            style: 'display:block',
16089            cn : [
16090                 {
16091                     cls : 'arrow'
16092                 },
16093                 {
16094                     cls : 'popover-inner',
16095                     cn : [
16096                         {
16097                             tag: 'h3',
16098                             cls: 'popover-title',
16099                             html : this.title
16100                         },
16101                         {
16102                             cls : 'popover-content',
16103                             html : this.html
16104                         }
16105                     ]
16106                     
16107                 }
16108            ]
16109         };
16110         
16111         return cfg;
16112     },
16113     setTitle: function(str)
16114     {
16115         this.title = str;
16116         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16117     },
16118     setContent: function(str)
16119     {
16120         this.html = str;
16121         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16122     },
16123     // as it get's added to the bottom of the page.
16124     onRender : function(ct, position)
16125     {
16126         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16127         if(!this.el){
16128             var cfg = Roo.apply({},  this.getAutoCreate());
16129             cfg.id = Roo.id();
16130             
16131             if (this.cls) {
16132                 cfg.cls += ' ' + this.cls;
16133             }
16134             if (this.style) {
16135                 cfg.style = this.style;
16136             }
16137             //Roo.log("adding to ");
16138             this.el = Roo.get(document.body).createChild(cfg, position);
16139 //            Roo.log(this.el);
16140         }
16141         this.initEvents();
16142     },
16143     
16144     initEvents : function()
16145     {
16146         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16147         this.el.enableDisplayMode('block');
16148         this.el.hide();
16149         if (this.over === false) {
16150             return; 
16151         }
16152         if (this.triggers === false) {
16153             return;
16154         }
16155         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16156         var triggers = this.trigger ? this.trigger.split(' ') : [];
16157         Roo.each(triggers, function(trigger) {
16158         
16159             if (trigger == 'click') {
16160                 on_el.on('click', this.toggle, this);
16161             } else if (trigger != 'manual') {
16162                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16163                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16164       
16165                 on_el.on(eventIn  ,this.enter, this);
16166                 on_el.on(eventOut, this.leave, this);
16167             }
16168         }, this);
16169         
16170     },
16171     
16172     
16173     // private
16174     timeout : null,
16175     hoverState : null,
16176     
16177     toggle : function () {
16178         this.hoverState == 'in' ? this.leave() : this.enter();
16179     },
16180     
16181     enter : function () {
16182         
16183         clearTimeout(this.timeout);
16184     
16185         this.hoverState = 'in';
16186     
16187         if (!this.delay || !this.delay.show) {
16188             this.show();
16189             return;
16190         }
16191         var _t = this;
16192         this.timeout = setTimeout(function () {
16193             if (_t.hoverState == 'in') {
16194                 _t.show();
16195             }
16196         }, this.delay.show)
16197     },
16198     
16199     leave : function() {
16200         clearTimeout(this.timeout);
16201     
16202         this.hoverState = 'out';
16203     
16204         if (!this.delay || !this.delay.hide) {
16205             this.hide();
16206             return;
16207         }
16208         var _t = this;
16209         this.timeout = setTimeout(function () {
16210             if (_t.hoverState == 'out') {
16211                 _t.hide();
16212             }
16213         }, this.delay.hide)
16214     },
16215     
16216     show : function (on_el)
16217     {
16218         if (!on_el) {
16219             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16220         }
16221         
16222         // set content.
16223         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16224         if (this.html !== false) {
16225             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16226         }
16227         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16228         if (!this.title.length) {
16229             this.el.select('.popover-title',true).hide();
16230         }
16231         
16232         var placement = typeof this.placement == 'function' ?
16233             this.placement.call(this, this.el, on_el) :
16234             this.placement;
16235             
16236         var autoToken = /\s?auto?\s?/i;
16237         var autoPlace = autoToken.test(placement);
16238         if (autoPlace) {
16239             placement = placement.replace(autoToken, '') || 'top';
16240         }
16241         
16242         //this.el.detach()
16243         //this.el.setXY([0,0]);
16244         this.el.show();
16245         this.el.dom.style.display='block';
16246         this.el.addClass(placement);
16247         
16248         //this.el.appendTo(on_el);
16249         
16250         var p = this.getPosition();
16251         var box = this.el.getBox();
16252         
16253         if (autoPlace) {
16254             // fixme..
16255         }
16256         var align = Roo.bootstrap.Popover.alignment[placement];
16257         this.el.alignTo(on_el, align[0],align[1]);
16258         //var arrow = this.el.select('.arrow',true).first();
16259         //arrow.set(align[2], 
16260         
16261         this.el.addClass('in');
16262         
16263         
16264         if (this.el.hasClass('fade')) {
16265             // fade it?
16266         }
16267         
16268         this.hoverState = 'in';
16269         
16270         this.fireEvent('show', this);
16271         
16272     },
16273     hide : function()
16274     {
16275         this.el.setXY([0,0]);
16276         this.el.removeClass('in');
16277         this.el.hide();
16278         this.hoverState = null;
16279         
16280         this.fireEvent('hide', this);
16281     }
16282     
16283 });
16284
16285 Roo.bootstrap.Popover.alignment = {
16286     'left' : ['r-l', [-10,0], 'right'],
16287     'right' : ['l-r', [10,0], 'left'],
16288     'bottom' : ['t-b', [0,10], 'top'],
16289     'top' : [ 'b-t', [0,-10], 'bottom']
16290 };
16291
16292  /*
16293  * - LGPL
16294  *
16295  * Progress
16296  * 
16297  */
16298
16299 /**
16300  * @class Roo.bootstrap.Progress
16301  * @extends Roo.bootstrap.Component
16302  * Bootstrap Progress class
16303  * @cfg {Boolean} striped striped of the progress bar
16304  * @cfg {Boolean} active animated of the progress bar
16305  * 
16306  * 
16307  * @constructor
16308  * Create a new Progress
16309  * @param {Object} config The config object
16310  */
16311
16312 Roo.bootstrap.Progress = function(config){
16313     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16314 };
16315
16316 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16317     
16318     striped : false,
16319     active: false,
16320     
16321     getAutoCreate : function(){
16322         var cfg = {
16323             tag: 'div',
16324             cls: 'progress'
16325         };
16326         
16327         
16328         if(this.striped){
16329             cfg.cls += ' progress-striped';
16330         }
16331       
16332         if(this.active){
16333             cfg.cls += ' active';
16334         }
16335         
16336         
16337         return cfg;
16338     }
16339    
16340 });
16341
16342  
16343
16344  /*
16345  * - LGPL
16346  *
16347  * ProgressBar
16348  * 
16349  */
16350
16351 /**
16352  * @class Roo.bootstrap.ProgressBar
16353  * @extends Roo.bootstrap.Component
16354  * Bootstrap ProgressBar class
16355  * @cfg {Number} aria_valuenow aria-value now
16356  * @cfg {Number} aria_valuemin aria-value min
16357  * @cfg {Number} aria_valuemax aria-value max
16358  * @cfg {String} label label for the progress bar
16359  * @cfg {String} panel (success | info | warning | danger )
16360  * @cfg {String} role role of the progress bar
16361  * @cfg {String} sr_only text
16362  * 
16363  * 
16364  * @constructor
16365  * Create a new ProgressBar
16366  * @param {Object} config The config object
16367  */
16368
16369 Roo.bootstrap.ProgressBar = function(config){
16370     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16371 };
16372
16373 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16374     
16375     aria_valuenow : 0,
16376     aria_valuemin : 0,
16377     aria_valuemax : 100,
16378     label : false,
16379     panel : false,
16380     role : false,
16381     sr_only: false,
16382     
16383     getAutoCreate : function()
16384     {
16385         
16386         var cfg = {
16387             tag: 'div',
16388             cls: 'progress-bar',
16389             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16390         };
16391         
16392         if(this.sr_only){
16393             cfg.cn = {
16394                 tag: 'span',
16395                 cls: 'sr-only',
16396                 html: this.sr_only
16397             }
16398         }
16399         
16400         if(this.role){
16401             cfg.role = this.role;
16402         }
16403         
16404         if(this.aria_valuenow){
16405             cfg['aria-valuenow'] = this.aria_valuenow;
16406         }
16407         
16408         if(this.aria_valuemin){
16409             cfg['aria-valuemin'] = this.aria_valuemin;
16410         }
16411         
16412         if(this.aria_valuemax){
16413             cfg['aria-valuemax'] = this.aria_valuemax;
16414         }
16415         
16416         if(this.label && !this.sr_only){
16417             cfg.html = this.label;
16418         }
16419         
16420         if(this.panel){
16421             cfg.cls += ' progress-bar-' + this.panel;
16422         }
16423         
16424         return cfg;
16425     },
16426     
16427     update : function(aria_valuenow)
16428     {
16429         this.aria_valuenow = aria_valuenow;
16430         
16431         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16432     }
16433    
16434 });
16435
16436  
16437
16438  /*
16439  * - LGPL
16440  *
16441  * column
16442  * 
16443  */
16444
16445 /**
16446  * @class Roo.bootstrap.TabGroup
16447  * @extends Roo.bootstrap.Column
16448  * Bootstrap Column class
16449  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16450  * @cfg {Boolean} carousel true to make the group behave like a carousel
16451  * @cfg {Boolean} bullets show bullets for the panels
16452  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16453  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16454  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16455  * @cfg {Boolean} showarrow (true|false) show arrow default true
16456  * 
16457  * @constructor
16458  * Create a new TabGroup
16459  * @param {Object} config The config object
16460  */
16461
16462 Roo.bootstrap.TabGroup = function(config){
16463     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16464     if (!this.navId) {
16465         this.navId = Roo.id();
16466     }
16467     this.tabs = [];
16468     Roo.bootstrap.TabGroup.register(this);
16469     
16470 };
16471
16472 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16473     
16474     carousel : false,
16475     transition : false,
16476     bullets : 0,
16477     timer : 0,
16478     autoslide : false,
16479     slideFn : false,
16480     slideOnTouch : false,
16481     showarrow : true,
16482     
16483     getAutoCreate : function()
16484     {
16485         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16486         
16487         cfg.cls += ' tab-content';
16488         
16489         if (this.carousel) {
16490             cfg.cls += ' carousel slide';
16491             
16492             cfg.cn = [{
16493                cls : 'carousel-inner',
16494                cn : []
16495             }];
16496         
16497             if(this.bullets  && !Roo.isTouch){
16498                 
16499                 var bullets = {
16500                     cls : 'carousel-bullets',
16501                     cn : []
16502                 };
16503                
16504                 if(this.bullets_cls){
16505                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16506                 }
16507                 
16508                 bullets.cn.push({
16509                     cls : 'clear'
16510                 });
16511                 
16512                 cfg.cn[0].cn.push(bullets);
16513             }
16514             
16515             if(this.showarrow){
16516                 cfg.cn[0].cn.push({
16517                     tag : 'div',
16518                     class : 'carousel-arrow',
16519                     cn : [
16520                         {
16521                             tag : 'div',
16522                             class : 'carousel-prev',
16523                             cn : [
16524                                 {
16525                                     tag : 'i',
16526                                     class : 'fa fa-chevron-left'
16527                                 }
16528                             ]
16529                         },
16530                         {
16531                             tag : 'div',
16532                             class : 'carousel-next',
16533                             cn : [
16534                                 {
16535                                     tag : 'i',
16536                                     class : 'fa fa-chevron-right'
16537                                 }
16538                             ]
16539                         }
16540                     ]
16541                 });
16542             }
16543             
16544         }
16545         
16546         return cfg;
16547     },
16548     
16549     initEvents:  function()
16550     {
16551         if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16552             this.el.on("touchstart", this.onTouchStart, this);
16553         }
16554         
16555         if(this.autoslide){
16556             var _this = this;
16557             
16558             this.slideFn = window.setInterval(function() {
16559                 _this.showPanelNext();
16560             }, this.timer);
16561         }
16562         
16563         if(this.showarrow){
16564             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16565             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16566         }
16567         
16568         
16569     },
16570     
16571     onTouchStart : function(e, el, o)
16572     {
16573         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16574             return;
16575         }
16576         
16577         this.showPanelNext();
16578     },
16579     
16580     getChildContainer : function()
16581     {
16582         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16583     },
16584     
16585     /**
16586     * register a Navigation item
16587     * @param {Roo.bootstrap.NavItem} the navitem to add
16588     */
16589     register : function(item)
16590     {
16591         this.tabs.push( item);
16592         item.navId = this.navId; // not really needed..
16593         this.addBullet();
16594     
16595     },
16596     
16597     getActivePanel : function()
16598     {
16599         var r = false;
16600         Roo.each(this.tabs, function(t) {
16601             if (t.active) {
16602                 r = t;
16603                 return false;
16604             }
16605             return null;
16606         });
16607         return r;
16608         
16609     },
16610     getPanelByName : function(n)
16611     {
16612         var r = false;
16613         Roo.each(this.tabs, function(t) {
16614             if (t.tabId == n) {
16615                 r = t;
16616                 return false;
16617             }
16618             return null;
16619         });
16620         return r;
16621     },
16622     indexOfPanel : function(p)
16623     {
16624         var r = false;
16625         Roo.each(this.tabs, function(t,i) {
16626             if (t.tabId == p.tabId) {
16627                 r = i;
16628                 return false;
16629             }
16630             return null;
16631         });
16632         return r;
16633     },
16634     /**
16635      * show a specific panel
16636      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16637      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16638      */
16639     showPanel : function (pan)
16640     {
16641         if(this.transition || typeof(pan) == 'undefined'){
16642             Roo.log("waiting for the transitionend");
16643             return;
16644         }
16645         
16646         if (typeof(pan) == 'number') {
16647             pan = this.tabs[pan];
16648         }
16649         
16650         if (typeof(pan) == 'string') {
16651             pan = this.getPanelByName(pan);
16652         }
16653         
16654         var cur = this.getActivePanel();
16655         
16656         if(!pan || !cur){
16657             Roo.log('pan or acitve pan is undefined');
16658             return false;
16659         }
16660         
16661         if (pan.tabId == this.getActivePanel().tabId) {
16662             return true;
16663         }
16664         
16665         if (false === cur.fireEvent('beforedeactivate')) {
16666             return false;
16667         }
16668         
16669         if(this.bullets > 0 && !Roo.isTouch){
16670             this.setActiveBullet(this.indexOfPanel(pan));
16671         }
16672         
16673         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16674             
16675             this.transition = true;
16676             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16677             var lr = dir == 'next' ? 'left' : 'right';
16678             pan.el.addClass(dir); // or prev
16679             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16680             cur.el.addClass(lr); // or right
16681             pan.el.addClass(lr);
16682             
16683             var _this = this;
16684             cur.el.on('transitionend', function() {
16685                 Roo.log("trans end?");
16686                 
16687                 pan.el.removeClass([lr,dir]);
16688                 pan.setActive(true);
16689                 
16690                 cur.el.removeClass([lr]);
16691                 cur.setActive(false);
16692                 
16693                 _this.transition = false;
16694                 
16695             }, this, { single:  true } );
16696             
16697             return true;
16698         }
16699         
16700         cur.setActive(false);
16701         pan.setActive(true);
16702         
16703         return true;
16704         
16705     },
16706     showPanelNext : function()
16707     {
16708         var i = this.indexOfPanel(this.getActivePanel());
16709         
16710         if (i >= this.tabs.length - 1 && !this.autoslide) {
16711             return;
16712         }
16713         
16714         if (i >= this.tabs.length - 1 && this.autoslide) {
16715             i = -1;
16716         }
16717         
16718         this.showPanel(this.tabs[i+1]);
16719     },
16720     
16721     showPanelPrev : function()
16722     {
16723         var i = this.indexOfPanel(this.getActivePanel());
16724         
16725         if (i  < 1 && !this.autoslide) {
16726             return;
16727         }
16728         
16729         if (i < 1 && this.autoslide) {
16730             i = this.tabs.length;
16731         }
16732         
16733         this.showPanel(this.tabs[i-1]);
16734     },
16735     
16736     
16737     addBullet: function()
16738     {
16739         if(!this.bullets || Roo.isTouch){
16740             return;
16741         }
16742         var ctr = this.el.select('.carousel-bullets',true).first();
16743         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16744         var bullet = ctr.createChild({
16745             cls : 'bullet bullet-' + i
16746         },ctr.dom.lastChild);
16747         
16748         
16749         var _this = this;
16750         
16751         bullet.on('click', (function(e, el, o, ii, t){
16752
16753             e.preventDefault();
16754
16755             this.showPanel(ii);
16756
16757             if(this.autoslide && this.slideFn){
16758                 clearInterval(this.slideFn);
16759                 this.slideFn = window.setInterval(function() {
16760                     _this.showPanelNext();
16761                 }, this.timer);
16762             }
16763
16764         }).createDelegate(this, [i, bullet], true));
16765                 
16766         
16767     },
16768      
16769     setActiveBullet : function(i)
16770     {
16771         if(Roo.isTouch){
16772             return;
16773         }
16774         
16775         Roo.each(this.el.select('.bullet', true).elements, function(el){
16776             el.removeClass('selected');
16777         });
16778
16779         var bullet = this.el.select('.bullet-' + i, true).first();
16780         
16781         if(!bullet){
16782             return;
16783         }
16784         
16785         bullet.addClass('selected');
16786     }
16787     
16788     
16789   
16790 });
16791
16792  
16793
16794  
16795  
16796 Roo.apply(Roo.bootstrap.TabGroup, {
16797     
16798     groups: {},
16799      /**
16800     * register a Navigation Group
16801     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16802     */
16803     register : function(navgrp)
16804     {
16805         this.groups[navgrp.navId] = navgrp;
16806         
16807     },
16808     /**
16809     * fetch a Navigation Group based on the navigation ID
16810     * if one does not exist , it will get created.
16811     * @param {string} the navgroup to add
16812     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16813     */
16814     get: function(navId) {
16815         if (typeof(this.groups[navId]) == 'undefined') {
16816             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16817         }
16818         return this.groups[navId] ;
16819     }
16820     
16821     
16822     
16823 });
16824
16825  /*
16826  * - LGPL
16827  *
16828  * TabPanel
16829  * 
16830  */
16831
16832 /**
16833  * @class Roo.bootstrap.TabPanel
16834  * @extends Roo.bootstrap.Component
16835  * Bootstrap TabPanel class
16836  * @cfg {Boolean} active panel active
16837  * @cfg {String} html panel content
16838  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16839  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16840  * 
16841  * 
16842  * @constructor
16843  * Create a new TabPanel
16844  * @param {Object} config The config object
16845  */
16846
16847 Roo.bootstrap.TabPanel = function(config){
16848     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16849     this.addEvents({
16850         /**
16851              * @event changed
16852              * Fires when the active status changes
16853              * @param {Roo.bootstrap.TabPanel} this
16854              * @param {Boolean} state the new state
16855             
16856          */
16857         'changed': true,
16858         /**
16859              * @event beforedeactivate
16860              * Fires before a tab is de-activated - can be used to do validation on a form.
16861              * @param {Roo.bootstrap.TabPanel} this
16862              * @return {Boolean} false if there is an error
16863             
16864          */
16865         'beforedeactivate': true
16866      });
16867     
16868     this.tabId = this.tabId || Roo.id();
16869   
16870 };
16871
16872 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16873     
16874     active: false,
16875     html: false,
16876     tabId: false,
16877     navId : false,
16878     
16879     getAutoCreate : function(){
16880         var cfg = {
16881             tag: 'div',
16882             // item is needed for carousel - not sure if it has any effect otherwise
16883             cls: 'tab-pane item',
16884             html: this.html || ''
16885         };
16886         
16887         if(this.active){
16888             cfg.cls += ' active';
16889         }
16890         
16891         if(this.tabId){
16892             cfg.tabId = this.tabId;
16893         }
16894         
16895         
16896         return cfg;
16897     },
16898     
16899     initEvents:  function()
16900     {
16901         var p = this.parent();
16902         this.navId = this.navId || p.navId;
16903         
16904         if (typeof(this.navId) != 'undefined') {
16905             // not really needed.. but just in case.. parent should be a NavGroup.
16906             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16907             
16908             tg.register(this);
16909             
16910             var i = tg.tabs.length - 1;
16911             
16912             if(this.active && tg.bullets > 0 && i < tg.bullets){
16913                 tg.setActiveBullet(i);
16914             }
16915         }
16916         
16917     },
16918     
16919     
16920     onRender : function(ct, position)
16921     {
16922        // Roo.log("Call onRender: " + this.xtype);
16923         
16924         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16925         
16926         
16927         
16928         
16929         
16930     },
16931     
16932     setActive: function(state)
16933     {
16934         Roo.log("panel - set active " + this.tabId + "=" + state);
16935         
16936         this.active = state;
16937         if (!state) {
16938             this.el.removeClass('active');
16939             
16940         } else  if (!this.el.hasClass('active')) {
16941             this.el.addClass('active');
16942         }
16943         
16944         this.fireEvent('changed', this, state);
16945     }
16946     
16947     
16948 });
16949  
16950
16951  
16952
16953  /*
16954  * - LGPL
16955  *
16956  * DateField
16957  * 
16958  */
16959
16960 /**
16961  * @class Roo.bootstrap.DateField
16962  * @extends Roo.bootstrap.Input
16963  * Bootstrap DateField class
16964  * @cfg {Number} weekStart default 0
16965  * @cfg {String} viewMode default empty, (months|years)
16966  * @cfg {String} minViewMode default empty, (months|years)
16967  * @cfg {Number} startDate default -Infinity
16968  * @cfg {Number} endDate default Infinity
16969  * @cfg {Boolean} todayHighlight default false
16970  * @cfg {Boolean} todayBtn default false
16971  * @cfg {Boolean} calendarWeeks default false
16972  * @cfg {Object} daysOfWeekDisabled default empty
16973  * @cfg {Boolean} singleMode default false (true | false)
16974  * 
16975  * @cfg {Boolean} keyboardNavigation default true
16976  * @cfg {String} language default en
16977  * 
16978  * @constructor
16979  * Create a new DateField
16980  * @param {Object} config The config object
16981  */
16982
16983 Roo.bootstrap.DateField = function(config){
16984     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16985      this.addEvents({
16986             /**
16987              * @event show
16988              * Fires when this field show.
16989              * @param {Roo.bootstrap.DateField} this
16990              * @param {Mixed} date The date value
16991              */
16992             show : true,
16993             /**
16994              * @event show
16995              * Fires when this field hide.
16996              * @param {Roo.bootstrap.DateField} this
16997              * @param {Mixed} date The date value
16998              */
16999             hide : true,
17000             /**
17001              * @event select
17002              * Fires when select a date.
17003              * @param {Roo.bootstrap.DateField} this
17004              * @param {Mixed} date The date value
17005              */
17006             select : true,
17007             /**
17008              * @event beforeselect
17009              * Fires when before select a date.
17010              * @param {Roo.bootstrap.DateField} this
17011              * @param {Mixed} date The date value
17012              */
17013             beforeselect : true
17014         });
17015 };
17016
17017 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17018     
17019     /**
17020      * @cfg {String} format
17021      * The default date format string which can be overriden for localization support.  The format must be
17022      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17023      */
17024     format : "m/d/y",
17025     /**
17026      * @cfg {String} altFormats
17027      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17028      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17029      */
17030     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17031     
17032     weekStart : 0,
17033     
17034     viewMode : '',
17035     
17036     minViewMode : '',
17037     
17038     todayHighlight : false,
17039     
17040     todayBtn: false,
17041     
17042     language: 'en',
17043     
17044     keyboardNavigation: true,
17045     
17046     calendarWeeks: false,
17047     
17048     startDate: -Infinity,
17049     
17050     endDate: Infinity,
17051     
17052     daysOfWeekDisabled: [],
17053     
17054     _events: [],
17055     
17056     singleMode : false,
17057     
17058     UTCDate: function()
17059     {
17060         return new Date(Date.UTC.apply(Date, arguments));
17061     },
17062     
17063     UTCToday: function()
17064     {
17065         var today = new Date();
17066         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17067     },
17068     
17069     getDate: function() {
17070             var d = this.getUTCDate();
17071             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17072     },
17073     
17074     getUTCDate: function() {
17075             return this.date;
17076     },
17077     
17078     setDate: function(d) {
17079             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17080     },
17081     
17082     setUTCDate: function(d) {
17083             this.date = d;
17084             this.setValue(this.formatDate(this.date));
17085     },
17086         
17087     onRender: function(ct, position)
17088     {
17089         
17090         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17091         
17092         this.language = this.language || 'en';
17093         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17094         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17095         
17096         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17097         this.format = this.format || 'm/d/y';
17098         this.isInline = false;
17099         this.isInput = true;
17100         this.component = this.el.select('.add-on', true).first() || false;
17101         this.component = (this.component && this.component.length === 0) ? false : this.component;
17102         this.hasInput = this.component && this.inputEL().length;
17103         
17104         if (typeof(this.minViewMode === 'string')) {
17105             switch (this.minViewMode) {
17106                 case 'months':
17107                     this.minViewMode = 1;
17108                     break;
17109                 case 'years':
17110                     this.minViewMode = 2;
17111                     break;
17112                 default:
17113                     this.minViewMode = 0;
17114                     break;
17115             }
17116         }
17117         
17118         if (typeof(this.viewMode === 'string')) {
17119             switch (this.viewMode) {
17120                 case 'months':
17121                     this.viewMode = 1;
17122                     break;
17123                 case 'years':
17124                     this.viewMode = 2;
17125                     break;
17126                 default:
17127                     this.viewMode = 0;
17128                     break;
17129             }
17130         }
17131                 
17132         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17133         
17134 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17135         
17136         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17137         
17138         this.picker().on('mousedown', this.onMousedown, this);
17139         this.picker().on('click', this.onClick, this);
17140         
17141         this.picker().addClass('datepicker-dropdown');
17142         
17143         this.startViewMode = this.viewMode;
17144         
17145         if(this.singleMode){
17146             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17147                 v.setVisibilityMode(Roo.Element.DISPLAY);
17148                 v.hide();
17149             });
17150             
17151             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17152                 v.setStyle('width', '189px');
17153             });
17154         }
17155         
17156         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17157             if(!this.calendarWeeks){
17158                 v.remove();
17159                 return;
17160             }
17161             
17162             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17163             v.attr('colspan', function(i, val){
17164                 return parseInt(val) + 1;
17165             });
17166         });
17167                         
17168         
17169         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17170         
17171         this.setStartDate(this.startDate);
17172         this.setEndDate(this.endDate);
17173         
17174         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17175         
17176         this.fillDow();
17177         this.fillMonths();
17178         this.update();
17179         this.showMode();
17180         
17181         if(this.isInline) {
17182             this.show();
17183         }
17184     },
17185     
17186     picker : function()
17187     {
17188         return this.pickerEl;
17189 //        return this.el.select('.datepicker', true).first();
17190     },
17191     
17192     fillDow: function()
17193     {
17194         var dowCnt = this.weekStart;
17195         
17196         var dow = {
17197             tag: 'tr',
17198             cn: [
17199                 
17200             ]
17201         };
17202         
17203         if(this.calendarWeeks){
17204             dow.cn.push({
17205                 tag: 'th',
17206                 cls: 'cw',
17207                 html: '&nbsp;'
17208             })
17209         }
17210         
17211         while (dowCnt < this.weekStart + 7) {
17212             dow.cn.push({
17213                 tag: 'th',
17214                 cls: 'dow',
17215                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17216             });
17217         }
17218         
17219         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17220     },
17221     
17222     fillMonths: function()
17223     {    
17224         var i = 0;
17225         var months = this.picker().select('>.datepicker-months td', true).first();
17226         
17227         months.dom.innerHTML = '';
17228         
17229         while (i < 12) {
17230             var month = {
17231                 tag: 'span',
17232                 cls: 'month',
17233                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17234             };
17235             
17236             months.createChild(month);
17237         }
17238         
17239     },
17240     
17241     update: function()
17242     {
17243         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;
17244         
17245         if (this.date < this.startDate) {
17246             this.viewDate = new Date(this.startDate);
17247         } else if (this.date > this.endDate) {
17248             this.viewDate = new Date(this.endDate);
17249         } else {
17250             this.viewDate = new Date(this.date);
17251         }
17252         
17253         this.fill();
17254     },
17255     
17256     fill: function() 
17257     {
17258         var d = new Date(this.viewDate),
17259                 year = d.getUTCFullYear(),
17260                 month = d.getUTCMonth(),
17261                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17262                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17263                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17264                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17265                 currentDate = this.date && this.date.valueOf(),
17266                 today = this.UTCToday();
17267         
17268         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17269         
17270 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17271         
17272 //        this.picker.select('>tfoot th.today').
17273 //                                              .text(dates[this.language].today)
17274 //                                              .toggle(this.todayBtn !== false);
17275     
17276         this.updateNavArrows();
17277         this.fillMonths();
17278                                                 
17279         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17280         
17281         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17282          
17283         prevMonth.setUTCDate(day);
17284         
17285         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17286         
17287         var nextMonth = new Date(prevMonth);
17288         
17289         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17290         
17291         nextMonth = nextMonth.valueOf();
17292         
17293         var fillMonths = false;
17294         
17295         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17296         
17297         while(prevMonth.valueOf() < nextMonth) {
17298             var clsName = '';
17299             
17300             if (prevMonth.getUTCDay() === this.weekStart) {
17301                 if(fillMonths){
17302                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17303                 }
17304                     
17305                 fillMonths = {
17306                     tag: 'tr',
17307                     cn: []
17308                 };
17309                 
17310                 if(this.calendarWeeks){
17311                     // ISO 8601: First week contains first thursday.
17312                     // ISO also states week starts on Monday, but we can be more abstract here.
17313                     var
17314                     // Start of current week: based on weekstart/current date
17315                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17316                     // Thursday of this week
17317                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17318                     // First Thursday of year, year from thursday
17319                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17320                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17321                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17322                     
17323                     fillMonths.cn.push({
17324                         tag: 'td',
17325                         cls: 'cw',
17326                         html: calWeek
17327                     });
17328                 }
17329             }
17330             
17331             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17332                 clsName += ' old';
17333             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17334                 clsName += ' new';
17335             }
17336             if (this.todayHighlight &&
17337                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17338                 prevMonth.getUTCMonth() == today.getMonth() &&
17339                 prevMonth.getUTCDate() == today.getDate()) {
17340                 clsName += ' today';
17341             }
17342             
17343             if (currentDate && prevMonth.valueOf() === currentDate) {
17344                 clsName += ' active';
17345             }
17346             
17347             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17348                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17349                     clsName += ' disabled';
17350             }
17351             
17352             fillMonths.cn.push({
17353                 tag: 'td',
17354                 cls: 'day ' + clsName,
17355                 html: prevMonth.getDate()
17356             });
17357             
17358             prevMonth.setDate(prevMonth.getDate()+1);
17359         }
17360           
17361         var currentYear = this.date && this.date.getUTCFullYear();
17362         var currentMonth = this.date && this.date.getUTCMonth();
17363         
17364         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17365         
17366         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17367             v.removeClass('active');
17368             
17369             if(currentYear === year && k === currentMonth){
17370                 v.addClass('active');
17371             }
17372             
17373             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17374                 v.addClass('disabled');
17375             }
17376             
17377         });
17378         
17379         
17380         year = parseInt(year/10, 10) * 10;
17381         
17382         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17383         
17384         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17385         
17386         year -= 1;
17387         for (var i = -1; i < 11; i++) {
17388             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17389                 tag: 'span',
17390                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17391                 html: year
17392             });
17393             
17394             year += 1;
17395         }
17396     },
17397     
17398     showMode: function(dir) 
17399     {
17400         if (dir) {
17401             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17402         }
17403         
17404         Roo.each(this.picker().select('>div',true).elements, function(v){
17405             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17406             v.hide();
17407         });
17408         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17409     },
17410     
17411     place: function()
17412     {
17413         if(this.isInline) {
17414             return;
17415         }
17416         
17417         this.picker().removeClass(['bottom', 'top']);
17418         
17419         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17420             /*
17421              * place to the top of element!
17422              *
17423              */
17424             
17425             this.picker().addClass('top');
17426             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17427             
17428             return;
17429         }
17430         
17431         this.picker().addClass('bottom');
17432         
17433         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17434     },
17435     
17436     parseDate : function(value)
17437     {
17438         if(!value || value instanceof Date){
17439             return value;
17440         }
17441         var v = Date.parseDate(value, this.format);
17442         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17443             v = Date.parseDate(value, 'Y-m-d');
17444         }
17445         if(!v && this.altFormats){
17446             if(!this.altFormatsArray){
17447                 this.altFormatsArray = this.altFormats.split("|");
17448             }
17449             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17450                 v = Date.parseDate(value, this.altFormatsArray[i]);
17451             }
17452         }
17453         return v;
17454     },
17455     
17456     formatDate : function(date, fmt)
17457     {   
17458         return (!date || !(date instanceof Date)) ?
17459         date : date.dateFormat(fmt || this.format);
17460     },
17461     
17462     onFocus : function()
17463     {
17464         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17465         this.show();
17466     },
17467     
17468     onBlur : function()
17469     {
17470         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17471         
17472         var d = this.inputEl().getValue();
17473         
17474         this.setValue(d);
17475                 
17476         this.hide();
17477     },
17478     
17479     show : function()
17480     {
17481         this.picker().show();
17482         this.update();
17483         this.place();
17484         
17485         this.fireEvent('show', this, this.date);
17486     },
17487     
17488     hide : function()
17489     {
17490         if(this.isInline) {
17491             return;
17492         }
17493         this.picker().hide();
17494         this.viewMode = this.startViewMode;
17495         this.showMode();
17496         
17497         this.fireEvent('hide', this, this.date);
17498         
17499     },
17500     
17501     onMousedown: function(e)
17502     {
17503         e.stopPropagation();
17504         e.preventDefault();
17505     },
17506     
17507     keyup: function(e)
17508     {
17509         Roo.bootstrap.DateField.superclass.keyup.call(this);
17510         this.update();
17511     },
17512
17513     setValue: function(v)
17514     {
17515         if(this.fireEvent('beforeselect', this, v) !== false){
17516             var d = new Date(this.parseDate(v) ).clearTime();
17517         
17518             if(isNaN(d.getTime())){
17519                 this.date = this.viewDate = '';
17520                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17521                 return;
17522             }
17523
17524             v = this.formatDate(d);
17525
17526             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17527
17528             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17529
17530             this.update();
17531
17532             this.fireEvent('select', this, this.date);
17533         }
17534     },
17535     
17536     getValue: function()
17537     {
17538         return this.formatDate(this.date);
17539     },
17540     
17541     fireKey: function(e)
17542     {
17543         if (!this.picker().isVisible()){
17544             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17545                 this.show();
17546             }
17547             return;
17548         }
17549         
17550         var dateChanged = false,
17551         dir, day, month,
17552         newDate, newViewDate;
17553         
17554         switch(e.keyCode){
17555             case 27: // escape
17556                 this.hide();
17557                 e.preventDefault();
17558                 break;
17559             case 37: // left
17560             case 39: // right
17561                 if (!this.keyboardNavigation) {
17562                     break;
17563                 }
17564                 dir = e.keyCode == 37 ? -1 : 1;
17565                 
17566                 if (e.ctrlKey){
17567                     newDate = this.moveYear(this.date, dir);
17568                     newViewDate = this.moveYear(this.viewDate, dir);
17569                 } else if (e.shiftKey){
17570                     newDate = this.moveMonth(this.date, dir);
17571                     newViewDate = this.moveMonth(this.viewDate, dir);
17572                 } else {
17573                     newDate = new Date(this.date);
17574                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17575                     newViewDate = new Date(this.viewDate);
17576                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17577                 }
17578                 if (this.dateWithinRange(newDate)){
17579                     this.date = newDate;
17580                     this.viewDate = newViewDate;
17581                     this.setValue(this.formatDate(this.date));
17582 //                    this.update();
17583                     e.preventDefault();
17584                     dateChanged = true;
17585                 }
17586                 break;
17587             case 38: // up
17588             case 40: // down
17589                 if (!this.keyboardNavigation) {
17590                     break;
17591                 }
17592                 dir = e.keyCode == 38 ? -1 : 1;
17593                 if (e.ctrlKey){
17594                     newDate = this.moveYear(this.date, dir);
17595                     newViewDate = this.moveYear(this.viewDate, dir);
17596                 } else if (e.shiftKey){
17597                     newDate = this.moveMonth(this.date, dir);
17598                     newViewDate = this.moveMonth(this.viewDate, dir);
17599                 } else {
17600                     newDate = new Date(this.date);
17601                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17602                     newViewDate = new Date(this.viewDate);
17603                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17604                 }
17605                 if (this.dateWithinRange(newDate)){
17606                     this.date = newDate;
17607                     this.viewDate = newViewDate;
17608                     this.setValue(this.formatDate(this.date));
17609 //                    this.update();
17610                     e.preventDefault();
17611                     dateChanged = true;
17612                 }
17613                 break;
17614             case 13: // enter
17615                 this.setValue(this.formatDate(this.date));
17616                 this.hide();
17617                 e.preventDefault();
17618                 break;
17619             case 9: // tab
17620                 this.setValue(this.formatDate(this.date));
17621                 this.hide();
17622                 break;
17623             case 16: // shift
17624             case 17: // ctrl
17625             case 18: // alt
17626                 break;
17627             default :
17628                 this.hide();
17629                 
17630         }
17631     },
17632     
17633     
17634     onClick: function(e) 
17635     {
17636         e.stopPropagation();
17637         e.preventDefault();
17638         
17639         var target = e.getTarget();
17640         
17641         if(target.nodeName.toLowerCase() === 'i'){
17642             target = Roo.get(target).dom.parentNode;
17643         }
17644         
17645         var nodeName = target.nodeName;
17646         var className = target.className;
17647         var html = target.innerHTML;
17648         //Roo.log(nodeName);
17649         
17650         switch(nodeName.toLowerCase()) {
17651             case 'th':
17652                 switch(className) {
17653                     case 'switch':
17654                         this.showMode(1);
17655                         break;
17656                     case 'prev':
17657                     case 'next':
17658                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17659                         switch(this.viewMode){
17660                                 case 0:
17661                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17662                                         break;
17663                                 case 1:
17664                                 case 2:
17665                                         this.viewDate = this.moveYear(this.viewDate, dir);
17666                                         break;
17667                         }
17668                         this.fill();
17669                         break;
17670                     case 'today':
17671                         var date = new Date();
17672                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17673 //                        this.fill()
17674                         this.setValue(this.formatDate(this.date));
17675                         
17676                         this.hide();
17677                         break;
17678                 }
17679                 break;
17680             case 'span':
17681                 if (className.indexOf('disabled') < 0) {
17682                     this.viewDate.setUTCDate(1);
17683                     if (className.indexOf('month') > -1) {
17684                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17685                     } else {
17686                         var year = parseInt(html, 10) || 0;
17687                         this.viewDate.setUTCFullYear(year);
17688                         
17689                     }
17690                     
17691                     if(this.singleMode){
17692                         this.setValue(this.formatDate(this.viewDate));
17693                         this.hide();
17694                         return;
17695                     }
17696                     
17697                     this.showMode(-1);
17698                     this.fill();
17699                 }
17700                 break;
17701                 
17702             case 'td':
17703                 //Roo.log(className);
17704                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17705                     var day = parseInt(html, 10) || 1;
17706                     var year = this.viewDate.getUTCFullYear(),
17707                         month = this.viewDate.getUTCMonth();
17708
17709                     if (className.indexOf('old') > -1) {
17710                         if(month === 0 ){
17711                             month = 11;
17712                             year -= 1;
17713                         }else{
17714                             month -= 1;
17715                         }
17716                     } else if (className.indexOf('new') > -1) {
17717                         if (month == 11) {
17718                             month = 0;
17719                             year += 1;
17720                         } else {
17721                             month += 1;
17722                         }
17723                     }
17724                     //Roo.log([year,month,day]);
17725                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17726                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17727 //                    this.fill();
17728                     //Roo.log(this.formatDate(this.date));
17729                     this.setValue(this.formatDate(this.date));
17730                     this.hide();
17731                 }
17732                 break;
17733         }
17734     },
17735     
17736     setStartDate: function(startDate)
17737     {
17738         this.startDate = startDate || -Infinity;
17739         if (this.startDate !== -Infinity) {
17740             this.startDate = this.parseDate(this.startDate);
17741         }
17742         this.update();
17743         this.updateNavArrows();
17744     },
17745
17746     setEndDate: function(endDate)
17747     {
17748         this.endDate = endDate || Infinity;
17749         if (this.endDate !== Infinity) {
17750             this.endDate = this.parseDate(this.endDate);
17751         }
17752         this.update();
17753         this.updateNavArrows();
17754     },
17755     
17756     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17757     {
17758         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17759         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17760             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17761         }
17762         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17763             return parseInt(d, 10);
17764         });
17765         this.update();
17766         this.updateNavArrows();
17767     },
17768     
17769     updateNavArrows: function() 
17770     {
17771         if(this.singleMode){
17772             return;
17773         }
17774         
17775         var d = new Date(this.viewDate),
17776         year = d.getUTCFullYear(),
17777         month = d.getUTCMonth();
17778         
17779         Roo.each(this.picker().select('.prev', true).elements, function(v){
17780             v.show();
17781             switch (this.viewMode) {
17782                 case 0:
17783
17784                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17785                         v.hide();
17786                     }
17787                     break;
17788                 case 1:
17789                 case 2:
17790                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17791                         v.hide();
17792                     }
17793                     break;
17794             }
17795         });
17796         
17797         Roo.each(this.picker().select('.next', true).elements, function(v){
17798             v.show();
17799             switch (this.viewMode) {
17800                 case 0:
17801
17802                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17803                         v.hide();
17804                     }
17805                     break;
17806                 case 1:
17807                 case 2:
17808                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17809                         v.hide();
17810                     }
17811                     break;
17812             }
17813         })
17814     },
17815     
17816     moveMonth: function(date, dir)
17817     {
17818         if (!dir) {
17819             return date;
17820         }
17821         var new_date = new Date(date.valueOf()),
17822         day = new_date.getUTCDate(),
17823         month = new_date.getUTCMonth(),
17824         mag = Math.abs(dir),
17825         new_month, test;
17826         dir = dir > 0 ? 1 : -1;
17827         if (mag == 1){
17828             test = dir == -1
17829             // If going back one month, make sure month is not current month
17830             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17831             ? function(){
17832                 return new_date.getUTCMonth() == month;
17833             }
17834             // If going forward one month, make sure month is as expected
17835             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17836             : function(){
17837                 return new_date.getUTCMonth() != new_month;
17838             };
17839             new_month = month + dir;
17840             new_date.setUTCMonth(new_month);
17841             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17842             if (new_month < 0 || new_month > 11) {
17843                 new_month = (new_month + 12) % 12;
17844             }
17845         } else {
17846             // For magnitudes >1, move one month at a time...
17847             for (var i=0; i<mag; i++) {
17848                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17849                 new_date = this.moveMonth(new_date, dir);
17850             }
17851             // ...then reset the day, keeping it in the new month
17852             new_month = new_date.getUTCMonth();
17853             new_date.setUTCDate(day);
17854             test = function(){
17855                 return new_month != new_date.getUTCMonth();
17856             };
17857         }
17858         // Common date-resetting loop -- if date is beyond end of month, make it
17859         // end of month
17860         while (test()){
17861             new_date.setUTCDate(--day);
17862             new_date.setUTCMonth(new_month);
17863         }
17864         return new_date;
17865     },
17866
17867     moveYear: function(date, dir)
17868     {
17869         return this.moveMonth(date, dir*12);
17870     },
17871
17872     dateWithinRange: function(date)
17873     {
17874         return date >= this.startDate && date <= this.endDate;
17875     },
17876
17877     
17878     remove: function() 
17879     {
17880         this.picker().remove();
17881     }
17882    
17883 });
17884
17885 Roo.apply(Roo.bootstrap.DateField,  {
17886     
17887     head : {
17888         tag: 'thead',
17889         cn: [
17890         {
17891             tag: 'tr',
17892             cn: [
17893             {
17894                 tag: 'th',
17895                 cls: 'prev',
17896                 html: '<i class="fa fa-arrow-left"/>'
17897             },
17898             {
17899                 tag: 'th',
17900                 cls: 'switch',
17901                 colspan: '5'
17902             },
17903             {
17904                 tag: 'th',
17905                 cls: 'next',
17906                 html: '<i class="fa fa-arrow-right"/>'
17907             }
17908
17909             ]
17910         }
17911         ]
17912     },
17913     
17914     content : {
17915         tag: 'tbody',
17916         cn: [
17917         {
17918             tag: 'tr',
17919             cn: [
17920             {
17921                 tag: 'td',
17922                 colspan: '7'
17923             }
17924             ]
17925         }
17926         ]
17927     },
17928     
17929     footer : {
17930         tag: 'tfoot',
17931         cn: [
17932         {
17933             tag: 'tr',
17934             cn: [
17935             {
17936                 tag: 'th',
17937                 colspan: '7',
17938                 cls: 'today'
17939             }
17940                     
17941             ]
17942         }
17943         ]
17944     },
17945     
17946     dates:{
17947         en: {
17948             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17949             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17950             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17951             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17952             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17953             today: "Today"
17954         }
17955     },
17956     
17957     modes: [
17958     {
17959         clsName: 'days',
17960         navFnc: 'Month',
17961         navStep: 1
17962     },
17963     {
17964         clsName: 'months',
17965         navFnc: 'FullYear',
17966         navStep: 1
17967     },
17968     {
17969         clsName: 'years',
17970         navFnc: 'FullYear',
17971         navStep: 10
17972     }]
17973 });
17974
17975 Roo.apply(Roo.bootstrap.DateField,  {
17976   
17977     template : {
17978         tag: 'div',
17979         cls: 'datepicker dropdown-menu roo-dynamic',
17980         cn: [
17981         {
17982             tag: 'div',
17983             cls: 'datepicker-days',
17984             cn: [
17985             {
17986                 tag: 'table',
17987                 cls: 'table-condensed',
17988                 cn:[
17989                 Roo.bootstrap.DateField.head,
17990                 {
17991                     tag: 'tbody'
17992                 },
17993                 Roo.bootstrap.DateField.footer
17994                 ]
17995             }
17996             ]
17997         },
17998         {
17999             tag: 'div',
18000             cls: 'datepicker-months',
18001             cn: [
18002             {
18003                 tag: 'table',
18004                 cls: 'table-condensed',
18005                 cn:[
18006                 Roo.bootstrap.DateField.head,
18007                 Roo.bootstrap.DateField.content,
18008                 Roo.bootstrap.DateField.footer
18009                 ]
18010             }
18011             ]
18012         },
18013         {
18014             tag: 'div',
18015             cls: 'datepicker-years',
18016             cn: [
18017             {
18018                 tag: 'table',
18019                 cls: 'table-condensed',
18020                 cn:[
18021                 Roo.bootstrap.DateField.head,
18022                 Roo.bootstrap.DateField.content,
18023                 Roo.bootstrap.DateField.footer
18024                 ]
18025             }
18026             ]
18027         }
18028         ]
18029     }
18030 });
18031
18032  
18033
18034  /*
18035  * - LGPL
18036  *
18037  * TimeField
18038  * 
18039  */
18040
18041 /**
18042  * @class Roo.bootstrap.TimeField
18043  * @extends Roo.bootstrap.Input
18044  * Bootstrap DateField class
18045  * 
18046  * 
18047  * @constructor
18048  * Create a new TimeField
18049  * @param {Object} config The config object
18050  */
18051
18052 Roo.bootstrap.TimeField = function(config){
18053     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18054     this.addEvents({
18055             /**
18056              * @event show
18057              * Fires when this field show.
18058              * @param {Roo.bootstrap.DateField} thisthis
18059              * @param {Mixed} date The date value
18060              */
18061             show : true,
18062             /**
18063              * @event show
18064              * Fires when this field hide.
18065              * @param {Roo.bootstrap.DateField} this
18066              * @param {Mixed} date The date value
18067              */
18068             hide : true,
18069             /**
18070              * @event select
18071              * Fires when select a date.
18072              * @param {Roo.bootstrap.DateField} this
18073              * @param {Mixed} date The date value
18074              */
18075             select : true
18076         });
18077 };
18078
18079 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18080     
18081     /**
18082      * @cfg {String} format
18083      * The default time format string which can be overriden for localization support.  The format must be
18084      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18085      */
18086     format : "H:i",
18087        
18088     onRender: function(ct, position)
18089     {
18090         
18091         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18092                 
18093         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18094         
18095         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18096         
18097         this.pop = this.picker().select('>.datepicker-time',true).first();
18098         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18099         
18100         this.picker().on('mousedown', this.onMousedown, this);
18101         this.picker().on('click', this.onClick, this);
18102         
18103         this.picker().addClass('datepicker-dropdown');
18104     
18105         this.fillTime();
18106         this.update();
18107             
18108         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18109         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18110         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18111         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18112         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18113         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18114
18115     },
18116     
18117     fireKey: function(e){
18118         if (!this.picker().isVisible()){
18119             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18120                 this.show();
18121             }
18122             return;
18123         }
18124
18125         e.preventDefault();
18126         
18127         switch(e.keyCode){
18128             case 27: // escape
18129                 this.hide();
18130                 break;
18131             case 37: // left
18132             case 39: // right
18133                 this.onTogglePeriod();
18134                 break;
18135             case 38: // up
18136                 this.onIncrementMinutes();
18137                 break;
18138             case 40: // down
18139                 this.onDecrementMinutes();
18140                 break;
18141             case 13: // enter
18142             case 9: // tab
18143                 this.setTime();
18144                 break;
18145         }
18146     },
18147     
18148     onClick: function(e) {
18149         e.stopPropagation();
18150         e.preventDefault();
18151     },
18152     
18153     picker : function()
18154     {
18155         return this.el.select('.datepicker', true).first();
18156     },
18157     
18158     fillTime: function()
18159     {    
18160         var time = this.pop.select('tbody', true).first();
18161         
18162         time.dom.innerHTML = '';
18163         
18164         time.createChild({
18165             tag: 'tr',
18166             cn: [
18167                 {
18168                     tag: 'td',
18169                     cn: [
18170                         {
18171                             tag: 'a',
18172                             href: '#',
18173                             cls: 'btn',
18174                             cn: [
18175                                 {
18176                                     tag: 'span',
18177                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18178                                 }
18179                             ]
18180                         } 
18181                     ]
18182                 },
18183                 {
18184                     tag: 'td',
18185                     cls: 'separator'
18186                 },
18187                 {
18188                     tag: 'td',
18189                     cn: [
18190                         {
18191                             tag: 'a',
18192                             href: '#',
18193                             cls: 'btn',
18194                             cn: [
18195                                 {
18196                                     tag: 'span',
18197                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18198                                 }
18199                             ]
18200                         }
18201                     ]
18202                 },
18203                 {
18204                     tag: 'td',
18205                     cls: 'separator'
18206                 }
18207             ]
18208         });
18209         
18210         time.createChild({
18211             tag: 'tr',
18212             cn: [
18213                 {
18214                     tag: 'td',
18215                     cn: [
18216                         {
18217                             tag: 'span',
18218                             cls: 'timepicker-hour',
18219                             html: '00'
18220                         }  
18221                     ]
18222                 },
18223                 {
18224                     tag: 'td',
18225                     cls: 'separator',
18226                     html: ':'
18227                 },
18228                 {
18229                     tag: 'td',
18230                     cn: [
18231                         {
18232                             tag: 'span',
18233                             cls: 'timepicker-minute',
18234                             html: '00'
18235                         }  
18236                     ]
18237                 },
18238                 {
18239                     tag: 'td',
18240                     cls: 'separator'
18241                 },
18242                 {
18243                     tag: 'td',
18244                     cn: [
18245                         {
18246                             tag: 'button',
18247                             type: 'button',
18248                             cls: 'btn btn-primary period',
18249                             html: 'AM'
18250                             
18251                         }
18252                     ]
18253                 }
18254             ]
18255         });
18256         
18257         time.createChild({
18258             tag: 'tr',
18259             cn: [
18260                 {
18261                     tag: 'td',
18262                     cn: [
18263                         {
18264                             tag: 'a',
18265                             href: '#',
18266                             cls: 'btn',
18267                             cn: [
18268                                 {
18269                                     tag: 'span',
18270                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18271                                 }
18272                             ]
18273                         }
18274                     ]
18275                 },
18276                 {
18277                     tag: 'td',
18278                     cls: 'separator'
18279                 },
18280                 {
18281                     tag: 'td',
18282                     cn: [
18283                         {
18284                             tag: 'a',
18285                             href: '#',
18286                             cls: 'btn',
18287                             cn: [
18288                                 {
18289                                     tag: 'span',
18290                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18291                                 }
18292                             ]
18293                         }
18294                     ]
18295                 },
18296                 {
18297                     tag: 'td',
18298                     cls: 'separator'
18299                 }
18300             ]
18301         });
18302         
18303     },
18304     
18305     update: function()
18306     {
18307         
18308         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18309         
18310         this.fill();
18311     },
18312     
18313     fill: function() 
18314     {
18315         var hours = this.time.getHours();
18316         var minutes = this.time.getMinutes();
18317         var period = 'AM';
18318         
18319         if(hours > 11){
18320             period = 'PM';
18321         }
18322         
18323         if(hours == 0){
18324             hours = 12;
18325         }
18326         
18327         
18328         if(hours > 12){
18329             hours = hours - 12;
18330         }
18331         
18332         if(hours < 10){
18333             hours = '0' + hours;
18334         }
18335         
18336         if(minutes < 10){
18337             minutes = '0' + minutes;
18338         }
18339         
18340         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18341         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18342         this.pop.select('button', true).first().dom.innerHTML = period;
18343         
18344     },
18345     
18346     place: function()
18347     {   
18348         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18349         
18350         var cls = ['bottom'];
18351         
18352         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18353             cls.pop();
18354             cls.push('top');
18355         }
18356         
18357         cls.push('right');
18358         
18359         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18360             cls.pop();
18361             cls.push('left');
18362         }
18363         
18364         this.picker().addClass(cls.join('-'));
18365         
18366         var _this = this;
18367         
18368         Roo.each(cls, function(c){
18369             if(c == 'bottom'){
18370                 _this.picker().setTop(_this.inputEl().getHeight());
18371                 return;
18372             }
18373             if(c == 'top'){
18374                 _this.picker().setTop(0 - _this.picker().getHeight());
18375                 return;
18376             }
18377             
18378             if(c == 'left'){
18379                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18380                 return;
18381             }
18382             if(c == 'right'){
18383                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18384                 return;
18385             }
18386         });
18387         
18388     },
18389   
18390     onFocus : function()
18391     {
18392         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18393         this.show();
18394     },
18395     
18396     onBlur : function()
18397     {
18398         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18399         this.hide();
18400     },
18401     
18402     show : function()
18403     {
18404         this.picker().show();
18405         this.pop.show();
18406         this.update();
18407         this.place();
18408         
18409         this.fireEvent('show', this, this.date);
18410     },
18411     
18412     hide : function()
18413     {
18414         this.picker().hide();
18415         this.pop.hide();
18416         
18417         this.fireEvent('hide', this, this.date);
18418     },
18419     
18420     setTime : function()
18421     {
18422         this.hide();
18423         this.setValue(this.time.format(this.format));
18424         
18425         this.fireEvent('select', this, this.date);
18426         
18427         
18428     },
18429     
18430     onMousedown: function(e){
18431         e.stopPropagation();
18432         e.preventDefault();
18433     },
18434     
18435     onIncrementHours: function()
18436     {
18437         Roo.log('onIncrementHours');
18438         this.time = this.time.add(Date.HOUR, 1);
18439         this.update();
18440         
18441     },
18442     
18443     onDecrementHours: function()
18444     {
18445         Roo.log('onDecrementHours');
18446         this.time = this.time.add(Date.HOUR, -1);
18447         this.update();
18448     },
18449     
18450     onIncrementMinutes: function()
18451     {
18452         Roo.log('onIncrementMinutes');
18453         this.time = this.time.add(Date.MINUTE, 1);
18454         this.update();
18455     },
18456     
18457     onDecrementMinutes: function()
18458     {
18459         Roo.log('onDecrementMinutes');
18460         this.time = this.time.add(Date.MINUTE, -1);
18461         this.update();
18462     },
18463     
18464     onTogglePeriod: function()
18465     {
18466         Roo.log('onTogglePeriod');
18467         this.time = this.time.add(Date.HOUR, 12);
18468         this.update();
18469     }
18470     
18471    
18472 });
18473
18474 Roo.apply(Roo.bootstrap.TimeField,  {
18475     
18476     content : {
18477         tag: 'tbody',
18478         cn: [
18479             {
18480                 tag: 'tr',
18481                 cn: [
18482                 {
18483                     tag: 'td',
18484                     colspan: '7'
18485                 }
18486                 ]
18487             }
18488         ]
18489     },
18490     
18491     footer : {
18492         tag: 'tfoot',
18493         cn: [
18494             {
18495                 tag: 'tr',
18496                 cn: [
18497                 {
18498                     tag: 'th',
18499                     colspan: '7',
18500                     cls: '',
18501                     cn: [
18502                         {
18503                             tag: 'button',
18504                             cls: 'btn btn-info ok',
18505                             html: 'OK'
18506                         }
18507                     ]
18508                 }
18509
18510                 ]
18511             }
18512         ]
18513     }
18514 });
18515
18516 Roo.apply(Roo.bootstrap.TimeField,  {
18517   
18518     template : {
18519         tag: 'div',
18520         cls: 'datepicker dropdown-menu',
18521         cn: [
18522             {
18523                 tag: 'div',
18524                 cls: 'datepicker-time',
18525                 cn: [
18526                 {
18527                     tag: 'table',
18528                     cls: 'table-condensed',
18529                     cn:[
18530                     Roo.bootstrap.TimeField.content,
18531                     Roo.bootstrap.TimeField.footer
18532                     ]
18533                 }
18534                 ]
18535             }
18536         ]
18537     }
18538 });
18539
18540  
18541
18542  /*
18543  * - LGPL
18544  *
18545  * MonthField
18546  * 
18547  */
18548
18549 /**
18550  * @class Roo.bootstrap.MonthField
18551  * @extends Roo.bootstrap.Input
18552  * Bootstrap MonthField class
18553  * 
18554  * @cfg {String} language default en
18555  * 
18556  * @constructor
18557  * Create a new MonthField
18558  * @param {Object} config The config object
18559  */
18560
18561 Roo.bootstrap.MonthField = function(config){
18562     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18563     
18564     this.addEvents({
18565         /**
18566          * @event show
18567          * Fires when this field show.
18568          * @param {Roo.bootstrap.MonthField} this
18569          * @param {Mixed} date The date value
18570          */
18571         show : true,
18572         /**
18573          * @event show
18574          * Fires when this field hide.
18575          * @param {Roo.bootstrap.MonthField} this
18576          * @param {Mixed} date The date value
18577          */
18578         hide : true,
18579         /**
18580          * @event select
18581          * Fires when select a date.
18582          * @param {Roo.bootstrap.MonthField} this
18583          * @param {String} oldvalue The old value
18584          * @param {String} newvalue The new value
18585          */
18586         select : true
18587     });
18588 };
18589
18590 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18591     
18592     onRender: function(ct, position)
18593     {
18594         
18595         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18596         
18597         this.language = this.language || 'en';
18598         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18599         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18600         
18601         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18602         this.isInline = false;
18603         this.isInput = true;
18604         this.component = this.el.select('.add-on', true).first() || false;
18605         this.component = (this.component && this.component.length === 0) ? false : this.component;
18606         this.hasInput = this.component && this.inputEL().length;
18607         
18608         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18609         
18610         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18611         
18612         this.picker().on('mousedown', this.onMousedown, this);
18613         this.picker().on('click', this.onClick, this);
18614         
18615         this.picker().addClass('datepicker-dropdown');
18616         
18617         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18618             v.setStyle('width', '189px');
18619         });
18620         
18621         this.fillMonths();
18622         
18623         this.update();
18624         
18625         if(this.isInline) {
18626             this.show();
18627         }
18628         
18629     },
18630     
18631     setValue: function(v, suppressEvent)
18632     {   
18633         var o = this.getValue();
18634         
18635         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18636         
18637         this.update();
18638
18639         if(suppressEvent !== true){
18640             this.fireEvent('select', this, o, v);
18641         }
18642         
18643     },
18644     
18645     getValue: function()
18646     {
18647         return this.value;
18648     },
18649     
18650     onClick: function(e) 
18651     {
18652         e.stopPropagation();
18653         e.preventDefault();
18654         
18655         var target = e.getTarget();
18656         
18657         if(target.nodeName.toLowerCase() === 'i'){
18658             target = Roo.get(target).dom.parentNode;
18659         }
18660         
18661         var nodeName = target.nodeName;
18662         var className = target.className;
18663         var html = target.innerHTML;
18664         
18665         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18666             return;
18667         }
18668         
18669         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18670         
18671         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18672         
18673         this.hide();
18674                         
18675     },
18676     
18677     picker : function()
18678     {
18679         return this.pickerEl;
18680     },
18681     
18682     fillMonths: function()
18683     {    
18684         var i = 0;
18685         var months = this.picker().select('>.datepicker-months td', true).first();
18686         
18687         months.dom.innerHTML = '';
18688         
18689         while (i < 12) {
18690             var month = {
18691                 tag: 'span',
18692                 cls: 'month',
18693                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18694             };
18695             
18696             months.createChild(month);
18697         }
18698         
18699     },
18700     
18701     update: function()
18702     {
18703         var _this = this;
18704         
18705         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18706             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18707         }
18708         
18709         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18710             e.removeClass('active');
18711             
18712             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18713                 e.addClass('active');
18714             }
18715         })
18716     },
18717     
18718     place: function()
18719     {
18720         if(this.isInline) {
18721             return;
18722         }
18723         
18724         this.picker().removeClass(['bottom', 'top']);
18725         
18726         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18727             /*
18728              * place to the top of element!
18729              *
18730              */
18731             
18732             this.picker().addClass('top');
18733             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18734             
18735             return;
18736         }
18737         
18738         this.picker().addClass('bottom');
18739         
18740         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18741     },
18742     
18743     onFocus : function()
18744     {
18745         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18746         this.show();
18747     },
18748     
18749     onBlur : function()
18750     {
18751         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18752         
18753         var d = this.inputEl().getValue();
18754         
18755         this.setValue(d);
18756                 
18757         this.hide();
18758     },
18759     
18760     show : function()
18761     {
18762         this.picker().show();
18763         this.picker().select('>.datepicker-months', true).first().show();
18764         this.update();
18765         this.place();
18766         
18767         this.fireEvent('show', this, this.date);
18768     },
18769     
18770     hide : function()
18771     {
18772         if(this.isInline) {
18773             return;
18774         }
18775         this.picker().hide();
18776         this.fireEvent('hide', this, this.date);
18777         
18778     },
18779     
18780     onMousedown: function(e)
18781     {
18782         e.stopPropagation();
18783         e.preventDefault();
18784     },
18785     
18786     keyup: function(e)
18787     {
18788         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18789         this.update();
18790     },
18791
18792     fireKey: function(e)
18793     {
18794         if (!this.picker().isVisible()){
18795             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18796                 this.show();
18797             }
18798             return;
18799         }
18800         
18801         var dir;
18802         
18803         switch(e.keyCode){
18804             case 27: // escape
18805                 this.hide();
18806                 e.preventDefault();
18807                 break;
18808             case 37: // left
18809             case 39: // right
18810                 dir = e.keyCode == 37 ? -1 : 1;
18811                 
18812                 this.vIndex = this.vIndex + dir;
18813                 
18814                 if(this.vIndex < 0){
18815                     this.vIndex = 0;
18816                 }
18817                 
18818                 if(this.vIndex > 11){
18819                     this.vIndex = 11;
18820                 }
18821                 
18822                 if(isNaN(this.vIndex)){
18823                     this.vIndex = 0;
18824                 }
18825                 
18826                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18827                 
18828                 break;
18829             case 38: // up
18830             case 40: // down
18831                 
18832                 dir = e.keyCode == 38 ? -1 : 1;
18833                 
18834                 this.vIndex = this.vIndex + dir * 4;
18835                 
18836                 if(this.vIndex < 0){
18837                     this.vIndex = 0;
18838                 }
18839                 
18840                 if(this.vIndex > 11){
18841                     this.vIndex = 11;
18842                 }
18843                 
18844                 if(isNaN(this.vIndex)){
18845                     this.vIndex = 0;
18846                 }
18847                 
18848                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18849                 break;
18850                 
18851             case 13: // enter
18852                 
18853                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18854                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18855                 }
18856                 
18857                 this.hide();
18858                 e.preventDefault();
18859                 break;
18860             case 9: // tab
18861                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18862                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18863                 }
18864                 this.hide();
18865                 break;
18866             case 16: // shift
18867             case 17: // ctrl
18868             case 18: // alt
18869                 break;
18870             default :
18871                 this.hide();
18872                 
18873         }
18874     },
18875     
18876     remove: function() 
18877     {
18878         this.picker().remove();
18879     }
18880    
18881 });
18882
18883 Roo.apply(Roo.bootstrap.MonthField,  {
18884     
18885     content : {
18886         tag: 'tbody',
18887         cn: [
18888         {
18889             tag: 'tr',
18890             cn: [
18891             {
18892                 tag: 'td',
18893                 colspan: '7'
18894             }
18895             ]
18896         }
18897         ]
18898     },
18899     
18900     dates:{
18901         en: {
18902             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18903             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18904         }
18905     }
18906 });
18907
18908 Roo.apply(Roo.bootstrap.MonthField,  {
18909   
18910     template : {
18911         tag: 'div',
18912         cls: 'datepicker dropdown-menu roo-dynamic',
18913         cn: [
18914             {
18915                 tag: 'div',
18916                 cls: 'datepicker-months',
18917                 cn: [
18918                 {
18919                     tag: 'table',
18920                     cls: 'table-condensed',
18921                     cn:[
18922                         Roo.bootstrap.DateField.content
18923                     ]
18924                 }
18925                 ]
18926             }
18927         ]
18928     }
18929 });
18930
18931  
18932
18933  
18934  /*
18935  * - LGPL
18936  *
18937  * CheckBox
18938  * 
18939  */
18940
18941 /**
18942  * @class Roo.bootstrap.CheckBox
18943  * @extends Roo.bootstrap.Input
18944  * Bootstrap CheckBox class
18945  * 
18946  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18947  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18948  * @cfg {String} boxLabel The text that appears beside the checkbox
18949  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18950  * @cfg {Boolean} checked initnal the element
18951  * @cfg {Boolean} inline inline the element (default false)
18952  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18953  * 
18954  * @constructor
18955  * Create a new CheckBox
18956  * @param {Object} config The config object
18957  */
18958
18959 Roo.bootstrap.CheckBox = function(config){
18960     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18961    
18962     this.addEvents({
18963         /**
18964         * @event check
18965         * Fires when the element is checked or unchecked.
18966         * @param {Roo.bootstrap.CheckBox} this This input
18967         * @param {Boolean} checked The new checked value
18968         */
18969        check : true
18970     });
18971     
18972 };
18973
18974 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18975   
18976     inputType: 'checkbox',
18977     inputValue: 1,
18978     valueOff: 0,
18979     boxLabel: false,
18980     checked: false,
18981     weight : false,
18982     inline: false,
18983     
18984     getAutoCreate : function()
18985     {
18986         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18987         
18988         var id = Roo.id();
18989         
18990         var cfg = {};
18991         
18992         cfg.cls = 'form-group ' + this.inputType; //input-group
18993         
18994         if(this.inline){
18995             cfg.cls += ' ' + this.inputType + '-inline';
18996         }
18997         
18998         var input =  {
18999             tag: 'input',
19000             id : id,
19001             type : this.inputType,
19002             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19003             cls : 'roo-' + this.inputType, //'form-box',
19004             placeholder : this.placeholder || ''
19005             
19006         };
19007         
19008         if (this.weight) { // Validity check?
19009             cfg.cls += " " + this.inputType + "-" + this.weight;
19010         }
19011         
19012         if (this.disabled) {
19013             input.disabled=true;
19014         }
19015         
19016         if(this.checked){
19017             input.checked = this.checked;
19018         }
19019         
19020         if (this.name) {
19021             input.name = this.name;
19022         }
19023         
19024         if (this.size) {
19025             input.cls += ' input-' + this.size;
19026         }
19027         
19028         var settings=this;
19029         
19030         ['xs','sm','md','lg'].map(function(size){
19031             if (settings[size]) {
19032                 cfg.cls += ' col-' + size + '-' + settings[size];
19033             }
19034         });
19035         
19036         var inputblock = input;
19037          
19038         if (this.before || this.after) {
19039             
19040             inputblock = {
19041                 cls : 'input-group',
19042                 cn :  [] 
19043             };
19044             
19045             if (this.before) {
19046                 inputblock.cn.push({
19047                     tag :'span',
19048                     cls : 'input-group-addon',
19049                     html : this.before
19050                 });
19051             }
19052             
19053             inputblock.cn.push(input);
19054             
19055             if (this.after) {
19056                 inputblock.cn.push({
19057                     tag :'span',
19058                     cls : 'input-group-addon',
19059                     html : this.after
19060                 });
19061             }
19062             
19063         }
19064         
19065         if (align ==='left' && this.fieldLabel.length) {
19066 //                Roo.log("left and has label");
19067                 cfg.cn = [
19068                     
19069                     {
19070                         tag: 'label',
19071                         'for' :  id,
19072                         cls : 'control-label col-md-' + this.labelWidth,
19073                         html : this.fieldLabel
19074                         
19075                     },
19076                     {
19077                         cls : "col-md-" + (12 - this.labelWidth), 
19078                         cn: [
19079                             inputblock
19080                         ]
19081                     }
19082                     
19083                 ];
19084         } else if ( this.fieldLabel.length) {
19085 //                Roo.log(" label");
19086                 cfg.cn = [
19087                    
19088                     {
19089                         tag: this.boxLabel ? 'span' : 'label',
19090                         'for': id,
19091                         cls: 'control-label box-input-label',
19092                         //cls : 'input-group-addon',
19093                         html : this.fieldLabel
19094                         
19095                     },
19096                     
19097                     inputblock
19098                     
19099                 ];
19100
19101         } else {
19102             
19103 //                Roo.log(" no label && no align");
19104                 cfg.cn = [  inputblock ] ;
19105                 
19106                 
19107         }
19108         
19109         if(this.boxLabel){
19110              var boxLabelCfg = {
19111                 tag: 'label',
19112                 //'for': id, // box label is handled by onclick - so no for...
19113                 cls: 'box-label',
19114                 html: this.boxLabel
19115             };
19116             
19117             if(this.tooltip){
19118                 boxLabelCfg.tooltip = this.tooltip;
19119             }
19120              
19121             cfg.cn.push(boxLabelCfg);
19122         }
19123         
19124         
19125        
19126         return cfg;
19127         
19128     },
19129     
19130     /**
19131      * return the real input element.
19132      */
19133     inputEl: function ()
19134     {
19135         return this.el.select('input.roo-' + this.inputType,true).first();
19136     },
19137     
19138     labelEl: function()
19139     {
19140         return this.el.select('label.control-label',true).first();
19141     },
19142     /* depricated... */
19143     
19144     label: function()
19145     {
19146         return this.labelEl();
19147     },
19148     
19149     boxLabelEl: function()
19150     {
19151         return this.el.select('label.box-label',true).first();
19152     },
19153     
19154     initEvents : function()
19155     {
19156 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19157         
19158         this.inputEl().on('click', this.onClick,  this);
19159         
19160         if (this.boxLabel) { 
19161             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19162         }
19163         
19164         this.startValue = this.getValue();
19165         
19166         if(this.groupId){
19167             Roo.bootstrap.CheckBox.register(this);
19168         }
19169     },
19170     
19171     onClick : function()
19172     {   
19173         this.setChecked(!this.checked);
19174     },
19175     
19176     setChecked : function(state,suppressEvent)
19177     {
19178         this.startValue = this.getValue();
19179         
19180         if(this.inputType == 'radio'){
19181             
19182             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19183                 e.dom.checked = false;
19184             });
19185             
19186             this.inputEl().dom.checked = true;
19187             
19188             this.inputEl().dom.value = this.inputValue;
19189             
19190             if(suppressEvent !== true){
19191                 this.fireEvent('check', this, true);
19192             }
19193             
19194             this.validate();
19195             
19196             return;
19197         }
19198         
19199         this.checked = state;
19200         
19201         this.inputEl().dom.checked = state;
19202         
19203         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19204         
19205         if(suppressEvent !== true){
19206             this.fireEvent('check', this, state);
19207         }
19208         
19209         this.validate();
19210     },
19211     
19212     getValue : function()
19213     {
19214         if(this.inputType == 'radio'){
19215             return this.getGroupValue();
19216         }
19217         
19218         return this.inputEl().getValue();
19219         
19220     },
19221     
19222     getGroupValue : function()
19223     {
19224         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19225             return '';
19226         }
19227         
19228         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19229     },
19230     
19231     setValue : function(v,suppressEvent)
19232     {
19233         if(this.inputType == 'radio'){
19234             this.setGroupValue(v, suppressEvent);
19235             return;
19236         }
19237         
19238         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19239         
19240         this.validate();
19241     },
19242     
19243     setGroupValue : function(v, suppressEvent)
19244     {
19245         this.startValue = this.getValue();
19246         
19247         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19248             e.dom.checked = false;
19249             
19250             if(e.dom.value == v){
19251                 e.dom.checked = true;
19252             }
19253         });
19254         
19255         if(suppressEvent !== true){
19256             this.fireEvent('check', this, true);
19257         }
19258
19259         this.validate();
19260         
19261         return;
19262     },
19263     
19264     validate : function()
19265     {
19266         if(
19267                 this.disabled || 
19268                 (this.inputType == 'radio' && this.validateRadio()) ||
19269                 (this.inputType == 'checkbox' && this.validateCheckbox())
19270         ){
19271             this.markValid();
19272             return true;
19273         }
19274         
19275         this.markInvalid();
19276         return false;
19277     },
19278     
19279     validateRadio : function()
19280     {
19281         var valid = false;
19282         
19283         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19284             if(!e.dom.checked){
19285                 return;
19286             }
19287             
19288             valid = true;
19289             
19290             return false;
19291         });
19292         
19293         return valid;
19294     },
19295     
19296     validateCheckbox : function()
19297     {
19298         if(!this.groupId){
19299             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19300         }
19301         
19302         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19303         
19304         if(!group){
19305             return false;
19306         }
19307         
19308         var r = false;
19309         
19310         for(var i in group){
19311             if(r){
19312                 break;
19313             }
19314             
19315             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19316         }
19317         
19318         return r;
19319     },
19320     
19321     /**
19322      * Mark this field as valid
19323      */
19324     markValid : function()
19325     {
19326         if(this.allowBlank){
19327             return;
19328         }
19329         
19330         var _this = this;
19331         
19332         this.fireEvent('valid', this);
19333         
19334         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19335         
19336         if(this.groupId){
19337             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19338         }
19339         
19340         if(label){
19341             label.markValid();
19342         }
19343         
19344         if(this.inputType == 'radio'){
19345             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19346                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19347                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19348             });
19349             
19350             return;
19351         }
19352         
19353         if(!this.groupId){
19354             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19355             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19356             return;
19357         }
19358         
19359         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19360             
19361         if(!group){
19362             return;
19363         }
19364         
19365         for(var i in group){
19366             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19367             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19368         }
19369     },
19370     
19371      /**
19372      * Mark this field as invalid
19373      * @param {String} msg The validation message
19374      */
19375     markInvalid : function(msg)
19376     {
19377         if(this.allowBlank){
19378             return;
19379         }
19380         
19381         var _this = this;
19382         
19383         this.fireEvent('invalid', this, msg);
19384         
19385         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19386         
19387         if(this.groupId){
19388             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19389         }
19390         
19391         if(label){
19392             label.markInvalid();
19393         }
19394             
19395         if(this.inputType == 'radio'){
19396             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19397                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19398                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19399             });
19400             
19401             return;
19402         }
19403         
19404         if(!this.groupId){
19405             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19406             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19407             return;
19408         }
19409         
19410         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19411         
19412         if(!group){
19413             return;
19414         }
19415         
19416         for(var i in group){
19417             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19418             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19419         }
19420         
19421     }
19422     
19423 });
19424
19425 Roo.apply(Roo.bootstrap.CheckBox, {
19426     
19427     groups: {},
19428     
19429      /**
19430     * register a CheckBox Group
19431     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19432     */
19433     register : function(checkbox)
19434     {
19435         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19436             this.groups[checkbox.groupId] = {};
19437         }
19438         
19439         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19440             return;
19441         }
19442         
19443         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19444         
19445     },
19446     /**
19447     * fetch a CheckBox Group based on the group ID
19448     * @param {string} the group ID
19449     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19450     */
19451     get: function(groupId) {
19452         if (typeof(this.groups[groupId]) == 'undefined') {
19453             return false;
19454         }
19455         
19456         return this.groups[groupId] ;
19457     }
19458     
19459     
19460 });
19461 /*
19462  * - LGPL
19463  *
19464  * Radio
19465  *
19466  *
19467  * not inline
19468  *<div class="radio">
19469   <label>
19470     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19471     Option one is this and that&mdash;be sure to include why it's great
19472   </label>
19473 </div>
19474  *
19475  *
19476  *inline
19477  *<span>
19478  *<label class="radio-inline">fieldLabel</label>
19479  *<label class="radio-inline">
19480   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19481 </label>
19482 <span>
19483  * 
19484  * 
19485  */
19486
19487 /**
19488  * @class Roo.bootstrap.Radio
19489  * @extends Roo.bootstrap.CheckBox
19490  * Bootstrap Radio class
19491
19492  * @constructor
19493  * Create a new Radio
19494  * @param {Object} config The config object
19495  */
19496
19497 Roo.bootstrap.Radio = function(config){
19498     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19499    
19500 };
19501
19502 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19503     
19504     inputType: 'radio',
19505     inputValue: '',
19506     valueOff: '',
19507     
19508     getAutoCreate : function()
19509     {
19510         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19511         align = align || 'left'; // default...
19512         
19513         
19514         
19515         var id = Roo.id();
19516         
19517         var cfg = {
19518                 tag : this.inline ? 'span' : 'div',
19519                 cls : '',
19520                 cn : []
19521         };
19522         
19523         var inline = this.inline ? ' radio-inline' : '';
19524         
19525         var lbl = {
19526                 tag: 'label' ,
19527                 // does not need for, as we wrap the input with it..
19528                 'for' : id,
19529                 cls : 'control-label box-label' + inline,
19530                 cn : []
19531         };
19532         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19533         
19534         var fieldLabel = {
19535             tag: 'label' ,
19536             //cls : 'control-label' + inline,
19537             html : this.fieldLabel,
19538             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19539         };
19540         
19541  
19542         
19543         
19544         var input =  {
19545             tag: 'input',
19546             id : id,
19547             type : this.inputType,
19548             //value : (!this.checked) ? this.valueOff : this.inputValue,
19549             value : this.inputValue,
19550             cls : 'roo-radio',
19551             placeholder : this.placeholder || '' // ?? needed????
19552             
19553         };
19554         if (this.weight) { // Validity check?
19555             input.cls += " radio-" + this.weight;
19556         }
19557         if (this.disabled) {
19558             input.disabled=true;
19559         }
19560         
19561         if(this.checked){
19562             input.checked = this.checked;
19563         }
19564         
19565         if (this.name) {
19566             input.name = this.name;
19567         }
19568         
19569         if (this.size) {
19570             input.cls += ' input-' + this.size;
19571         }
19572         
19573         //?? can span's inline have a width??
19574         
19575         var settings=this;
19576         ['xs','sm','md','lg'].map(function(size){
19577             if (settings[size]) {
19578                 cfg.cls += ' col-' + size + '-' + settings[size];
19579             }
19580         });
19581         
19582         var inputblock = input;
19583         
19584         if (this.before || this.after) {
19585             
19586             inputblock = {
19587                 cls : 'input-group',
19588                 tag : 'span',
19589                 cn :  [] 
19590             };
19591             if (this.before) {
19592                 inputblock.cn.push({
19593                     tag :'span',
19594                     cls : 'input-group-addon',
19595                     html : this.before
19596                 });
19597             }
19598             inputblock.cn.push(input);
19599             if (this.after) {
19600                 inputblock.cn.push({
19601                     tag :'span',
19602                     cls : 'input-group-addon',
19603                     html : this.after
19604                 });
19605             }
19606             
19607         };
19608         
19609         
19610         if (this.fieldLabel && this.fieldLabel.length) {
19611             cfg.cn.push(fieldLabel);
19612         }
19613        
19614         // normal bootstrap puts the input inside the label.
19615         // however with our styled version - it has to go after the input.
19616        
19617         //lbl.cn.push(inputblock);
19618         
19619         var lblwrap =  {
19620             tag: 'span',
19621             cls: 'radio' + inline,
19622             cn: [
19623                 inputblock,
19624                 lbl
19625             ]
19626         };
19627         
19628         cfg.cn.push( lblwrap);
19629         
19630         if(this.boxLabel){
19631             lbl.cn.push({
19632                 tag: 'span',
19633                 html: this.boxLabel
19634             })
19635         }
19636          
19637         
19638         return cfg;
19639         
19640     },
19641     
19642     initEvents : function()
19643     {
19644 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19645         
19646         this.inputEl().on('click', this.onClick,  this);
19647         if (this.boxLabel) {
19648             //Roo.log('find label');
19649             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19650         }
19651         
19652     },
19653     
19654     inputEl: function ()
19655     {
19656         return this.el.select('input.roo-radio',true).first();
19657     },
19658     onClick : function()
19659     {   
19660         Roo.log("click");
19661         this.setChecked(true);
19662     },
19663     
19664     setChecked : function(state,suppressEvent)
19665     {
19666         if(state){
19667             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19668                 v.dom.checked = false;
19669             });
19670         }
19671         Roo.log(this.inputEl().dom);
19672         this.checked = state;
19673         this.inputEl().dom.checked = state;
19674         
19675         if(suppressEvent !== true){
19676             this.fireEvent('check', this, state);
19677         }
19678         
19679         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19680         
19681     },
19682     
19683     getGroupValue : function()
19684     {
19685         var value = '';
19686         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19687             if(v.dom.checked == true){
19688                 value = v.dom.value;
19689             }
19690         });
19691         
19692         return value;
19693     },
19694     
19695     /**
19696      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19697      * @return {Mixed} value The field value
19698      */
19699     getValue : function(){
19700         return this.getGroupValue();
19701     }
19702     
19703 });
19704
19705  
19706 //<script type="text/javascript">
19707
19708 /*
19709  * Based  Ext JS Library 1.1.1
19710  * Copyright(c) 2006-2007, Ext JS, LLC.
19711  * LGPL
19712  *
19713  */
19714  
19715 /**
19716  * @class Roo.HtmlEditorCore
19717  * @extends Roo.Component
19718  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19719  *
19720  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19721  */
19722
19723 Roo.HtmlEditorCore = function(config){
19724     
19725     
19726     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19727     
19728     
19729     this.addEvents({
19730         /**
19731          * @event initialize
19732          * Fires when the editor is fully initialized (including the iframe)
19733          * @param {Roo.HtmlEditorCore} this
19734          */
19735         initialize: true,
19736         /**
19737          * @event activate
19738          * Fires when the editor is first receives the focus. Any insertion must wait
19739          * until after this event.
19740          * @param {Roo.HtmlEditorCore} this
19741          */
19742         activate: true,
19743          /**
19744          * @event beforesync
19745          * Fires before the textarea is updated with content from the editor iframe. Return false
19746          * to cancel the sync.
19747          * @param {Roo.HtmlEditorCore} this
19748          * @param {String} html
19749          */
19750         beforesync: true,
19751          /**
19752          * @event beforepush
19753          * Fires before the iframe editor is updated with content from the textarea. Return false
19754          * to cancel the push.
19755          * @param {Roo.HtmlEditorCore} this
19756          * @param {String} html
19757          */
19758         beforepush: true,
19759          /**
19760          * @event sync
19761          * Fires when the textarea is updated with content from the editor iframe.
19762          * @param {Roo.HtmlEditorCore} this
19763          * @param {String} html
19764          */
19765         sync: true,
19766          /**
19767          * @event push
19768          * Fires when the iframe editor is updated with content from the textarea.
19769          * @param {Roo.HtmlEditorCore} this
19770          * @param {String} html
19771          */
19772         push: true,
19773         
19774         /**
19775          * @event editorevent
19776          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19777          * @param {Roo.HtmlEditorCore} this
19778          */
19779         editorevent: true
19780         
19781     });
19782     
19783     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19784     
19785     // defaults : white / black...
19786     this.applyBlacklists();
19787     
19788     
19789     
19790 };
19791
19792
19793 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19794
19795
19796      /**
19797      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19798      */
19799     
19800     owner : false,
19801     
19802      /**
19803      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19804      *                        Roo.resizable.
19805      */
19806     resizable : false,
19807      /**
19808      * @cfg {Number} height (in pixels)
19809      */   
19810     height: 300,
19811    /**
19812      * @cfg {Number} width (in pixels)
19813      */   
19814     width: 500,
19815     
19816     /**
19817      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19818      * 
19819      */
19820     stylesheets: false,
19821     
19822     // id of frame..
19823     frameId: false,
19824     
19825     // private properties
19826     validationEvent : false,
19827     deferHeight: true,
19828     initialized : false,
19829     activated : false,
19830     sourceEditMode : false,
19831     onFocus : Roo.emptyFn,
19832     iframePad:3,
19833     hideMode:'offsets',
19834     
19835     clearUp: true,
19836     
19837     // blacklist + whitelisted elements..
19838     black: false,
19839     white: false,
19840      
19841     
19842
19843     /**
19844      * Protected method that will not generally be called directly. It
19845      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19846      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19847      */
19848     getDocMarkup : function(){
19849         // body styles..
19850         var st = '';
19851         
19852         // inherit styels from page...?? 
19853         if (this.stylesheets === false) {
19854             
19855             Roo.get(document.head).select('style').each(function(node) {
19856                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19857             });
19858             
19859             Roo.get(document.head).select('link').each(function(node) { 
19860                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19861             });
19862             
19863         } else if (!this.stylesheets.length) {
19864                 // simple..
19865                 st = '<style type="text/css">' +
19866                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19867                    '</style>';
19868         } else { 
19869             
19870         }
19871         
19872         st +=  '<style type="text/css">' +
19873             'IMG { cursor: pointer } ' +
19874         '</style>';
19875
19876         
19877         return '<html><head>' + st  +
19878             //<style type="text/css">' +
19879             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19880             //'</style>' +
19881             ' </head><body class="roo-htmleditor-body"></body></html>';
19882     },
19883
19884     // private
19885     onRender : function(ct, position)
19886     {
19887         var _t = this;
19888         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19889         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19890         
19891         
19892         this.el.dom.style.border = '0 none';
19893         this.el.dom.setAttribute('tabIndex', -1);
19894         this.el.addClass('x-hidden hide');
19895         
19896         
19897         
19898         if(Roo.isIE){ // fix IE 1px bogus margin
19899             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19900         }
19901        
19902         
19903         this.frameId = Roo.id();
19904         
19905          
19906         
19907         var iframe = this.owner.wrap.createChild({
19908             tag: 'iframe',
19909             cls: 'form-control', // bootstrap..
19910             id: this.frameId,
19911             name: this.frameId,
19912             frameBorder : 'no',
19913             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19914         }, this.el
19915         );
19916         
19917         
19918         this.iframe = iframe.dom;
19919
19920          this.assignDocWin();
19921         
19922         this.doc.designMode = 'on';
19923        
19924         this.doc.open();
19925         this.doc.write(this.getDocMarkup());
19926         this.doc.close();
19927
19928         
19929         var task = { // must defer to wait for browser to be ready
19930             run : function(){
19931                 //console.log("run task?" + this.doc.readyState);
19932                 this.assignDocWin();
19933                 if(this.doc.body || this.doc.readyState == 'complete'){
19934                     try {
19935                         this.doc.designMode="on";
19936                     } catch (e) {
19937                         return;
19938                     }
19939                     Roo.TaskMgr.stop(task);
19940                     this.initEditor.defer(10, this);
19941                 }
19942             },
19943             interval : 10,
19944             duration: 10000,
19945             scope: this
19946         };
19947         Roo.TaskMgr.start(task);
19948
19949     },
19950
19951     // private
19952     onResize : function(w, h)
19953     {
19954          Roo.log('resize: ' +w + ',' + h );
19955         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19956         if(!this.iframe){
19957             return;
19958         }
19959         if(typeof w == 'number'){
19960             
19961             this.iframe.style.width = w + 'px';
19962         }
19963         if(typeof h == 'number'){
19964             
19965             this.iframe.style.height = h + 'px';
19966             if(this.doc){
19967                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19968             }
19969         }
19970         
19971     },
19972
19973     /**
19974      * Toggles the editor between standard and source edit mode.
19975      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19976      */
19977     toggleSourceEdit : function(sourceEditMode){
19978         
19979         this.sourceEditMode = sourceEditMode === true;
19980         
19981         if(this.sourceEditMode){
19982  
19983             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19984             
19985         }else{
19986             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19987             //this.iframe.className = '';
19988             this.deferFocus();
19989         }
19990         //this.setSize(this.owner.wrap.getSize());
19991         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19992     },
19993
19994     
19995   
19996
19997     /**
19998      * Protected method that will not generally be called directly. If you need/want
19999      * custom HTML cleanup, this is the method you should override.
20000      * @param {String} html The HTML to be cleaned
20001      * return {String} The cleaned HTML
20002      */
20003     cleanHtml : function(html){
20004         html = String(html);
20005         if(html.length > 5){
20006             if(Roo.isSafari){ // strip safari nonsense
20007                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20008             }
20009         }
20010         if(html == '&nbsp;'){
20011             html = '';
20012         }
20013         return html;
20014     },
20015
20016     /**
20017      * HTML Editor -> Textarea
20018      * Protected method that will not generally be called directly. Syncs the contents
20019      * of the editor iframe with the textarea.
20020      */
20021     syncValue : function(){
20022         if(this.initialized){
20023             var bd = (this.doc.body || this.doc.documentElement);
20024             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20025             var html = bd.innerHTML;
20026             if(Roo.isSafari){
20027                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20028                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20029                 if(m && m[1]){
20030                     html = '<div style="'+m[0]+'">' + html + '</div>';
20031                 }
20032             }
20033             html = this.cleanHtml(html);
20034             // fix up the special chars.. normaly like back quotes in word...
20035             // however we do not want to do this with chinese..
20036             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20037                 var cc = b.charCodeAt();
20038                 if (
20039                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20040                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20041                     (cc >= 0xf900 && cc < 0xfb00 )
20042                 ) {
20043                         return b;
20044                 }
20045                 return "&#"+cc+";" 
20046             });
20047             if(this.owner.fireEvent('beforesync', this, html) !== false){
20048                 this.el.dom.value = html;
20049                 this.owner.fireEvent('sync', this, html);
20050             }
20051         }
20052     },
20053
20054     /**
20055      * Protected method that will not generally be called directly. Pushes the value of the textarea
20056      * into the iframe editor.
20057      */
20058     pushValue : function(){
20059         if(this.initialized){
20060             var v = this.el.dom.value.trim();
20061             
20062 //            if(v.length < 1){
20063 //                v = '&#160;';
20064 //            }
20065             
20066             if(this.owner.fireEvent('beforepush', this, v) !== false){
20067                 var d = (this.doc.body || this.doc.documentElement);
20068                 d.innerHTML = v;
20069                 this.cleanUpPaste();
20070                 this.el.dom.value = d.innerHTML;
20071                 this.owner.fireEvent('push', this, v);
20072             }
20073         }
20074     },
20075
20076     // private
20077     deferFocus : function(){
20078         this.focus.defer(10, this);
20079     },
20080
20081     // doc'ed in Field
20082     focus : function(){
20083         if(this.win && !this.sourceEditMode){
20084             this.win.focus();
20085         }else{
20086             this.el.focus();
20087         }
20088     },
20089     
20090     assignDocWin: function()
20091     {
20092         var iframe = this.iframe;
20093         
20094          if(Roo.isIE){
20095             this.doc = iframe.contentWindow.document;
20096             this.win = iframe.contentWindow;
20097         } else {
20098 //            if (!Roo.get(this.frameId)) {
20099 //                return;
20100 //            }
20101 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20102 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20103             
20104             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20105                 return;
20106             }
20107             
20108             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20109             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20110         }
20111     },
20112     
20113     // private
20114     initEditor : function(){
20115         //console.log("INIT EDITOR");
20116         this.assignDocWin();
20117         
20118         
20119         
20120         this.doc.designMode="on";
20121         this.doc.open();
20122         this.doc.write(this.getDocMarkup());
20123         this.doc.close();
20124         
20125         var dbody = (this.doc.body || this.doc.documentElement);
20126         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20127         // this copies styles from the containing element into thsi one..
20128         // not sure why we need all of this..
20129         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20130         
20131         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20132         //ss['background-attachment'] = 'fixed'; // w3c
20133         dbody.bgProperties = 'fixed'; // ie
20134         //Roo.DomHelper.applyStyles(dbody, ss);
20135         Roo.EventManager.on(this.doc, {
20136             //'mousedown': this.onEditorEvent,
20137             'mouseup': this.onEditorEvent,
20138             'dblclick': this.onEditorEvent,
20139             'click': this.onEditorEvent,
20140             'keyup': this.onEditorEvent,
20141             buffer:100,
20142             scope: this
20143         });
20144         if(Roo.isGecko){
20145             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20146         }
20147         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20148             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20149         }
20150         this.initialized = true;
20151
20152         this.owner.fireEvent('initialize', this);
20153         this.pushValue();
20154     },
20155
20156     // private
20157     onDestroy : function(){
20158         
20159         
20160         
20161         if(this.rendered){
20162             
20163             //for (var i =0; i < this.toolbars.length;i++) {
20164             //    // fixme - ask toolbars for heights?
20165             //    this.toolbars[i].onDestroy();
20166            // }
20167             
20168             //this.wrap.dom.innerHTML = '';
20169             //this.wrap.remove();
20170         }
20171     },
20172
20173     // private
20174     onFirstFocus : function(){
20175         
20176         this.assignDocWin();
20177         
20178         
20179         this.activated = true;
20180          
20181     
20182         if(Roo.isGecko){ // prevent silly gecko errors
20183             this.win.focus();
20184             var s = this.win.getSelection();
20185             if(!s.focusNode || s.focusNode.nodeType != 3){
20186                 var r = s.getRangeAt(0);
20187                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20188                 r.collapse(true);
20189                 this.deferFocus();
20190             }
20191             try{
20192                 this.execCmd('useCSS', true);
20193                 this.execCmd('styleWithCSS', false);
20194             }catch(e){}
20195         }
20196         this.owner.fireEvent('activate', this);
20197     },
20198
20199     // private
20200     adjustFont: function(btn){
20201         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20202         //if(Roo.isSafari){ // safari
20203         //    adjust *= 2;
20204        // }
20205         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20206         if(Roo.isSafari){ // safari
20207             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20208             v =  (v < 10) ? 10 : v;
20209             v =  (v > 48) ? 48 : v;
20210             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20211             
20212         }
20213         
20214         
20215         v = Math.max(1, v+adjust);
20216         
20217         this.execCmd('FontSize', v  );
20218     },
20219
20220     onEditorEvent : function(e)
20221     {
20222         this.owner.fireEvent('editorevent', this, e);
20223       //  this.updateToolbar();
20224         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20225     },
20226
20227     insertTag : function(tg)
20228     {
20229         // could be a bit smarter... -> wrap the current selected tRoo..
20230         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20231             
20232             range = this.createRange(this.getSelection());
20233             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20234             wrappingNode.appendChild(range.extractContents());
20235             range.insertNode(wrappingNode);
20236
20237             return;
20238             
20239             
20240             
20241         }
20242         this.execCmd("formatblock",   tg);
20243         
20244     },
20245     
20246     insertText : function(txt)
20247     {
20248         
20249         
20250         var range = this.createRange();
20251         range.deleteContents();
20252                //alert(Sender.getAttribute('label'));
20253                
20254         range.insertNode(this.doc.createTextNode(txt));
20255     } ,
20256     
20257      
20258
20259     /**
20260      * Executes a Midas editor command on the editor document and performs necessary focus and
20261      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20262      * @param {String} cmd The Midas command
20263      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20264      */
20265     relayCmd : function(cmd, value){
20266         this.win.focus();
20267         this.execCmd(cmd, value);
20268         this.owner.fireEvent('editorevent', this);
20269         //this.updateToolbar();
20270         this.owner.deferFocus();
20271     },
20272
20273     /**
20274      * Executes a Midas editor command directly on the editor document.
20275      * For visual commands, you should use {@link #relayCmd} instead.
20276      * <b>This should only be called after the editor is initialized.</b>
20277      * @param {String} cmd The Midas command
20278      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20279      */
20280     execCmd : function(cmd, value){
20281         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20282         this.syncValue();
20283     },
20284  
20285  
20286    
20287     /**
20288      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20289      * to insert tRoo.
20290      * @param {String} text | dom node.. 
20291      */
20292     insertAtCursor : function(text)
20293     {
20294         
20295         
20296         
20297         if(!this.activated){
20298             return;
20299         }
20300         /*
20301         if(Roo.isIE){
20302             this.win.focus();
20303             var r = this.doc.selection.createRange();
20304             if(r){
20305                 r.collapse(true);
20306                 r.pasteHTML(text);
20307                 this.syncValue();
20308                 this.deferFocus();
20309             
20310             }
20311             return;
20312         }
20313         */
20314         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20315             this.win.focus();
20316             
20317             
20318             // from jquery ui (MIT licenced)
20319             var range, node;
20320             var win = this.win;
20321             
20322             if (win.getSelection && win.getSelection().getRangeAt) {
20323                 range = win.getSelection().getRangeAt(0);
20324                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20325                 range.insertNode(node);
20326             } else if (win.document.selection && win.document.selection.createRange) {
20327                 // no firefox support
20328                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20329                 win.document.selection.createRange().pasteHTML(txt);
20330             } else {
20331                 // no firefox support
20332                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20333                 this.execCmd('InsertHTML', txt);
20334             } 
20335             
20336             this.syncValue();
20337             
20338             this.deferFocus();
20339         }
20340     },
20341  // private
20342     mozKeyPress : function(e){
20343         if(e.ctrlKey){
20344             var c = e.getCharCode(), cmd;
20345           
20346             if(c > 0){
20347                 c = String.fromCharCode(c).toLowerCase();
20348                 switch(c){
20349                     case 'b':
20350                         cmd = 'bold';
20351                         break;
20352                     case 'i':
20353                         cmd = 'italic';
20354                         break;
20355                     
20356                     case 'u':
20357                         cmd = 'underline';
20358                         break;
20359                     
20360                     case 'v':
20361                         this.cleanUpPaste.defer(100, this);
20362                         return;
20363                         
20364                 }
20365                 if(cmd){
20366                     this.win.focus();
20367                     this.execCmd(cmd);
20368                     this.deferFocus();
20369                     e.preventDefault();
20370                 }
20371                 
20372             }
20373         }
20374     },
20375
20376     // private
20377     fixKeys : function(){ // load time branching for fastest keydown performance
20378         if(Roo.isIE){
20379             return function(e){
20380                 var k = e.getKey(), r;
20381                 if(k == e.TAB){
20382                     e.stopEvent();
20383                     r = this.doc.selection.createRange();
20384                     if(r){
20385                         r.collapse(true);
20386                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20387                         this.deferFocus();
20388                     }
20389                     return;
20390                 }
20391                 
20392                 if(k == e.ENTER){
20393                     r = this.doc.selection.createRange();
20394                     if(r){
20395                         var target = r.parentElement();
20396                         if(!target || target.tagName.toLowerCase() != 'li'){
20397                             e.stopEvent();
20398                             r.pasteHTML('<br />');
20399                             r.collapse(false);
20400                             r.select();
20401                         }
20402                     }
20403                 }
20404                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20405                     this.cleanUpPaste.defer(100, this);
20406                     return;
20407                 }
20408                 
20409                 
20410             };
20411         }else if(Roo.isOpera){
20412             return function(e){
20413                 var k = e.getKey();
20414                 if(k == e.TAB){
20415                     e.stopEvent();
20416                     this.win.focus();
20417                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20418                     this.deferFocus();
20419                 }
20420                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20421                     this.cleanUpPaste.defer(100, this);
20422                     return;
20423                 }
20424                 
20425             };
20426         }else if(Roo.isSafari){
20427             return function(e){
20428                 var k = e.getKey();
20429                 
20430                 if(k == e.TAB){
20431                     e.stopEvent();
20432                     this.execCmd('InsertText','\t');
20433                     this.deferFocus();
20434                     return;
20435                 }
20436                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20437                     this.cleanUpPaste.defer(100, this);
20438                     return;
20439                 }
20440                 
20441              };
20442         }
20443     }(),
20444     
20445     getAllAncestors: function()
20446     {
20447         var p = this.getSelectedNode();
20448         var a = [];
20449         if (!p) {
20450             a.push(p); // push blank onto stack..
20451             p = this.getParentElement();
20452         }
20453         
20454         
20455         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20456             a.push(p);
20457             p = p.parentNode;
20458         }
20459         a.push(this.doc.body);
20460         return a;
20461     },
20462     lastSel : false,
20463     lastSelNode : false,
20464     
20465     
20466     getSelection : function() 
20467     {
20468         this.assignDocWin();
20469         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20470     },
20471     
20472     getSelectedNode: function() 
20473     {
20474         // this may only work on Gecko!!!
20475         
20476         // should we cache this!!!!
20477         
20478         
20479         
20480          
20481         var range = this.createRange(this.getSelection()).cloneRange();
20482         
20483         if (Roo.isIE) {
20484             var parent = range.parentElement();
20485             while (true) {
20486                 var testRange = range.duplicate();
20487                 testRange.moveToElementText(parent);
20488                 if (testRange.inRange(range)) {
20489                     break;
20490                 }
20491                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20492                     break;
20493                 }
20494                 parent = parent.parentElement;
20495             }
20496             return parent;
20497         }
20498         
20499         // is ancestor a text element.
20500         var ac =  range.commonAncestorContainer;
20501         if (ac.nodeType == 3) {
20502             ac = ac.parentNode;
20503         }
20504         
20505         var ar = ac.childNodes;
20506          
20507         var nodes = [];
20508         var other_nodes = [];
20509         var has_other_nodes = false;
20510         for (var i=0;i<ar.length;i++) {
20511             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20512                 continue;
20513             }
20514             // fullly contained node.
20515             
20516             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20517                 nodes.push(ar[i]);
20518                 continue;
20519             }
20520             
20521             // probably selected..
20522             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20523                 other_nodes.push(ar[i]);
20524                 continue;
20525             }
20526             // outer..
20527             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20528                 continue;
20529             }
20530             
20531             
20532             has_other_nodes = true;
20533         }
20534         if (!nodes.length && other_nodes.length) {
20535             nodes= other_nodes;
20536         }
20537         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20538             return false;
20539         }
20540         
20541         return nodes[0];
20542     },
20543     createRange: function(sel)
20544     {
20545         // this has strange effects when using with 
20546         // top toolbar - not sure if it's a great idea.
20547         //this.editor.contentWindow.focus();
20548         if (typeof sel != "undefined") {
20549             try {
20550                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20551             } catch(e) {
20552                 return this.doc.createRange();
20553             }
20554         } else {
20555             return this.doc.createRange();
20556         }
20557     },
20558     getParentElement: function()
20559     {
20560         
20561         this.assignDocWin();
20562         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20563         
20564         var range = this.createRange(sel);
20565          
20566         try {
20567             var p = range.commonAncestorContainer;
20568             while (p.nodeType == 3) { // text node
20569                 p = p.parentNode;
20570             }
20571             return p;
20572         } catch (e) {
20573             return null;
20574         }
20575     
20576     },
20577     /***
20578      *
20579      * Range intersection.. the hard stuff...
20580      *  '-1' = before
20581      *  '0' = hits..
20582      *  '1' = after.
20583      *         [ -- selected range --- ]
20584      *   [fail]                        [fail]
20585      *
20586      *    basically..
20587      *      if end is before start or  hits it. fail.
20588      *      if start is after end or hits it fail.
20589      *
20590      *   if either hits (but other is outside. - then it's not 
20591      *   
20592      *    
20593      **/
20594     
20595     
20596     // @see http://www.thismuchiknow.co.uk/?p=64.
20597     rangeIntersectsNode : function(range, node)
20598     {
20599         var nodeRange = node.ownerDocument.createRange();
20600         try {
20601             nodeRange.selectNode(node);
20602         } catch (e) {
20603             nodeRange.selectNodeContents(node);
20604         }
20605     
20606         var rangeStartRange = range.cloneRange();
20607         rangeStartRange.collapse(true);
20608     
20609         var rangeEndRange = range.cloneRange();
20610         rangeEndRange.collapse(false);
20611     
20612         var nodeStartRange = nodeRange.cloneRange();
20613         nodeStartRange.collapse(true);
20614     
20615         var nodeEndRange = nodeRange.cloneRange();
20616         nodeEndRange.collapse(false);
20617     
20618         return rangeStartRange.compareBoundaryPoints(
20619                  Range.START_TO_START, nodeEndRange) == -1 &&
20620                rangeEndRange.compareBoundaryPoints(
20621                  Range.START_TO_START, nodeStartRange) == 1;
20622         
20623          
20624     },
20625     rangeCompareNode : function(range, node)
20626     {
20627         var nodeRange = node.ownerDocument.createRange();
20628         try {
20629             nodeRange.selectNode(node);
20630         } catch (e) {
20631             nodeRange.selectNodeContents(node);
20632         }
20633         
20634         
20635         range.collapse(true);
20636     
20637         nodeRange.collapse(true);
20638      
20639         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20640         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20641          
20642         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20643         
20644         var nodeIsBefore   =  ss == 1;
20645         var nodeIsAfter    = ee == -1;
20646         
20647         if (nodeIsBefore && nodeIsAfter) {
20648             return 0; // outer
20649         }
20650         if (!nodeIsBefore && nodeIsAfter) {
20651             return 1; //right trailed.
20652         }
20653         
20654         if (nodeIsBefore && !nodeIsAfter) {
20655             return 2;  // left trailed.
20656         }
20657         // fully contined.
20658         return 3;
20659     },
20660
20661     // private? - in a new class?
20662     cleanUpPaste :  function()
20663     {
20664         // cleans up the whole document..
20665         Roo.log('cleanuppaste');
20666         
20667         this.cleanUpChildren(this.doc.body);
20668         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20669         if (clean != this.doc.body.innerHTML) {
20670             this.doc.body.innerHTML = clean;
20671         }
20672         
20673     },
20674     
20675     cleanWordChars : function(input) {// change the chars to hex code
20676         var he = Roo.HtmlEditorCore;
20677         
20678         var output = input;
20679         Roo.each(he.swapCodes, function(sw) { 
20680             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20681             
20682             output = output.replace(swapper, sw[1]);
20683         });
20684         
20685         return output;
20686     },
20687     
20688     
20689     cleanUpChildren : function (n)
20690     {
20691         if (!n.childNodes.length) {
20692             return;
20693         }
20694         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20695            this.cleanUpChild(n.childNodes[i]);
20696         }
20697     },
20698     
20699     
20700         
20701     
20702     cleanUpChild : function (node)
20703     {
20704         var ed = this;
20705         //console.log(node);
20706         if (node.nodeName == "#text") {
20707             // clean up silly Windows -- stuff?
20708             return; 
20709         }
20710         if (node.nodeName == "#comment") {
20711             node.parentNode.removeChild(node);
20712             // clean up silly Windows -- stuff?
20713             return; 
20714         }
20715         var lcname = node.tagName.toLowerCase();
20716         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20717         // whitelist of tags..
20718         
20719         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20720             // remove node.
20721             node.parentNode.removeChild(node);
20722             return;
20723             
20724         }
20725         
20726         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20727         
20728         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20729         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20730         
20731         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20732         //    remove_keep_children = true;
20733         //}
20734         
20735         if (remove_keep_children) {
20736             this.cleanUpChildren(node);
20737             // inserts everything just before this node...
20738             while (node.childNodes.length) {
20739                 var cn = node.childNodes[0];
20740                 node.removeChild(cn);
20741                 node.parentNode.insertBefore(cn, node);
20742             }
20743             node.parentNode.removeChild(node);
20744             return;
20745         }
20746         
20747         if (!node.attributes || !node.attributes.length) {
20748             this.cleanUpChildren(node);
20749             return;
20750         }
20751         
20752         function cleanAttr(n,v)
20753         {
20754             
20755             if (v.match(/^\./) || v.match(/^\//)) {
20756                 return;
20757             }
20758             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20759                 return;
20760             }
20761             if (v.match(/^#/)) {
20762                 return;
20763             }
20764 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20765             node.removeAttribute(n);
20766             
20767         }
20768         
20769         var cwhite = this.cwhite;
20770         var cblack = this.cblack;
20771             
20772         function cleanStyle(n,v)
20773         {
20774             if (v.match(/expression/)) { //XSS?? should we even bother..
20775                 node.removeAttribute(n);
20776                 return;
20777             }
20778             
20779             var parts = v.split(/;/);
20780             var clean = [];
20781             
20782             Roo.each(parts, function(p) {
20783                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20784                 if (!p.length) {
20785                     return true;
20786                 }
20787                 var l = p.split(':').shift().replace(/\s+/g,'');
20788                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20789                 
20790                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20791 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20792                     //node.removeAttribute(n);
20793                     return true;
20794                 }
20795                 //Roo.log()
20796                 // only allow 'c whitelisted system attributes'
20797                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20798 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20799                     //node.removeAttribute(n);
20800                     return true;
20801                 }
20802                 
20803                 
20804                  
20805                 
20806                 clean.push(p);
20807                 return true;
20808             });
20809             if (clean.length) { 
20810                 node.setAttribute(n, clean.join(';'));
20811             } else {
20812                 node.removeAttribute(n);
20813             }
20814             
20815         }
20816         
20817         
20818         for (var i = node.attributes.length-1; i > -1 ; i--) {
20819             var a = node.attributes[i];
20820             //console.log(a);
20821             
20822             if (a.name.toLowerCase().substr(0,2)=='on')  {
20823                 node.removeAttribute(a.name);
20824                 continue;
20825             }
20826             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20827                 node.removeAttribute(a.name);
20828                 continue;
20829             }
20830             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20831                 cleanAttr(a.name,a.value); // fixme..
20832                 continue;
20833             }
20834             if (a.name == 'style') {
20835                 cleanStyle(a.name,a.value);
20836                 continue;
20837             }
20838             /// clean up MS crap..
20839             // tecnically this should be a list of valid class'es..
20840             
20841             
20842             if (a.name == 'class') {
20843                 if (a.value.match(/^Mso/)) {
20844                     node.className = '';
20845                 }
20846                 
20847                 if (a.value.match(/body/)) {
20848                     node.className = '';
20849                 }
20850                 continue;
20851             }
20852             
20853             // style cleanup!?
20854             // class cleanup?
20855             
20856         }
20857         
20858         
20859         this.cleanUpChildren(node);
20860         
20861         
20862     },
20863     
20864     /**
20865      * Clean up MS wordisms...
20866      */
20867     cleanWord : function(node)
20868     {
20869         
20870         
20871         if (!node) {
20872             this.cleanWord(this.doc.body);
20873             return;
20874         }
20875         if (node.nodeName == "#text") {
20876             // clean up silly Windows -- stuff?
20877             return; 
20878         }
20879         if (node.nodeName == "#comment") {
20880             node.parentNode.removeChild(node);
20881             // clean up silly Windows -- stuff?
20882             return; 
20883         }
20884         
20885         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20886             node.parentNode.removeChild(node);
20887             return;
20888         }
20889         
20890         // remove - but keep children..
20891         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20892             while (node.childNodes.length) {
20893                 var cn = node.childNodes[0];
20894                 node.removeChild(cn);
20895                 node.parentNode.insertBefore(cn, node);
20896             }
20897             node.parentNode.removeChild(node);
20898             this.iterateChildren(node, this.cleanWord);
20899             return;
20900         }
20901         // clean styles
20902         if (node.className.length) {
20903             
20904             var cn = node.className.split(/\W+/);
20905             var cna = [];
20906             Roo.each(cn, function(cls) {
20907                 if (cls.match(/Mso[a-zA-Z]+/)) {
20908                     return;
20909                 }
20910                 cna.push(cls);
20911             });
20912             node.className = cna.length ? cna.join(' ') : '';
20913             if (!cna.length) {
20914                 node.removeAttribute("class");
20915             }
20916         }
20917         
20918         if (node.hasAttribute("lang")) {
20919             node.removeAttribute("lang");
20920         }
20921         
20922         if (node.hasAttribute("style")) {
20923             
20924             var styles = node.getAttribute("style").split(";");
20925             var nstyle = [];
20926             Roo.each(styles, function(s) {
20927                 if (!s.match(/:/)) {
20928                     return;
20929                 }
20930                 var kv = s.split(":");
20931                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20932                     return;
20933                 }
20934                 // what ever is left... we allow.
20935                 nstyle.push(s);
20936             });
20937             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20938             if (!nstyle.length) {
20939                 node.removeAttribute('style');
20940             }
20941         }
20942         this.iterateChildren(node, this.cleanWord);
20943         
20944         
20945         
20946     },
20947     /**
20948      * iterateChildren of a Node, calling fn each time, using this as the scole..
20949      * @param {DomNode} node node to iterate children of.
20950      * @param {Function} fn method of this class to call on each item.
20951      */
20952     iterateChildren : function(node, fn)
20953     {
20954         if (!node.childNodes.length) {
20955                 return;
20956         }
20957         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20958            fn.call(this, node.childNodes[i])
20959         }
20960     },
20961     
20962     
20963     /**
20964      * cleanTableWidths.
20965      *
20966      * Quite often pasting from word etc.. results in tables with column and widths.
20967      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20968      *
20969      */
20970     cleanTableWidths : function(node)
20971     {
20972          
20973          
20974         if (!node) {
20975             this.cleanTableWidths(this.doc.body);
20976             return;
20977         }
20978         
20979         // ignore list...
20980         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20981             return; 
20982         }
20983         Roo.log(node.tagName);
20984         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20985             this.iterateChildren(node, this.cleanTableWidths);
20986             return;
20987         }
20988         if (node.hasAttribute('width')) {
20989             node.removeAttribute('width');
20990         }
20991         
20992          
20993         if (node.hasAttribute("style")) {
20994             // pretty basic...
20995             
20996             var styles = node.getAttribute("style").split(";");
20997             var nstyle = [];
20998             Roo.each(styles, function(s) {
20999                 if (!s.match(/:/)) {
21000                     return;
21001                 }
21002                 var kv = s.split(":");
21003                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21004                     return;
21005                 }
21006                 // what ever is left... we allow.
21007                 nstyle.push(s);
21008             });
21009             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21010             if (!nstyle.length) {
21011                 node.removeAttribute('style');
21012             }
21013         }
21014         
21015         this.iterateChildren(node, this.cleanTableWidths);
21016         
21017         
21018     },
21019     
21020     
21021     
21022     
21023     domToHTML : function(currentElement, depth, nopadtext) {
21024         
21025         depth = depth || 0;
21026         nopadtext = nopadtext || false;
21027     
21028         if (!currentElement) {
21029             return this.domToHTML(this.doc.body);
21030         }
21031         
21032         //Roo.log(currentElement);
21033         var j;
21034         var allText = false;
21035         var nodeName = currentElement.nodeName;
21036         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21037         
21038         if  (nodeName == '#text') {
21039             
21040             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21041         }
21042         
21043         
21044         var ret = '';
21045         if (nodeName != 'BODY') {
21046              
21047             var i = 0;
21048             // Prints the node tagName, such as <A>, <IMG>, etc
21049             if (tagName) {
21050                 var attr = [];
21051                 for(i = 0; i < currentElement.attributes.length;i++) {
21052                     // quoting?
21053                     var aname = currentElement.attributes.item(i).name;
21054                     if (!currentElement.attributes.item(i).value.length) {
21055                         continue;
21056                     }
21057                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21058                 }
21059                 
21060                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21061             } 
21062             else {
21063                 
21064                 // eack
21065             }
21066         } else {
21067             tagName = false;
21068         }
21069         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21070             return ret;
21071         }
21072         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21073             nopadtext = true;
21074         }
21075         
21076         
21077         // Traverse the tree
21078         i = 0;
21079         var currentElementChild = currentElement.childNodes.item(i);
21080         var allText = true;
21081         var innerHTML  = '';
21082         lastnode = '';
21083         while (currentElementChild) {
21084             // Formatting code (indent the tree so it looks nice on the screen)
21085             var nopad = nopadtext;
21086             if (lastnode == 'SPAN') {
21087                 nopad  = true;
21088             }
21089             // text
21090             if  (currentElementChild.nodeName == '#text') {
21091                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21092                 toadd = nopadtext ? toadd : toadd.trim();
21093                 if (!nopad && toadd.length > 80) {
21094                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21095                 }
21096                 innerHTML  += toadd;
21097                 
21098                 i++;
21099                 currentElementChild = currentElement.childNodes.item(i);
21100                 lastNode = '';
21101                 continue;
21102             }
21103             allText = false;
21104             
21105             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21106                 
21107             // Recursively traverse the tree structure of the child node
21108             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21109             lastnode = currentElementChild.nodeName;
21110             i++;
21111             currentElementChild=currentElement.childNodes.item(i);
21112         }
21113         
21114         ret += innerHTML;
21115         
21116         if (!allText) {
21117                 // The remaining code is mostly for formatting the tree
21118             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21119         }
21120         
21121         
21122         if (tagName) {
21123             ret+= "</"+tagName+">";
21124         }
21125         return ret;
21126         
21127     },
21128         
21129     applyBlacklists : function()
21130     {
21131         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21132         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21133         
21134         this.white = [];
21135         this.black = [];
21136         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21137             if (b.indexOf(tag) > -1) {
21138                 return;
21139             }
21140             this.white.push(tag);
21141             
21142         }, this);
21143         
21144         Roo.each(w, function(tag) {
21145             if (b.indexOf(tag) > -1) {
21146                 return;
21147             }
21148             if (this.white.indexOf(tag) > -1) {
21149                 return;
21150             }
21151             this.white.push(tag);
21152             
21153         }, this);
21154         
21155         
21156         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21157             if (w.indexOf(tag) > -1) {
21158                 return;
21159             }
21160             this.black.push(tag);
21161             
21162         }, this);
21163         
21164         Roo.each(b, function(tag) {
21165             if (w.indexOf(tag) > -1) {
21166                 return;
21167             }
21168             if (this.black.indexOf(tag) > -1) {
21169                 return;
21170             }
21171             this.black.push(tag);
21172             
21173         }, this);
21174         
21175         
21176         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21177         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21178         
21179         this.cwhite = [];
21180         this.cblack = [];
21181         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21182             if (b.indexOf(tag) > -1) {
21183                 return;
21184             }
21185             this.cwhite.push(tag);
21186             
21187         }, this);
21188         
21189         Roo.each(w, function(tag) {
21190             if (b.indexOf(tag) > -1) {
21191                 return;
21192             }
21193             if (this.cwhite.indexOf(tag) > -1) {
21194                 return;
21195             }
21196             this.cwhite.push(tag);
21197             
21198         }, this);
21199         
21200         
21201         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21202             if (w.indexOf(tag) > -1) {
21203                 return;
21204             }
21205             this.cblack.push(tag);
21206             
21207         }, this);
21208         
21209         Roo.each(b, function(tag) {
21210             if (w.indexOf(tag) > -1) {
21211                 return;
21212             }
21213             if (this.cblack.indexOf(tag) > -1) {
21214                 return;
21215             }
21216             this.cblack.push(tag);
21217             
21218         }, this);
21219     },
21220     
21221     setStylesheets : function(stylesheets)
21222     {
21223         if(typeof(stylesheets) == 'string'){
21224             Roo.get(this.iframe.contentDocument.head).createChild({
21225                 tag : 'link',
21226                 rel : 'stylesheet',
21227                 type : 'text/css',
21228                 href : stylesheets
21229             });
21230             
21231             return;
21232         }
21233         var _this = this;
21234      
21235         Roo.each(stylesheets, function(s) {
21236             if(!s.length){
21237                 return;
21238             }
21239             
21240             Roo.get(_this.iframe.contentDocument.head).createChild({
21241                 tag : 'link',
21242                 rel : 'stylesheet',
21243                 type : 'text/css',
21244                 href : s
21245             });
21246         });
21247
21248         
21249     },
21250     
21251     removeStylesheets : function()
21252     {
21253         var _this = this;
21254         
21255         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21256             s.remove();
21257         });
21258     }
21259     
21260     // hide stuff that is not compatible
21261     /**
21262      * @event blur
21263      * @hide
21264      */
21265     /**
21266      * @event change
21267      * @hide
21268      */
21269     /**
21270      * @event focus
21271      * @hide
21272      */
21273     /**
21274      * @event specialkey
21275      * @hide
21276      */
21277     /**
21278      * @cfg {String} fieldClass @hide
21279      */
21280     /**
21281      * @cfg {String} focusClass @hide
21282      */
21283     /**
21284      * @cfg {String} autoCreate @hide
21285      */
21286     /**
21287      * @cfg {String} inputType @hide
21288      */
21289     /**
21290      * @cfg {String} invalidClass @hide
21291      */
21292     /**
21293      * @cfg {String} invalidText @hide
21294      */
21295     /**
21296      * @cfg {String} msgFx @hide
21297      */
21298     /**
21299      * @cfg {String} validateOnBlur @hide
21300      */
21301 });
21302
21303 Roo.HtmlEditorCore.white = [
21304         'area', 'br', 'img', 'input', 'hr', 'wbr',
21305         
21306        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21307        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21308        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21309        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21310        'table',   'ul',         'xmp', 
21311        
21312        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21313       'thead',   'tr', 
21314      
21315       'dir', 'menu', 'ol', 'ul', 'dl',
21316        
21317       'embed',  'object'
21318 ];
21319
21320
21321 Roo.HtmlEditorCore.black = [
21322     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21323         'applet', // 
21324         'base',   'basefont', 'bgsound', 'blink',  'body', 
21325         'frame',  'frameset', 'head',    'html',   'ilayer', 
21326         'iframe', 'layer',  'link',     'meta',    'object',   
21327         'script', 'style' ,'title',  'xml' // clean later..
21328 ];
21329 Roo.HtmlEditorCore.clean = [
21330     'script', 'style', 'title', 'xml'
21331 ];
21332 Roo.HtmlEditorCore.remove = [
21333     'font'
21334 ];
21335 // attributes..
21336
21337 Roo.HtmlEditorCore.ablack = [
21338     'on'
21339 ];
21340     
21341 Roo.HtmlEditorCore.aclean = [ 
21342     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21343 ];
21344
21345 // protocols..
21346 Roo.HtmlEditorCore.pwhite= [
21347         'http',  'https',  'mailto'
21348 ];
21349
21350 // white listed style attributes.
21351 Roo.HtmlEditorCore.cwhite= [
21352       //  'text-align', /// default is to allow most things..
21353       
21354          
21355 //        'font-size'//??
21356 ];
21357
21358 // black listed style attributes.
21359 Roo.HtmlEditorCore.cblack= [
21360       //  'font-size' -- this can be set by the project 
21361 ];
21362
21363
21364 Roo.HtmlEditorCore.swapCodes   =[ 
21365     [    8211, "--" ], 
21366     [    8212, "--" ], 
21367     [    8216,  "'" ],  
21368     [    8217, "'" ],  
21369     [    8220, '"' ],  
21370     [    8221, '"' ],  
21371     [    8226, "*" ],  
21372     [    8230, "..." ]
21373 ]; 
21374
21375     /*
21376  * - LGPL
21377  *
21378  * HtmlEditor
21379  * 
21380  */
21381
21382 /**
21383  * @class Roo.bootstrap.HtmlEditor
21384  * @extends Roo.bootstrap.TextArea
21385  * Bootstrap HtmlEditor class
21386
21387  * @constructor
21388  * Create a new HtmlEditor
21389  * @param {Object} config The config object
21390  */
21391
21392 Roo.bootstrap.HtmlEditor = function(config){
21393     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21394     if (!this.toolbars) {
21395         this.toolbars = [];
21396     }
21397     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21398     this.addEvents({
21399             /**
21400              * @event initialize
21401              * Fires when the editor is fully initialized (including the iframe)
21402              * @param {HtmlEditor} this
21403              */
21404             initialize: true,
21405             /**
21406              * @event activate
21407              * Fires when the editor is first receives the focus. Any insertion must wait
21408              * until after this event.
21409              * @param {HtmlEditor} this
21410              */
21411             activate: true,
21412              /**
21413              * @event beforesync
21414              * Fires before the textarea is updated with content from the editor iframe. Return false
21415              * to cancel the sync.
21416              * @param {HtmlEditor} this
21417              * @param {String} html
21418              */
21419             beforesync: true,
21420              /**
21421              * @event beforepush
21422              * Fires before the iframe editor is updated with content from the textarea. Return false
21423              * to cancel the push.
21424              * @param {HtmlEditor} this
21425              * @param {String} html
21426              */
21427             beforepush: true,
21428              /**
21429              * @event sync
21430              * Fires when the textarea is updated with content from the editor iframe.
21431              * @param {HtmlEditor} this
21432              * @param {String} html
21433              */
21434             sync: true,
21435              /**
21436              * @event push
21437              * Fires when the iframe editor is updated with content from the textarea.
21438              * @param {HtmlEditor} this
21439              * @param {String} html
21440              */
21441             push: true,
21442              /**
21443              * @event editmodechange
21444              * Fires when the editor switches edit modes
21445              * @param {HtmlEditor} this
21446              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21447              */
21448             editmodechange: true,
21449             /**
21450              * @event editorevent
21451              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21452              * @param {HtmlEditor} this
21453              */
21454             editorevent: true,
21455             /**
21456              * @event firstfocus
21457              * Fires when on first focus - needed by toolbars..
21458              * @param {HtmlEditor} this
21459              */
21460             firstfocus: true,
21461             /**
21462              * @event autosave
21463              * Auto save the htmlEditor value as a file into Events
21464              * @param {HtmlEditor} this
21465              */
21466             autosave: true,
21467             /**
21468              * @event savedpreview
21469              * preview the saved version of htmlEditor
21470              * @param {HtmlEditor} this
21471              */
21472             savedpreview: true
21473         });
21474 };
21475
21476
21477 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21478     
21479     
21480       /**
21481      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21482      */
21483     toolbars : false,
21484    
21485      /**
21486      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21487      *                        Roo.resizable.
21488      */
21489     resizable : false,
21490      /**
21491      * @cfg {Number} height (in pixels)
21492      */   
21493     height: 300,
21494    /**
21495      * @cfg {Number} width (in pixels)
21496      */   
21497     width: false,
21498     
21499     /**
21500      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21501      * 
21502      */
21503     stylesheets: false,
21504     
21505     // id of frame..
21506     frameId: false,
21507     
21508     // private properties
21509     validationEvent : false,
21510     deferHeight: true,
21511     initialized : false,
21512     activated : false,
21513     
21514     onFocus : Roo.emptyFn,
21515     iframePad:3,
21516     hideMode:'offsets',
21517     
21518     
21519     tbContainer : false,
21520     
21521     toolbarContainer :function() {
21522         return this.wrap.select('.x-html-editor-tb',true).first();
21523     },
21524
21525     /**
21526      * Protected method that will not generally be called directly. It
21527      * is called when the editor creates its toolbar. Override this method if you need to
21528      * add custom toolbar buttons.
21529      * @param {HtmlEditor} editor
21530      */
21531     createToolbar : function(){
21532         
21533         Roo.log("create toolbars");
21534         
21535         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21536         this.toolbars[0].render(this.toolbarContainer());
21537         
21538         return;
21539         
21540 //        if (!editor.toolbars || !editor.toolbars.length) {
21541 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21542 //        }
21543 //        
21544 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21545 //            editor.toolbars[i] = Roo.factory(
21546 //                    typeof(editor.toolbars[i]) == 'string' ?
21547 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21548 //                Roo.bootstrap.HtmlEditor);
21549 //            editor.toolbars[i].init(editor);
21550 //        }
21551     },
21552
21553      
21554     // private
21555     onRender : function(ct, position)
21556     {
21557        // Roo.log("Call onRender: " + this.xtype);
21558         var _t = this;
21559         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21560       
21561         this.wrap = this.inputEl().wrap({
21562             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21563         });
21564         
21565         this.editorcore.onRender(ct, position);
21566          
21567         if (this.resizable) {
21568             this.resizeEl = new Roo.Resizable(this.wrap, {
21569                 pinned : true,
21570                 wrap: true,
21571                 dynamic : true,
21572                 minHeight : this.height,
21573                 height: this.height,
21574                 handles : this.resizable,
21575                 width: this.width,
21576                 listeners : {
21577                     resize : function(r, w, h) {
21578                         _t.onResize(w,h); // -something
21579                     }
21580                 }
21581             });
21582             
21583         }
21584         this.createToolbar(this);
21585        
21586         
21587         if(!this.width && this.resizable){
21588             this.setSize(this.wrap.getSize());
21589         }
21590         if (this.resizeEl) {
21591             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21592             // should trigger onReize..
21593         }
21594         
21595     },
21596
21597     // private
21598     onResize : function(w, h)
21599     {
21600         Roo.log('resize: ' +w + ',' + h );
21601         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21602         var ew = false;
21603         var eh = false;
21604         
21605         if(this.inputEl() ){
21606             if(typeof w == 'number'){
21607                 var aw = w - this.wrap.getFrameWidth('lr');
21608                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21609                 ew = aw;
21610             }
21611             if(typeof h == 'number'){
21612                  var tbh = -11;  // fixme it needs to tool bar size!
21613                 for (var i =0; i < this.toolbars.length;i++) {
21614                     // fixme - ask toolbars for heights?
21615                     tbh += this.toolbars[i].el.getHeight();
21616                     //if (this.toolbars[i].footer) {
21617                     //    tbh += this.toolbars[i].footer.el.getHeight();
21618                     //}
21619                 }
21620               
21621                 
21622                 
21623                 
21624                 
21625                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21626                 ah -= 5; // knock a few pixes off for look..
21627                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21628                 var eh = ah;
21629             }
21630         }
21631         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21632         this.editorcore.onResize(ew,eh);
21633         
21634     },
21635
21636     /**
21637      * Toggles the editor between standard and source edit mode.
21638      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21639      */
21640     toggleSourceEdit : function(sourceEditMode)
21641     {
21642         this.editorcore.toggleSourceEdit(sourceEditMode);
21643         
21644         if(this.editorcore.sourceEditMode){
21645             Roo.log('editor - showing textarea');
21646             
21647 //            Roo.log('in');
21648 //            Roo.log(this.syncValue());
21649             this.syncValue();
21650             this.inputEl().removeClass(['hide', 'x-hidden']);
21651             this.inputEl().dom.removeAttribute('tabIndex');
21652             this.inputEl().focus();
21653         }else{
21654             Roo.log('editor - hiding textarea');
21655 //            Roo.log('out')
21656 //            Roo.log(this.pushValue()); 
21657             this.pushValue();
21658             
21659             this.inputEl().addClass(['hide', 'x-hidden']);
21660             this.inputEl().dom.setAttribute('tabIndex', -1);
21661             //this.deferFocus();
21662         }
21663          
21664         if(this.resizable){
21665             this.setSize(this.wrap.getSize());
21666         }
21667         
21668         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21669     },
21670  
21671     // private (for BoxComponent)
21672     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21673
21674     // private (for BoxComponent)
21675     getResizeEl : function(){
21676         return this.wrap;
21677     },
21678
21679     // private (for BoxComponent)
21680     getPositionEl : function(){
21681         return this.wrap;
21682     },
21683
21684     // private
21685     initEvents : function(){
21686         this.originalValue = this.getValue();
21687     },
21688
21689 //    /**
21690 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21691 //     * @method
21692 //     */
21693 //    markInvalid : Roo.emptyFn,
21694 //    /**
21695 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21696 //     * @method
21697 //     */
21698 //    clearInvalid : Roo.emptyFn,
21699
21700     setValue : function(v){
21701         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21702         this.editorcore.pushValue();
21703     },
21704
21705      
21706     // private
21707     deferFocus : function(){
21708         this.focus.defer(10, this);
21709     },
21710
21711     // doc'ed in Field
21712     focus : function(){
21713         this.editorcore.focus();
21714         
21715     },
21716       
21717
21718     // private
21719     onDestroy : function(){
21720         
21721         
21722         
21723         if(this.rendered){
21724             
21725             for (var i =0; i < this.toolbars.length;i++) {
21726                 // fixme - ask toolbars for heights?
21727                 this.toolbars[i].onDestroy();
21728             }
21729             
21730             this.wrap.dom.innerHTML = '';
21731             this.wrap.remove();
21732         }
21733     },
21734
21735     // private
21736     onFirstFocus : function(){
21737         //Roo.log("onFirstFocus");
21738         this.editorcore.onFirstFocus();
21739          for (var i =0; i < this.toolbars.length;i++) {
21740             this.toolbars[i].onFirstFocus();
21741         }
21742         
21743     },
21744     
21745     // private
21746     syncValue : function()
21747     {   
21748         this.editorcore.syncValue();
21749     },
21750     
21751     pushValue : function()
21752     {   
21753         this.editorcore.pushValue();
21754     }
21755      
21756     
21757     // hide stuff that is not compatible
21758     /**
21759      * @event blur
21760      * @hide
21761      */
21762     /**
21763      * @event change
21764      * @hide
21765      */
21766     /**
21767      * @event focus
21768      * @hide
21769      */
21770     /**
21771      * @event specialkey
21772      * @hide
21773      */
21774     /**
21775      * @cfg {String} fieldClass @hide
21776      */
21777     /**
21778      * @cfg {String} focusClass @hide
21779      */
21780     /**
21781      * @cfg {String} autoCreate @hide
21782      */
21783     /**
21784      * @cfg {String} inputType @hide
21785      */
21786     /**
21787      * @cfg {String} invalidClass @hide
21788      */
21789     /**
21790      * @cfg {String} invalidText @hide
21791      */
21792     /**
21793      * @cfg {String} msgFx @hide
21794      */
21795     /**
21796      * @cfg {String} validateOnBlur @hide
21797      */
21798 });
21799  
21800     
21801    
21802    
21803    
21804       
21805 Roo.namespace('Roo.bootstrap.htmleditor');
21806 /**
21807  * @class Roo.bootstrap.HtmlEditorToolbar1
21808  * Basic Toolbar
21809  * 
21810  * Usage:
21811  *
21812  new Roo.bootstrap.HtmlEditor({
21813     ....
21814     toolbars : [
21815         new Roo.bootstrap.HtmlEditorToolbar1({
21816             disable : { fonts: 1 , format: 1, ..., ... , ...],
21817             btns : [ .... ]
21818         })
21819     }
21820      
21821  * 
21822  * @cfg {Object} disable List of elements to disable..
21823  * @cfg {Array} btns List of additional buttons.
21824  * 
21825  * 
21826  * NEEDS Extra CSS? 
21827  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21828  */
21829  
21830 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21831 {
21832     
21833     Roo.apply(this, config);
21834     
21835     // default disabled, based on 'good practice'..
21836     this.disable = this.disable || {};
21837     Roo.applyIf(this.disable, {
21838         fontSize : true,
21839         colors : true,
21840         specialElements : true
21841     });
21842     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21843     
21844     this.editor = config.editor;
21845     this.editorcore = config.editor.editorcore;
21846     
21847     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21848     
21849     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21850     // dont call parent... till later.
21851 }
21852 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21853      
21854     bar : true,
21855     
21856     editor : false,
21857     editorcore : false,
21858     
21859     
21860     formats : [
21861         "p" ,  
21862         "h1","h2","h3","h4","h5","h6", 
21863         "pre", "code", 
21864         "abbr", "acronym", "address", "cite", "samp", "var",
21865         'div','span'
21866     ],
21867     
21868     onRender : function(ct, position)
21869     {
21870        // Roo.log("Call onRender: " + this.xtype);
21871         
21872        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21873        Roo.log(this.el);
21874        this.el.dom.style.marginBottom = '0';
21875        var _this = this;
21876        var editorcore = this.editorcore;
21877        var editor= this.editor;
21878        
21879        var children = [];
21880        var btn = function(id,cmd , toggle, handler){
21881        
21882             var  event = toggle ? 'toggle' : 'click';
21883        
21884             var a = {
21885                 size : 'sm',
21886                 xtype: 'Button',
21887                 xns: Roo.bootstrap,
21888                 glyphicon : id,
21889                 cmd : id || cmd,
21890                 enableToggle:toggle !== false,
21891                 //html : 'submit'
21892                 pressed : toggle ? false : null,
21893                 listeners : {}
21894             };
21895             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21896                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21897             };
21898             children.push(a);
21899             return a;
21900        }
21901         
21902         var style = {
21903                 xtype: 'Button',
21904                 size : 'sm',
21905                 xns: Roo.bootstrap,
21906                 glyphicon : 'font',
21907                 //html : 'submit'
21908                 menu : {
21909                     xtype: 'Menu',
21910                     xns: Roo.bootstrap,
21911                     items:  []
21912                 }
21913         };
21914         Roo.each(this.formats, function(f) {
21915             style.menu.items.push({
21916                 xtype :'MenuItem',
21917                 xns: Roo.bootstrap,
21918                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21919                 tagname : f,
21920                 listeners : {
21921                     click : function()
21922                     {
21923                         editorcore.insertTag(this.tagname);
21924                         editor.focus();
21925                     }
21926                 }
21927                 
21928             });
21929         });
21930          children.push(style);   
21931             
21932             
21933         btn('bold',false,true);
21934         btn('italic',false,true);
21935         btn('align-left', 'justifyleft',true);
21936         btn('align-center', 'justifycenter',true);
21937         btn('align-right' , 'justifyright',true);
21938         btn('link', false, false, function(btn) {
21939             //Roo.log("create link?");
21940             var url = prompt(this.createLinkText, this.defaultLinkValue);
21941             if(url && url != 'http:/'+'/'){
21942                 this.editorcore.relayCmd('createlink', url);
21943             }
21944         }),
21945         btn('list','insertunorderedlist',true);
21946         btn('pencil', false,true, function(btn){
21947                 Roo.log(this);
21948                 
21949                 this.toggleSourceEdit(btn.pressed);
21950         });
21951         /*
21952         var cog = {
21953                 xtype: 'Button',
21954                 size : 'sm',
21955                 xns: Roo.bootstrap,
21956                 glyphicon : 'cog',
21957                 //html : 'submit'
21958                 menu : {
21959                     xtype: 'Menu',
21960                     xns: Roo.bootstrap,
21961                     items:  []
21962                 }
21963         };
21964         
21965         cog.menu.items.push({
21966             xtype :'MenuItem',
21967             xns: Roo.bootstrap,
21968             html : Clean styles,
21969             tagname : f,
21970             listeners : {
21971                 click : function()
21972                 {
21973                     editorcore.insertTag(this.tagname);
21974                     editor.focus();
21975                 }
21976             }
21977             
21978         });
21979        */
21980         
21981          
21982        this.xtype = 'NavSimplebar';
21983         
21984         for(var i=0;i< children.length;i++) {
21985             
21986             this.buttons.add(this.addxtypeChild(children[i]));
21987             
21988         }
21989         
21990         editor.on('editorevent', this.updateToolbar, this);
21991     },
21992     onBtnClick : function(id)
21993     {
21994        this.editorcore.relayCmd(id);
21995        this.editorcore.focus();
21996     },
21997     
21998     /**
21999      * Protected method that will not generally be called directly. It triggers
22000      * a toolbar update by reading the markup state of the current selection in the editor.
22001      */
22002     updateToolbar: function(){
22003
22004         if(!this.editorcore.activated){
22005             this.editor.onFirstFocus(); // is this neeed?
22006             return;
22007         }
22008
22009         var btns = this.buttons; 
22010         var doc = this.editorcore.doc;
22011         btns.get('bold').setActive(doc.queryCommandState('bold'));
22012         btns.get('italic').setActive(doc.queryCommandState('italic'));
22013         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22014         
22015         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22016         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22017         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22018         
22019         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22020         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22021          /*
22022         
22023         var ans = this.editorcore.getAllAncestors();
22024         if (this.formatCombo) {
22025             
22026             
22027             var store = this.formatCombo.store;
22028             this.formatCombo.setValue("");
22029             for (var i =0; i < ans.length;i++) {
22030                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22031                     // select it..
22032                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22033                     break;
22034                 }
22035             }
22036         }
22037         
22038         
22039         
22040         // hides menus... - so this cant be on a menu...
22041         Roo.bootstrap.MenuMgr.hideAll();
22042         */
22043         Roo.bootstrap.MenuMgr.hideAll();
22044         //this.editorsyncValue();
22045     },
22046     onFirstFocus: function() {
22047         this.buttons.each(function(item){
22048            item.enable();
22049         });
22050     },
22051     toggleSourceEdit : function(sourceEditMode){
22052         
22053           
22054         if(sourceEditMode){
22055             Roo.log("disabling buttons");
22056            this.buttons.each( function(item){
22057                 if(item.cmd != 'pencil'){
22058                     item.disable();
22059                 }
22060             });
22061           
22062         }else{
22063             Roo.log("enabling buttons");
22064             if(this.editorcore.initialized){
22065                 this.buttons.each( function(item){
22066                     item.enable();
22067                 });
22068             }
22069             
22070         }
22071         Roo.log("calling toggole on editor");
22072         // tell the editor that it's been pressed..
22073         this.editor.toggleSourceEdit(sourceEditMode);
22074        
22075     }
22076 });
22077
22078
22079
22080
22081
22082 /**
22083  * @class Roo.bootstrap.Table.AbstractSelectionModel
22084  * @extends Roo.util.Observable
22085  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22086  * implemented by descendant classes.  This class should not be directly instantiated.
22087  * @constructor
22088  */
22089 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22090     this.locked = false;
22091     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22092 };
22093
22094
22095 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22096     /** @ignore Called by the grid automatically. Do not call directly. */
22097     init : function(grid){
22098         this.grid = grid;
22099         this.initEvents();
22100     },
22101
22102     /**
22103      * Locks the selections.
22104      */
22105     lock : function(){
22106         this.locked = true;
22107     },
22108
22109     /**
22110      * Unlocks the selections.
22111      */
22112     unlock : function(){
22113         this.locked = false;
22114     },
22115
22116     /**
22117      * Returns true if the selections are locked.
22118      * @return {Boolean}
22119      */
22120     isLocked : function(){
22121         return this.locked;
22122     }
22123 });
22124 /**
22125  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22126  * @class Roo.bootstrap.Table.RowSelectionModel
22127  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22128  * It supports multiple selections and keyboard selection/navigation. 
22129  * @constructor
22130  * @param {Object} config
22131  */
22132
22133 Roo.bootstrap.Table.RowSelectionModel = function(config){
22134     Roo.apply(this, config);
22135     this.selections = new Roo.util.MixedCollection(false, function(o){
22136         return o.id;
22137     });
22138
22139     this.last = false;
22140     this.lastActive = false;
22141
22142     this.addEvents({
22143         /**
22144              * @event selectionchange
22145              * Fires when the selection changes
22146              * @param {SelectionModel} this
22147              */
22148             "selectionchange" : true,
22149         /**
22150              * @event afterselectionchange
22151              * Fires after the selection changes (eg. by key press or clicking)
22152              * @param {SelectionModel} this
22153              */
22154             "afterselectionchange" : true,
22155         /**
22156              * @event beforerowselect
22157              * Fires when a row is selected being selected, return false to cancel.
22158              * @param {SelectionModel} this
22159              * @param {Number} rowIndex The selected index
22160              * @param {Boolean} keepExisting False if other selections will be cleared
22161              */
22162             "beforerowselect" : true,
22163         /**
22164              * @event rowselect
22165              * Fires when a row is selected.
22166              * @param {SelectionModel} this
22167              * @param {Number} rowIndex The selected index
22168              * @param {Roo.data.Record} r The record
22169              */
22170             "rowselect" : true,
22171         /**
22172              * @event rowdeselect
22173              * Fires when a row is deselected.
22174              * @param {SelectionModel} this
22175              * @param {Number} rowIndex The selected index
22176              */
22177         "rowdeselect" : true
22178     });
22179     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22180     this.locked = false;
22181 };
22182
22183 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22184     /**
22185      * @cfg {Boolean} singleSelect
22186      * True to allow selection of only one row at a time (defaults to false)
22187      */
22188     singleSelect : false,
22189
22190     // private
22191     initEvents : function(){
22192
22193         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22194             this.grid.on("mousedown", this.handleMouseDown, this);
22195         }else{ // allow click to work like normal
22196             this.grid.on("rowclick", this.handleDragableRowClick, this);
22197         }
22198
22199         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22200             "up" : function(e){
22201                 if(!e.shiftKey){
22202                     this.selectPrevious(e.shiftKey);
22203                 }else if(this.last !== false && this.lastActive !== false){
22204                     var last = this.last;
22205                     this.selectRange(this.last,  this.lastActive-1);
22206                     this.grid.getView().focusRow(this.lastActive);
22207                     if(last !== false){
22208                         this.last = last;
22209                     }
22210                 }else{
22211                     this.selectFirstRow();
22212                 }
22213                 this.fireEvent("afterselectionchange", this);
22214             },
22215             "down" : function(e){
22216                 if(!e.shiftKey){
22217                     this.selectNext(e.shiftKey);
22218                 }else if(this.last !== false && this.lastActive !== false){
22219                     var last = this.last;
22220                     this.selectRange(this.last,  this.lastActive+1);
22221                     this.grid.getView().focusRow(this.lastActive);
22222                     if(last !== false){
22223                         this.last = last;
22224                     }
22225                 }else{
22226                     this.selectFirstRow();
22227                 }
22228                 this.fireEvent("afterselectionchange", this);
22229             },
22230             scope: this
22231         });
22232
22233         var view = this.grid.view;
22234         view.on("refresh", this.onRefresh, this);
22235         view.on("rowupdated", this.onRowUpdated, this);
22236         view.on("rowremoved", this.onRemove, this);
22237     },
22238
22239     // private
22240     onRefresh : function(){
22241         var ds = this.grid.dataSource, i, v = this.grid.view;
22242         var s = this.selections;
22243         s.each(function(r){
22244             if((i = ds.indexOfId(r.id)) != -1){
22245                 v.onRowSelect(i);
22246             }else{
22247                 s.remove(r);
22248             }
22249         });
22250     },
22251
22252     // private
22253     onRemove : function(v, index, r){
22254         this.selections.remove(r);
22255     },
22256
22257     // private
22258     onRowUpdated : function(v, index, r){
22259         if(this.isSelected(r)){
22260             v.onRowSelect(index);
22261         }
22262     },
22263
22264     /**
22265      * Select records.
22266      * @param {Array} records The records to select
22267      * @param {Boolean} keepExisting (optional) True to keep existing selections
22268      */
22269     selectRecords : function(records, keepExisting){
22270         if(!keepExisting){
22271             this.clearSelections();
22272         }
22273         var ds = this.grid.dataSource;
22274         for(var i = 0, len = records.length; i < len; i++){
22275             this.selectRow(ds.indexOf(records[i]), true);
22276         }
22277     },
22278
22279     /**
22280      * Gets the number of selected rows.
22281      * @return {Number}
22282      */
22283     getCount : function(){
22284         return this.selections.length;
22285     },
22286
22287     /**
22288      * Selects the first row in the grid.
22289      */
22290     selectFirstRow : function(){
22291         this.selectRow(0);
22292     },
22293
22294     /**
22295      * Select the last row.
22296      * @param {Boolean} keepExisting (optional) True to keep existing selections
22297      */
22298     selectLastRow : function(keepExisting){
22299         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22300     },
22301
22302     /**
22303      * Selects the row immediately following the last selected row.
22304      * @param {Boolean} keepExisting (optional) True to keep existing selections
22305      */
22306     selectNext : function(keepExisting){
22307         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22308             this.selectRow(this.last+1, keepExisting);
22309             this.grid.getView().focusRow(this.last);
22310         }
22311     },
22312
22313     /**
22314      * Selects the row that precedes the last selected row.
22315      * @param {Boolean} keepExisting (optional) True to keep existing selections
22316      */
22317     selectPrevious : function(keepExisting){
22318         if(this.last){
22319             this.selectRow(this.last-1, keepExisting);
22320             this.grid.getView().focusRow(this.last);
22321         }
22322     },
22323
22324     /**
22325      * Returns the selected records
22326      * @return {Array} Array of selected records
22327      */
22328     getSelections : function(){
22329         return [].concat(this.selections.items);
22330     },
22331
22332     /**
22333      * Returns the first selected record.
22334      * @return {Record}
22335      */
22336     getSelected : function(){
22337         return this.selections.itemAt(0);
22338     },
22339
22340
22341     /**
22342      * Clears all selections.
22343      */
22344     clearSelections : function(fast){
22345         if(this.locked) {
22346             return;
22347         }
22348         if(fast !== true){
22349             var ds = this.grid.dataSource;
22350             var s = this.selections;
22351             s.each(function(r){
22352                 this.deselectRow(ds.indexOfId(r.id));
22353             }, this);
22354             s.clear();
22355         }else{
22356             this.selections.clear();
22357         }
22358         this.last = false;
22359     },
22360
22361
22362     /**
22363      * Selects all rows.
22364      */
22365     selectAll : function(){
22366         if(this.locked) {
22367             return;
22368         }
22369         this.selections.clear();
22370         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22371             this.selectRow(i, true);
22372         }
22373     },
22374
22375     /**
22376      * Returns True if there is a selection.
22377      * @return {Boolean}
22378      */
22379     hasSelection : function(){
22380         return this.selections.length > 0;
22381     },
22382
22383     /**
22384      * Returns True if the specified row is selected.
22385      * @param {Number/Record} record The record or index of the record to check
22386      * @return {Boolean}
22387      */
22388     isSelected : function(index){
22389         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22390         return (r && this.selections.key(r.id) ? true : false);
22391     },
22392
22393     /**
22394      * Returns True if the specified record id is selected.
22395      * @param {String} id The id of record to check
22396      * @return {Boolean}
22397      */
22398     isIdSelected : function(id){
22399         return (this.selections.key(id) ? true : false);
22400     },
22401
22402     // private
22403     handleMouseDown : function(e, t){
22404         var view = this.grid.getView(), rowIndex;
22405         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22406             return;
22407         };
22408         if(e.shiftKey && this.last !== false){
22409             var last = this.last;
22410             this.selectRange(last, rowIndex, e.ctrlKey);
22411             this.last = last; // reset the last
22412             view.focusRow(rowIndex);
22413         }else{
22414             var isSelected = this.isSelected(rowIndex);
22415             if(e.button !== 0 && isSelected){
22416                 view.focusRow(rowIndex);
22417             }else if(e.ctrlKey && isSelected){
22418                 this.deselectRow(rowIndex);
22419             }else if(!isSelected){
22420                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22421                 view.focusRow(rowIndex);
22422             }
22423         }
22424         this.fireEvent("afterselectionchange", this);
22425     },
22426     // private
22427     handleDragableRowClick :  function(grid, rowIndex, e) 
22428     {
22429         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22430             this.selectRow(rowIndex, false);
22431             grid.view.focusRow(rowIndex);
22432              this.fireEvent("afterselectionchange", this);
22433         }
22434     },
22435     
22436     /**
22437      * Selects multiple rows.
22438      * @param {Array} rows Array of the indexes of the row to select
22439      * @param {Boolean} keepExisting (optional) True to keep existing selections
22440      */
22441     selectRows : function(rows, keepExisting){
22442         if(!keepExisting){
22443             this.clearSelections();
22444         }
22445         for(var i = 0, len = rows.length; i < len; i++){
22446             this.selectRow(rows[i], true);
22447         }
22448     },
22449
22450     /**
22451      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22452      * @param {Number} startRow The index of the first row in the range
22453      * @param {Number} endRow The index of the last row in the range
22454      * @param {Boolean} keepExisting (optional) True to retain existing selections
22455      */
22456     selectRange : function(startRow, endRow, keepExisting){
22457         if(this.locked) {
22458             return;
22459         }
22460         if(!keepExisting){
22461             this.clearSelections();
22462         }
22463         if(startRow <= endRow){
22464             for(var i = startRow; i <= endRow; i++){
22465                 this.selectRow(i, true);
22466             }
22467         }else{
22468             for(var i = startRow; i >= endRow; i--){
22469                 this.selectRow(i, true);
22470             }
22471         }
22472     },
22473
22474     /**
22475      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22476      * @param {Number} startRow The index of the first row in the range
22477      * @param {Number} endRow The index of the last row in the range
22478      */
22479     deselectRange : function(startRow, endRow, preventViewNotify){
22480         if(this.locked) {
22481             return;
22482         }
22483         for(var i = startRow; i <= endRow; i++){
22484             this.deselectRow(i, preventViewNotify);
22485         }
22486     },
22487
22488     /**
22489      * Selects a row.
22490      * @param {Number} row The index of the row to select
22491      * @param {Boolean} keepExisting (optional) True to keep existing selections
22492      */
22493     selectRow : function(index, keepExisting, preventViewNotify){
22494         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22495             return;
22496         }
22497         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22498             if(!keepExisting || this.singleSelect){
22499                 this.clearSelections();
22500             }
22501             var r = this.grid.dataSource.getAt(index);
22502             this.selections.add(r);
22503             this.last = this.lastActive = index;
22504             if(!preventViewNotify){
22505                 this.grid.getView().onRowSelect(index);
22506             }
22507             this.fireEvent("rowselect", this, index, r);
22508             this.fireEvent("selectionchange", this);
22509         }
22510     },
22511
22512     /**
22513      * Deselects a row.
22514      * @param {Number} row The index of the row to deselect
22515      */
22516     deselectRow : function(index, preventViewNotify){
22517         if(this.locked) {
22518             return;
22519         }
22520         if(this.last == index){
22521             this.last = false;
22522         }
22523         if(this.lastActive == index){
22524             this.lastActive = false;
22525         }
22526         var r = this.grid.dataSource.getAt(index);
22527         this.selections.remove(r);
22528         if(!preventViewNotify){
22529             this.grid.getView().onRowDeselect(index);
22530         }
22531         this.fireEvent("rowdeselect", this, index);
22532         this.fireEvent("selectionchange", this);
22533     },
22534
22535     // private
22536     restoreLast : function(){
22537         if(this._last){
22538             this.last = this._last;
22539         }
22540     },
22541
22542     // private
22543     acceptsNav : function(row, col, cm){
22544         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22545     },
22546
22547     // private
22548     onEditorKey : function(field, e){
22549         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22550         if(k == e.TAB){
22551             e.stopEvent();
22552             ed.completeEdit();
22553             if(e.shiftKey){
22554                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22555             }else{
22556                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22557             }
22558         }else if(k == e.ENTER && !e.ctrlKey){
22559             e.stopEvent();
22560             ed.completeEdit();
22561             if(e.shiftKey){
22562                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22563             }else{
22564                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22565             }
22566         }else if(k == e.ESC){
22567             ed.cancelEdit();
22568         }
22569         if(newCell){
22570             g.startEditing(newCell[0], newCell[1]);
22571         }
22572     }
22573 });/*
22574  * Based on:
22575  * Ext JS Library 1.1.1
22576  * Copyright(c) 2006-2007, Ext JS, LLC.
22577  *
22578  * Originally Released Under LGPL - original licence link has changed is not relivant.
22579  *
22580  * Fork - LGPL
22581  * <script type="text/javascript">
22582  */
22583  
22584 /**
22585  * @class Roo.bootstrap.PagingToolbar
22586  * @extends Roo.bootstrap.NavSimplebar
22587  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22588  * @constructor
22589  * Create a new PagingToolbar
22590  * @param {Object} config The config object
22591  * @param {Roo.data.Store} store
22592  */
22593 Roo.bootstrap.PagingToolbar = function(config)
22594 {
22595     // old args format still supported... - xtype is prefered..
22596         // created from xtype...
22597     
22598     this.ds = config.dataSource;
22599     
22600     if (config.store && !this.ds) {
22601         this.store= Roo.factory(config.store, Roo.data);
22602         this.ds = this.store;
22603         this.ds.xmodule = this.xmodule || false;
22604     }
22605     
22606     this.toolbarItems = [];
22607     if (config.items) {
22608         this.toolbarItems = config.items;
22609     }
22610     
22611     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22612     
22613     this.cursor = 0;
22614     
22615     if (this.ds) { 
22616         this.bind(this.ds);
22617     }
22618     
22619     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22620     
22621 };
22622
22623 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22624     /**
22625      * @cfg {Roo.data.Store} dataSource
22626      * The underlying data store providing the paged data
22627      */
22628     /**
22629      * @cfg {String/HTMLElement/Element} container
22630      * container The id or element that will contain the toolbar
22631      */
22632     /**
22633      * @cfg {Boolean} displayInfo
22634      * True to display the displayMsg (defaults to false)
22635      */
22636     /**
22637      * @cfg {Number} pageSize
22638      * The number of records to display per page (defaults to 20)
22639      */
22640     pageSize: 20,
22641     /**
22642      * @cfg {String} displayMsg
22643      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22644      */
22645     displayMsg : 'Displaying {0} - {1} of {2}',
22646     /**
22647      * @cfg {String} emptyMsg
22648      * The message to display when no records are found (defaults to "No data to display")
22649      */
22650     emptyMsg : 'No data to display',
22651     /**
22652      * Customizable piece of the default paging text (defaults to "Page")
22653      * @type String
22654      */
22655     beforePageText : "Page",
22656     /**
22657      * Customizable piece of the default paging text (defaults to "of %0")
22658      * @type String
22659      */
22660     afterPageText : "of {0}",
22661     /**
22662      * Customizable piece of the default paging text (defaults to "First Page")
22663      * @type String
22664      */
22665     firstText : "First Page",
22666     /**
22667      * Customizable piece of the default paging text (defaults to "Previous Page")
22668      * @type String
22669      */
22670     prevText : "Previous Page",
22671     /**
22672      * Customizable piece of the default paging text (defaults to "Next Page")
22673      * @type String
22674      */
22675     nextText : "Next Page",
22676     /**
22677      * Customizable piece of the default paging text (defaults to "Last Page")
22678      * @type String
22679      */
22680     lastText : "Last Page",
22681     /**
22682      * Customizable piece of the default paging text (defaults to "Refresh")
22683      * @type String
22684      */
22685     refreshText : "Refresh",
22686
22687     buttons : false,
22688     // private
22689     onRender : function(ct, position) 
22690     {
22691         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22692         this.navgroup.parentId = this.id;
22693         this.navgroup.onRender(this.el, null);
22694         // add the buttons to the navgroup
22695         
22696         if(this.displayInfo){
22697             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22698             this.displayEl = this.el.select('.x-paging-info', true).first();
22699 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22700 //            this.displayEl = navel.el.select('span',true).first();
22701         }
22702         
22703         var _this = this;
22704         
22705         if(this.buttons){
22706             Roo.each(_this.buttons, function(e){ // this might need to use render????
22707                Roo.factory(e).onRender(_this.el, null);
22708             });
22709         }
22710             
22711         Roo.each(_this.toolbarItems, function(e) {
22712             _this.navgroup.addItem(e);
22713         });
22714         
22715         
22716         this.first = this.navgroup.addItem({
22717             tooltip: this.firstText,
22718             cls: "prev",
22719             icon : 'fa fa-backward',
22720             disabled: true,
22721             preventDefault: true,
22722             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22723         });
22724         
22725         this.prev =  this.navgroup.addItem({
22726             tooltip: this.prevText,
22727             cls: "prev",
22728             icon : 'fa fa-step-backward',
22729             disabled: true,
22730             preventDefault: true,
22731             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22732         });
22733     //this.addSeparator();
22734         
22735         
22736         var field = this.navgroup.addItem( {
22737             tagtype : 'span',
22738             cls : 'x-paging-position',
22739             
22740             html : this.beforePageText  +
22741                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22742                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22743          } ); //?? escaped?
22744         
22745         this.field = field.el.select('input', true).first();
22746         this.field.on("keydown", this.onPagingKeydown, this);
22747         this.field.on("focus", function(){this.dom.select();});
22748     
22749     
22750         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22751         //this.field.setHeight(18);
22752         //this.addSeparator();
22753         this.next = this.navgroup.addItem({
22754             tooltip: this.nextText,
22755             cls: "next",
22756             html : ' <i class="fa fa-step-forward">',
22757             disabled: true,
22758             preventDefault: true,
22759             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22760         });
22761         this.last = this.navgroup.addItem({
22762             tooltip: this.lastText,
22763             icon : 'fa fa-forward',
22764             cls: "next",
22765             disabled: true,
22766             preventDefault: true,
22767             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22768         });
22769     //this.addSeparator();
22770         this.loading = this.navgroup.addItem({
22771             tooltip: this.refreshText,
22772             icon: 'fa fa-refresh',
22773             preventDefault: true,
22774             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22775         });
22776         
22777     },
22778
22779     // private
22780     updateInfo : function(){
22781         if(this.displayEl){
22782             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22783             var msg = count == 0 ?
22784                 this.emptyMsg :
22785                 String.format(
22786                     this.displayMsg,
22787                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22788                 );
22789             this.displayEl.update(msg);
22790         }
22791     },
22792
22793     // private
22794     onLoad : function(ds, r, o){
22795        this.cursor = o.params ? o.params.start : 0;
22796        var d = this.getPageData(),
22797             ap = d.activePage,
22798             ps = d.pages;
22799         
22800        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22801        this.field.dom.value = ap;
22802        this.first.setDisabled(ap == 1);
22803        this.prev.setDisabled(ap == 1);
22804        this.next.setDisabled(ap == ps);
22805        this.last.setDisabled(ap == ps);
22806        this.loading.enable();
22807        this.updateInfo();
22808     },
22809
22810     // private
22811     getPageData : function(){
22812         var total = this.ds.getTotalCount();
22813         return {
22814             total : total,
22815             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22816             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22817         };
22818     },
22819
22820     // private
22821     onLoadError : function(){
22822         this.loading.enable();
22823     },
22824
22825     // private
22826     onPagingKeydown : function(e){
22827         var k = e.getKey();
22828         var d = this.getPageData();
22829         if(k == e.RETURN){
22830             var v = this.field.dom.value, pageNum;
22831             if(!v || isNaN(pageNum = parseInt(v, 10))){
22832                 this.field.dom.value = d.activePage;
22833                 return;
22834             }
22835             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22836             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22837             e.stopEvent();
22838         }
22839         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))
22840         {
22841           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22842           this.field.dom.value = pageNum;
22843           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22844           e.stopEvent();
22845         }
22846         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22847         {
22848           var v = this.field.dom.value, pageNum; 
22849           var increment = (e.shiftKey) ? 10 : 1;
22850           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22851                 increment *= -1;
22852           }
22853           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22854             this.field.dom.value = d.activePage;
22855             return;
22856           }
22857           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22858           {
22859             this.field.dom.value = parseInt(v, 10) + increment;
22860             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22861             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22862           }
22863           e.stopEvent();
22864         }
22865     },
22866
22867     // private
22868     beforeLoad : function(){
22869         if(this.loading){
22870             this.loading.disable();
22871         }
22872     },
22873
22874     // private
22875     onClick : function(which){
22876         
22877         var ds = this.ds;
22878         if (!ds) {
22879             return;
22880         }
22881         
22882         switch(which){
22883             case "first":
22884                 ds.load({params:{start: 0, limit: this.pageSize}});
22885             break;
22886             case "prev":
22887                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22888             break;
22889             case "next":
22890                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22891             break;
22892             case "last":
22893                 var total = ds.getTotalCount();
22894                 var extra = total % this.pageSize;
22895                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22896                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22897             break;
22898             case "refresh":
22899                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22900             break;
22901         }
22902     },
22903
22904     /**
22905      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22906      * @param {Roo.data.Store} store The data store to unbind
22907      */
22908     unbind : function(ds){
22909         ds.un("beforeload", this.beforeLoad, this);
22910         ds.un("load", this.onLoad, this);
22911         ds.un("loadexception", this.onLoadError, this);
22912         ds.un("remove", this.updateInfo, this);
22913         ds.un("add", this.updateInfo, this);
22914         this.ds = undefined;
22915     },
22916
22917     /**
22918      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22919      * @param {Roo.data.Store} store The data store to bind
22920      */
22921     bind : function(ds){
22922         ds.on("beforeload", this.beforeLoad, this);
22923         ds.on("load", this.onLoad, this);
22924         ds.on("loadexception", this.onLoadError, this);
22925         ds.on("remove", this.updateInfo, this);
22926         ds.on("add", this.updateInfo, this);
22927         this.ds = ds;
22928     }
22929 });/*
22930  * - LGPL
22931  *
22932  * element
22933  * 
22934  */
22935
22936 /**
22937  * @class Roo.bootstrap.MessageBar
22938  * @extends Roo.bootstrap.Component
22939  * Bootstrap MessageBar class
22940  * @cfg {String} html contents of the MessageBar
22941  * @cfg {String} weight (info | success | warning | danger) default info
22942  * @cfg {String} beforeClass insert the bar before the given class
22943  * @cfg {Boolean} closable (true | false) default false
22944  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22945  * 
22946  * @constructor
22947  * Create a new Element
22948  * @param {Object} config The config object
22949  */
22950
22951 Roo.bootstrap.MessageBar = function(config){
22952     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22953 };
22954
22955 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22956     
22957     html: '',
22958     weight: 'info',
22959     closable: false,
22960     fixed: false,
22961     beforeClass: 'bootstrap-sticky-wrap',
22962     
22963     getAutoCreate : function(){
22964         
22965         var cfg = {
22966             tag: 'div',
22967             cls: 'alert alert-dismissable alert-' + this.weight,
22968             cn: [
22969                 {
22970                     tag: 'span',
22971                     cls: 'message',
22972                     html: this.html || ''
22973                 }
22974             ]
22975         };
22976         
22977         if(this.fixed){
22978             cfg.cls += ' alert-messages-fixed';
22979         }
22980         
22981         if(this.closable){
22982             cfg.cn.push({
22983                 tag: 'button',
22984                 cls: 'close',
22985                 html: 'x'
22986             });
22987         }
22988         
22989         return cfg;
22990     },
22991     
22992     onRender : function(ct, position)
22993     {
22994         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22995         
22996         if(!this.el){
22997             var cfg = Roo.apply({},  this.getAutoCreate());
22998             cfg.id = Roo.id();
22999             
23000             if (this.cls) {
23001                 cfg.cls += ' ' + this.cls;
23002             }
23003             if (this.style) {
23004                 cfg.style = this.style;
23005             }
23006             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23007             
23008             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23009         }
23010         
23011         this.el.select('>button.close').on('click', this.hide, this);
23012         
23013     },
23014     
23015     show : function()
23016     {
23017         if (!this.rendered) {
23018             this.render();
23019         }
23020         
23021         this.el.show();
23022         
23023         this.fireEvent('show', this);
23024         
23025     },
23026     
23027     hide : function()
23028     {
23029         if (!this.rendered) {
23030             this.render();
23031         }
23032         
23033         this.el.hide();
23034         
23035         this.fireEvent('hide', this);
23036     },
23037     
23038     update : function()
23039     {
23040 //        var e = this.el.dom.firstChild;
23041 //        
23042 //        if(this.closable){
23043 //            e = e.nextSibling;
23044 //        }
23045 //        
23046 //        e.data = this.html || '';
23047
23048         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23049     }
23050    
23051 });
23052
23053  
23054
23055      /*
23056  * - LGPL
23057  *
23058  * Graph
23059  * 
23060  */
23061
23062
23063 /**
23064  * @class Roo.bootstrap.Graph
23065  * @extends Roo.bootstrap.Component
23066  * Bootstrap Graph class
23067 > Prameters
23068  -sm {number} sm 4
23069  -md {number} md 5
23070  @cfg {String} graphtype  bar | vbar | pie
23071  @cfg {number} g_x coodinator | centre x (pie)
23072  @cfg {number} g_y coodinator | centre y (pie)
23073  @cfg {number} g_r radius (pie)
23074  @cfg {number} g_height height of the chart (respected by all elements in the set)
23075  @cfg {number} g_width width of the chart (respected by all elements in the set)
23076  @cfg {Object} title The title of the chart
23077     
23078  -{Array}  values
23079  -opts (object) options for the chart 
23080      o {
23081      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23082      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23083      o vgutter (number)
23084      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.
23085      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23086      o to
23087      o stretch (boolean)
23088      o }
23089  -opts (object) options for the pie
23090      o{
23091      o cut
23092      o startAngle (number)
23093      o endAngle (number)
23094      } 
23095  *
23096  * @constructor
23097  * Create a new Input
23098  * @param {Object} config The config object
23099  */
23100
23101 Roo.bootstrap.Graph = function(config){
23102     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23103     
23104     this.addEvents({
23105         // img events
23106         /**
23107          * @event click
23108          * The img click event for the img.
23109          * @param {Roo.EventObject} e
23110          */
23111         "click" : true
23112     });
23113 };
23114
23115 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23116     
23117     sm: 4,
23118     md: 5,
23119     graphtype: 'bar',
23120     g_height: 250,
23121     g_width: 400,
23122     g_x: 50,
23123     g_y: 50,
23124     g_r: 30,
23125     opts:{
23126         //g_colors: this.colors,
23127         g_type: 'soft',
23128         g_gutter: '20%'
23129
23130     },
23131     title : false,
23132
23133     getAutoCreate : function(){
23134         
23135         var cfg = {
23136             tag: 'div',
23137             html : null
23138         };
23139         
23140         
23141         return  cfg;
23142     },
23143
23144     onRender : function(ct,position){
23145         
23146         
23147         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23148         
23149         if (typeof(Raphael) == 'undefined') {
23150             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23151             return;
23152         }
23153         
23154         this.raphael = Raphael(this.el.dom);
23155         
23156                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23157                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23158                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23159                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23160                 /*
23161                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23162                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23163                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23164                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23165                 
23166                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23167                 r.barchart(330, 10, 300, 220, data1);
23168                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23169                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23170                 */
23171                 
23172                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23173                 // r.barchart(30, 30, 560, 250,  xdata, {
23174                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23175                 //     axis : "0 0 1 1",
23176                 //     axisxlabels :  xdata
23177                 //     //yvalues : cols,
23178                    
23179                 // });
23180 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23181 //        
23182 //        this.load(null,xdata,{
23183 //                axis : "0 0 1 1",
23184 //                axisxlabels :  xdata
23185 //                });
23186
23187     },
23188
23189     load : function(graphtype,xdata,opts)
23190     {
23191         this.raphael.clear();
23192         if(!graphtype) {
23193             graphtype = this.graphtype;
23194         }
23195         if(!opts){
23196             opts = this.opts;
23197         }
23198         var r = this.raphael,
23199             fin = function () {
23200                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23201             },
23202             fout = function () {
23203                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23204             },
23205             pfin = function() {
23206                 this.sector.stop();
23207                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23208
23209                 if (this.label) {
23210                     this.label[0].stop();
23211                     this.label[0].attr({ r: 7.5 });
23212                     this.label[1].attr({ "font-weight": 800 });
23213                 }
23214             },
23215             pfout = function() {
23216                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23217
23218                 if (this.label) {
23219                     this.label[0].animate({ r: 5 }, 500, "bounce");
23220                     this.label[1].attr({ "font-weight": 400 });
23221                 }
23222             };
23223
23224         switch(graphtype){
23225             case 'bar':
23226                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23227                 break;
23228             case 'hbar':
23229                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23230                 break;
23231             case 'pie':
23232 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23233 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23234 //            
23235                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23236                 
23237                 break;
23238
23239         }
23240         
23241         if(this.title){
23242             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23243         }
23244         
23245     },
23246     
23247     setTitle: function(o)
23248     {
23249         this.title = o;
23250     },
23251     
23252     initEvents: function() {
23253         
23254         if(!this.href){
23255             this.el.on('click', this.onClick, this);
23256         }
23257     },
23258     
23259     onClick : function(e)
23260     {
23261         Roo.log('img onclick');
23262         this.fireEvent('click', this, e);
23263     }
23264    
23265 });
23266
23267  
23268 /*
23269  * - LGPL
23270  *
23271  * numberBox
23272  * 
23273  */
23274 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23275
23276 /**
23277  * @class Roo.bootstrap.dash.NumberBox
23278  * @extends Roo.bootstrap.Component
23279  * Bootstrap NumberBox class
23280  * @cfg {String} headline Box headline
23281  * @cfg {String} content Box content
23282  * @cfg {String} icon Box icon
23283  * @cfg {String} footer Footer text
23284  * @cfg {String} fhref Footer href
23285  * 
23286  * @constructor
23287  * Create a new NumberBox
23288  * @param {Object} config The config object
23289  */
23290
23291
23292 Roo.bootstrap.dash.NumberBox = function(config){
23293     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23294     
23295 };
23296
23297 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23298     
23299     headline : '',
23300     content : '',
23301     icon : '',
23302     footer : '',
23303     fhref : '',
23304     ficon : '',
23305     
23306     getAutoCreate : function(){
23307         
23308         var cfg = {
23309             tag : 'div',
23310             cls : 'small-box ',
23311             cn : [
23312                 {
23313                     tag : 'div',
23314                     cls : 'inner',
23315                     cn :[
23316                         {
23317                             tag : 'h3',
23318                             cls : 'roo-headline',
23319                             html : this.headline
23320                         },
23321                         {
23322                             tag : 'p',
23323                             cls : 'roo-content',
23324                             html : this.content
23325                         }
23326                     ]
23327                 }
23328             ]
23329         };
23330         
23331         if(this.icon){
23332             cfg.cn.push({
23333                 tag : 'div',
23334                 cls : 'icon',
23335                 cn :[
23336                     {
23337                         tag : 'i',
23338                         cls : 'ion ' + this.icon
23339                     }
23340                 ]
23341             });
23342         }
23343         
23344         if(this.footer){
23345             var footer = {
23346                 tag : 'a',
23347                 cls : 'small-box-footer',
23348                 href : this.fhref || '#',
23349                 html : this.footer
23350             };
23351             
23352             cfg.cn.push(footer);
23353             
23354         }
23355         
23356         return  cfg;
23357     },
23358
23359     onRender : function(ct,position){
23360         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23361
23362
23363        
23364                 
23365     },
23366
23367     setHeadline: function (value)
23368     {
23369         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23370     },
23371     
23372     setFooter: function (value, href)
23373     {
23374         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23375         
23376         if(href){
23377             this.el.select('a.small-box-footer',true).first().attr('href', href);
23378         }
23379         
23380     },
23381
23382     setContent: function (value)
23383     {
23384         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23385     },
23386
23387     initEvents: function() 
23388     {   
23389         
23390     }
23391     
23392 });
23393
23394  
23395 /*
23396  * - LGPL
23397  *
23398  * TabBox
23399  * 
23400  */
23401 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23402
23403 /**
23404  * @class Roo.bootstrap.dash.TabBox
23405  * @extends Roo.bootstrap.Component
23406  * Bootstrap TabBox class
23407  * @cfg {String} title Title of the TabBox
23408  * @cfg {String} icon Icon of the TabBox
23409  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23410  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23411  * 
23412  * @constructor
23413  * Create a new TabBox
23414  * @param {Object} config The config object
23415  */
23416
23417
23418 Roo.bootstrap.dash.TabBox = function(config){
23419     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23420     this.addEvents({
23421         // raw events
23422         /**
23423          * @event addpane
23424          * When a pane is added
23425          * @param {Roo.bootstrap.dash.TabPane} pane
23426          */
23427         "addpane" : true,
23428         /**
23429          * @event activatepane
23430          * When a pane is activated
23431          * @param {Roo.bootstrap.dash.TabPane} pane
23432          */
23433         "activatepane" : true
23434         
23435          
23436     });
23437     
23438     this.panes = [];
23439 };
23440
23441 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23442
23443     title : '',
23444     icon : false,
23445     showtabs : true,
23446     tabScrollable : false,
23447     
23448     getChildContainer : function()
23449     {
23450         return this.el.select('.tab-content', true).first();
23451     },
23452     
23453     getAutoCreate : function(){
23454         
23455         var header = {
23456             tag: 'li',
23457             cls: 'pull-left header',
23458             html: this.title,
23459             cn : []
23460         };
23461         
23462         if(this.icon){
23463             header.cn.push({
23464                 tag: 'i',
23465                 cls: 'fa ' + this.icon
23466             });
23467         }
23468         
23469         var h = {
23470             tag: 'ul',
23471             cls: 'nav nav-tabs pull-right',
23472             cn: [
23473                 header
23474             ]
23475         };
23476         
23477         if(this.tabScrollable){
23478             h = {
23479                 tag: 'div',
23480                 cls: 'tab-header',
23481                 cn: [
23482                     {
23483                         tag: 'ul',
23484                         cls: 'nav nav-tabs pull-right',
23485                         cn: [
23486                             header
23487                         ]
23488                     }
23489                 ]
23490             };
23491         }
23492         
23493         var cfg = {
23494             tag: 'div',
23495             cls: 'nav-tabs-custom',
23496             cn: [
23497                 h,
23498                 {
23499                     tag: 'div',
23500                     cls: 'tab-content no-padding',
23501                     cn: []
23502                 }
23503             ]
23504         };
23505
23506         return  cfg;
23507     },
23508     initEvents : function()
23509     {
23510         //Roo.log('add add pane handler');
23511         this.on('addpane', this.onAddPane, this);
23512     },
23513      /**
23514      * Updates the box title
23515      * @param {String} html to set the title to.
23516      */
23517     setTitle : function(value)
23518     {
23519         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23520     },
23521     onAddPane : function(pane)
23522     {
23523         this.panes.push(pane);
23524         //Roo.log('addpane');
23525         //Roo.log(pane);
23526         // tabs are rendere left to right..
23527         if(!this.showtabs){
23528             return;
23529         }
23530         
23531         var ctr = this.el.select('.nav-tabs', true).first();
23532          
23533          
23534         var existing = ctr.select('.nav-tab',true);
23535         var qty = existing.getCount();;
23536         
23537         
23538         var tab = ctr.createChild({
23539             tag : 'li',
23540             cls : 'nav-tab' + (qty ? '' : ' active'),
23541             cn : [
23542                 {
23543                     tag : 'a',
23544                     href:'#',
23545                     html : pane.title
23546                 }
23547             ]
23548         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23549         pane.tab = tab;
23550         
23551         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23552         if (!qty) {
23553             pane.el.addClass('active');
23554         }
23555         
23556                 
23557     },
23558     onTabClick : function(ev,un,ob,pane)
23559     {
23560         //Roo.log('tab - prev default');
23561         ev.preventDefault();
23562         
23563         
23564         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23565         pane.tab.addClass('active');
23566         //Roo.log(pane.title);
23567         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23568         // technically we should have a deactivate event.. but maybe add later.
23569         // and it should not de-activate the selected tab...
23570         this.fireEvent('activatepane', pane);
23571         pane.el.addClass('active');
23572         pane.fireEvent('activate');
23573         
23574         
23575     },
23576     
23577     getActivePane : function()
23578     {
23579         var r = false;
23580         Roo.each(this.panes, function(p) {
23581             if(p.el.hasClass('active')){
23582                 r = p;
23583                 return false;
23584             }
23585             
23586             return;
23587         });
23588         
23589         return r;
23590     }
23591     
23592     
23593 });
23594
23595  
23596 /*
23597  * - LGPL
23598  *
23599  * Tab pane
23600  * 
23601  */
23602 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23603 /**
23604  * @class Roo.bootstrap.TabPane
23605  * @extends Roo.bootstrap.Component
23606  * Bootstrap TabPane class
23607  * @cfg {Boolean} active (false | true) Default false
23608  * @cfg {String} title title of panel
23609
23610  * 
23611  * @constructor
23612  * Create a new TabPane
23613  * @param {Object} config The config object
23614  */
23615
23616 Roo.bootstrap.dash.TabPane = function(config){
23617     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23618     
23619     this.addEvents({
23620         // raw events
23621         /**
23622          * @event activate
23623          * When a pane is activated
23624          * @param {Roo.bootstrap.dash.TabPane} pane
23625          */
23626         "activate" : true
23627          
23628     });
23629 };
23630
23631 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23632     
23633     active : false,
23634     title : '',
23635     
23636     // the tabBox that this is attached to.
23637     tab : false,
23638      
23639     getAutoCreate : function() 
23640     {
23641         var cfg = {
23642             tag: 'div',
23643             cls: 'tab-pane'
23644         };
23645         
23646         if(this.active){
23647             cfg.cls += ' active';
23648         }
23649         
23650         return cfg;
23651     },
23652     initEvents  : function()
23653     {
23654         //Roo.log('trigger add pane handler');
23655         this.parent().fireEvent('addpane', this)
23656     },
23657     
23658      /**
23659      * Updates the tab title 
23660      * @param {String} html to set the title to.
23661      */
23662     setTitle: function(str)
23663     {
23664         if (!this.tab) {
23665             return;
23666         }
23667         this.title = str;
23668         this.tab.select('a', true).first().dom.innerHTML = str;
23669         
23670     }
23671     
23672     
23673     
23674 });
23675
23676  
23677
23678
23679  /*
23680  * - LGPL
23681  *
23682  * menu
23683  * 
23684  */
23685 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23686
23687 /**
23688  * @class Roo.bootstrap.menu.Menu
23689  * @extends Roo.bootstrap.Component
23690  * Bootstrap Menu class - container for Menu
23691  * @cfg {String} html Text of the menu
23692  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23693  * @cfg {String} icon Font awesome icon
23694  * @cfg {String} pos Menu align to (top | bottom) default bottom
23695  * 
23696  * 
23697  * @constructor
23698  * Create a new Menu
23699  * @param {Object} config The config object
23700  */
23701
23702
23703 Roo.bootstrap.menu.Menu = function(config){
23704     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23705     
23706     this.addEvents({
23707         /**
23708          * @event beforeshow
23709          * Fires before this menu is displayed
23710          * @param {Roo.bootstrap.menu.Menu} this
23711          */
23712         beforeshow : true,
23713         /**
23714          * @event beforehide
23715          * Fires before this menu is hidden
23716          * @param {Roo.bootstrap.menu.Menu} this
23717          */
23718         beforehide : true,
23719         /**
23720          * @event show
23721          * Fires after this menu is displayed
23722          * @param {Roo.bootstrap.menu.Menu} this
23723          */
23724         show : true,
23725         /**
23726          * @event hide
23727          * Fires after this menu is hidden
23728          * @param {Roo.bootstrap.menu.Menu} this
23729          */
23730         hide : true,
23731         /**
23732          * @event click
23733          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23734          * @param {Roo.bootstrap.menu.Menu} this
23735          * @param {Roo.EventObject} e
23736          */
23737         click : true
23738     });
23739     
23740 };
23741
23742 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23743     
23744     submenu : false,
23745     html : '',
23746     weight : 'default',
23747     icon : false,
23748     pos : 'bottom',
23749     
23750     
23751     getChildContainer : function() {
23752         if(this.isSubMenu){
23753             return this.el;
23754         }
23755         
23756         return this.el.select('ul.dropdown-menu', true).first();  
23757     },
23758     
23759     getAutoCreate : function()
23760     {
23761         var text = [
23762             {
23763                 tag : 'span',
23764                 cls : 'roo-menu-text',
23765                 html : this.html
23766             }
23767         ];
23768         
23769         if(this.icon){
23770             text.unshift({
23771                 tag : 'i',
23772                 cls : 'fa ' + this.icon
23773             })
23774         }
23775         
23776         
23777         var cfg = {
23778             tag : 'div',
23779             cls : 'btn-group',
23780             cn : [
23781                 {
23782                     tag : 'button',
23783                     cls : 'dropdown-button btn btn-' + this.weight,
23784                     cn : text
23785                 },
23786                 {
23787                     tag : 'button',
23788                     cls : 'dropdown-toggle btn btn-' + this.weight,
23789                     cn : [
23790                         {
23791                             tag : 'span',
23792                             cls : 'caret'
23793                         }
23794                     ]
23795                 },
23796                 {
23797                     tag : 'ul',
23798                     cls : 'dropdown-menu'
23799                 }
23800             ]
23801             
23802         };
23803         
23804         if(this.pos == 'top'){
23805             cfg.cls += ' dropup';
23806         }
23807         
23808         if(this.isSubMenu){
23809             cfg = {
23810                 tag : 'ul',
23811                 cls : 'dropdown-menu'
23812             }
23813         }
23814         
23815         return cfg;
23816     },
23817     
23818     onRender : function(ct, position)
23819     {
23820         this.isSubMenu = ct.hasClass('dropdown-submenu');
23821         
23822         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23823     },
23824     
23825     initEvents : function() 
23826     {
23827         if(this.isSubMenu){
23828             return;
23829         }
23830         
23831         this.hidden = true;
23832         
23833         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23834         this.triggerEl.on('click', this.onTriggerPress, this);
23835         
23836         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23837         this.buttonEl.on('click', this.onClick, this);
23838         
23839     },
23840     
23841     list : function()
23842     {
23843         if(this.isSubMenu){
23844             return this.el;
23845         }
23846         
23847         return this.el.select('ul.dropdown-menu', true).first();
23848     },
23849     
23850     onClick : function(e)
23851     {
23852         this.fireEvent("click", this, e);
23853     },
23854     
23855     onTriggerPress  : function(e)
23856     {   
23857         if (this.isVisible()) {
23858             this.hide();
23859         } else {
23860             this.show();
23861         }
23862     },
23863     
23864     isVisible : function(){
23865         return !this.hidden;
23866     },
23867     
23868     show : function()
23869     {
23870         this.fireEvent("beforeshow", this);
23871         
23872         this.hidden = false;
23873         this.el.addClass('open');
23874         
23875         Roo.get(document).on("mouseup", this.onMouseUp, this);
23876         
23877         this.fireEvent("show", this);
23878         
23879         
23880     },
23881     
23882     hide : function()
23883     {
23884         this.fireEvent("beforehide", this);
23885         
23886         this.hidden = true;
23887         this.el.removeClass('open');
23888         
23889         Roo.get(document).un("mouseup", this.onMouseUp);
23890         
23891         this.fireEvent("hide", this);
23892     },
23893     
23894     onMouseUp : function()
23895     {
23896         this.hide();
23897     }
23898     
23899 });
23900
23901  
23902  /*
23903  * - LGPL
23904  *
23905  * menu item
23906  * 
23907  */
23908 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23909
23910 /**
23911  * @class Roo.bootstrap.menu.Item
23912  * @extends Roo.bootstrap.Component
23913  * Bootstrap MenuItem class
23914  * @cfg {Boolean} submenu (true | false) default false
23915  * @cfg {String} html text of the item
23916  * @cfg {String} href the link
23917  * @cfg {Boolean} disable (true | false) default false
23918  * @cfg {Boolean} preventDefault (true | false) default true
23919  * @cfg {String} icon Font awesome icon
23920  * @cfg {String} pos Submenu align to (left | right) default right 
23921  * 
23922  * 
23923  * @constructor
23924  * Create a new Item
23925  * @param {Object} config The config object
23926  */
23927
23928
23929 Roo.bootstrap.menu.Item = function(config){
23930     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23931     this.addEvents({
23932         /**
23933          * @event mouseover
23934          * Fires when the mouse is hovering over this menu
23935          * @param {Roo.bootstrap.menu.Item} this
23936          * @param {Roo.EventObject} e
23937          */
23938         mouseover : true,
23939         /**
23940          * @event mouseout
23941          * Fires when the mouse exits this menu
23942          * @param {Roo.bootstrap.menu.Item} this
23943          * @param {Roo.EventObject} e
23944          */
23945         mouseout : true,
23946         // raw events
23947         /**
23948          * @event click
23949          * The raw click event for the entire grid.
23950          * @param {Roo.EventObject} e
23951          */
23952         click : true
23953     });
23954 };
23955
23956 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23957     
23958     submenu : false,
23959     href : '',
23960     html : '',
23961     preventDefault: true,
23962     disable : false,
23963     icon : false,
23964     pos : 'right',
23965     
23966     getAutoCreate : function()
23967     {
23968         var text = [
23969             {
23970                 tag : 'span',
23971                 cls : 'roo-menu-item-text',
23972                 html : this.html
23973             }
23974         ];
23975         
23976         if(this.icon){
23977             text.unshift({
23978                 tag : 'i',
23979                 cls : 'fa ' + this.icon
23980             })
23981         }
23982         
23983         var cfg = {
23984             tag : 'li',
23985             cn : [
23986                 {
23987                     tag : 'a',
23988                     href : this.href || '#',
23989                     cn : text
23990                 }
23991             ]
23992         };
23993         
23994         if(this.disable){
23995             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23996         }
23997         
23998         if(this.submenu){
23999             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24000             
24001             if(this.pos == 'left'){
24002                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24003             }
24004         }
24005         
24006         return cfg;
24007     },
24008     
24009     initEvents : function() 
24010     {
24011         this.el.on('mouseover', this.onMouseOver, this);
24012         this.el.on('mouseout', this.onMouseOut, this);
24013         
24014         this.el.select('a', true).first().on('click', this.onClick, this);
24015         
24016     },
24017     
24018     onClick : function(e)
24019     {
24020         if(this.preventDefault){
24021             e.preventDefault();
24022         }
24023         
24024         this.fireEvent("click", this, e);
24025     },
24026     
24027     onMouseOver : function(e)
24028     {
24029         if(this.submenu && this.pos == 'left'){
24030             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24031         }
24032         
24033         this.fireEvent("mouseover", this, e);
24034     },
24035     
24036     onMouseOut : function(e)
24037     {
24038         this.fireEvent("mouseout", this, e);
24039     }
24040 });
24041
24042  
24043
24044  /*
24045  * - LGPL
24046  *
24047  * menu separator
24048  * 
24049  */
24050 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24051
24052 /**
24053  * @class Roo.bootstrap.menu.Separator
24054  * @extends Roo.bootstrap.Component
24055  * Bootstrap Separator class
24056  * 
24057  * @constructor
24058  * Create a new Separator
24059  * @param {Object} config The config object
24060  */
24061
24062
24063 Roo.bootstrap.menu.Separator = function(config){
24064     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24065 };
24066
24067 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24068     
24069     getAutoCreate : function(){
24070         var cfg = {
24071             tag : 'li',
24072             cls: 'divider'
24073         };
24074         
24075         return cfg;
24076     }
24077    
24078 });
24079
24080  
24081
24082  /*
24083  * - LGPL
24084  *
24085  * Tooltip
24086  * 
24087  */
24088
24089 /**
24090  * @class Roo.bootstrap.Tooltip
24091  * Bootstrap Tooltip class
24092  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24093  * to determine which dom element triggers the tooltip.
24094  * 
24095  * It needs to add support for additional attributes like tooltip-position
24096  * 
24097  * @constructor
24098  * Create a new Toolti
24099  * @param {Object} config The config object
24100  */
24101
24102 Roo.bootstrap.Tooltip = function(config){
24103     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24104 };
24105
24106 Roo.apply(Roo.bootstrap.Tooltip, {
24107     /**
24108      * @function init initialize tooltip monitoring.
24109      * @static
24110      */
24111     currentEl : false,
24112     currentTip : false,
24113     currentRegion : false,
24114     
24115     //  init : delay?
24116     
24117     init : function()
24118     {
24119         Roo.get(document).on('mouseover', this.enter ,this);
24120         Roo.get(document).on('mouseout', this.leave, this);
24121          
24122         
24123         this.currentTip = new Roo.bootstrap.Tooltip();
24124     },
24125     
24126     enter : function(ev)
24127     {
24128         var dom = ev.getTarget();
24129         
24130         //Roo.log(['enter',dom]);
24131         var el = Roo.fly(dom);
24132         if (this.currentEl) {
24133             //Roo.log(dom);
24134             //Roo.log(this.currentEl);
24135             //Roo.log(this.currentEl.contains(dom));
24136             if (this.currentEl == el) {
24137                 return;
24138             }
24139             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24140                 return;
24141             }
24142
24143         }
24144         
24145         if (this.currentTip.el) {
24146             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24147         }    
24148         //Roo.log(ev);
24149         var bindEl = el;
24150         
24151         // you can not look for children, as if el is the body.. then everythign is the child..
24152         if (!el.attr('tooltip')) { //
24153             if (!el.select("[tooltip]").elements.length) {
24154                 return;
24155             }
24156             // is the mouse over this child...?
24157             bindEl = el.select("[tooltip]").first();
24158             var xy = ev.getXY();
24159             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24160                 //Roo.log("not in region.");
24161                 return;
24162             }
24163             //Roo.log("child element over..");
24164             
24165         }
24166         this.currentEl = bindEl;
24167         this.currentTip.bind(bindEl);
24168         this.currentRegion = Roo.lib.Region.getRegion(dom);
24169         this.currentTip.enter();
24170         
24171     },
24172     leave : function(ev)
24173     {
24174         var dom = ev.getTarget();
24175         //Roo.log(['leave',dom]);
24176         if (!this.currentEl) {
24177             return;
24178         }
24179         
24180         
24181         if (dom != this.currentEl.dom) {
24182             return;
24183         }
24184         var xy = ev.getXY();
24185         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24186             return;
24187         }
24188         // only activate leave if mouse cursor is outside... bounding box..
24189         
24190         
24191         
24192         
24193         if (this.currentTip) {
24194             this.currentTip.leave();
24195         }
24196         //Roo.log('clear currentEl');
24197         this.currentEl = false;
24198         
24199         
24200     },
24201     alignment : {
24202         'left' : ['r-l', [-2,0], 'right'],
24203         'right' : ['l-r', [2,0], 'left'],
24204         'bottom' : ['t-b', [0,2], 'top'],
24205         'top' : [ 'b-t', [0,-2], 'bottom']
24206     }
24207     
24208 });
24209
24210
24211 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24212     
24213     
24214     bindEl : false,
24215     
24216     delay : null, // can be { show : 300 , hide: 500}
24217     
24218     timeout : null,
24219     
24220     hoverState : null, //???
24221     
24222     placement : 'bottom', 
24223     
24224     getAutoCreate : function(){
24225     
24226         var cfg = {
24227            cls : 'tooltip',
24228            role : 'tooltip',
24229            cn : [
24230                 {
24231                     cls : 'tooltip-arrow'
24232                 },
24233                 {
24234                     cls : 'tooltip-inner'
24235                 }
24236            ]
24237         };
24238         
24239         return cfg;
24240     },
24241     bind : function(el)
24242     {
24243         this.bindEl = el;
24244     },
24245       
24246     
24247     enter : function () {
24248        
24249         if (this.timeout != null) {
24250             clearTimeout(this.timeout);
24251         }
24252         
24253         this.hoverState = 'in';
24254          //Roo.log("enter - show");
24255         if (!this.delay || !this.delay.show) {
24256             this.show();
24257             return;
24258         }
24259         var _t = this;
24260         this.timeout = setTimeout(function () {
24261             if (_t.hoverState == 'in') {
24262                 _t.show();
24263             }
24264         }, this.delay.show);
24265     },
24266     leave : function()
24267     {
24268         clearTimeout(this.timeout);
24269     
24270         this.hoverState = 'out';
24271          if (!this.delay || !this.delay.hide) {
24272             this.hide();
24273             return;
24274         }
24275        
24276         var _t = this;
24277         this.timeout = setTimeout(function () {
24278             //Roo.log("leave - timeout");
24279             
24280             if (_t.hoverState == 'out') {
24281                 _t.hide();
24282                 Roo.bootstrap.Tooltip.currentEl = false;
24283             }
24284         }, delay);
24285     },
24286     
24287     show : function ()
24288     {
24289         if (!this.el) {
24290             this.render(document.body);
24291         }
24292         // set content.
24293         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24294         
24295         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24296         
24297         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24298         
24299         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24300         
24301         var placement = typeof this.placement == 'function' ?
24302             this.placement.call(this, this.el, on_el) :
24303             this.placement;
24304             
24305         var autoToken = /\s?auto?\s?/i;
24306         var autoPlace = autoToken.test(placement);
24307         if (autoPlace) {
24308             placement = placement.replace(autoToken, '') || 'top';
24309         }
24310         
24311         //this.el.detach()
24312         //this.el.setXY([0,0]);
24313         this.el.show();
24314         //this.el.dom.style.display='block';
24315         
24316         //this.el.appendTo(on_el);
24317         
24318         var p = this.getPosition();
24319         var box = this.el.getBox();
24320         
24321         if (autoPlace) {
24322             // fixme..
24323         }
24324         
24325         var align = Roo.bootstrap.Tooltip.alignment[placement];
24326         
24327         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24328         
24329         if(placement == 'top' || placement == 'bottom'){
24330             if(xy[0] < 0){
24331                 placement = 'right';
24332             }
24333             
24334             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24335                 placement = 'left';
24336             }
24337         }
24338         
24339         align = Roo.bootstrap.Tooltip.alignment[placement];
24340         
24341         this.el.alignTo(this.bindEl, align[0],align[1]);
24342         //var arrow = this.el.select('.arrow',true).first();
24343         //arrow.set(align[2], 
24344         
24345         this.el.addClass(placement);
24346         
24347         this.el.addClass('in fade');
24348         
24349         this.hoverState = null;
24350         
24351         if (this.el.hasClass('fade')) {
24352             // fade it?
24353         }
24354         
24355     },
24356     hide : function()
24357     {
24358          
24359         if (!this.el) {
24360             return;
24361         }
24362         //this.el.setXY([0,0]);
24363         this.el.removeClass('in');
24364         //this.el.hide();
24365         
24366     }
24367     
24368 });
24369  
24370
24371  /*
24372  * - LGPL
24373  *
24374  * Location Picker
24375  * 
24376  */
24377
24378 /**
24379  * @class Roo.bootstrap.LocationPicker
24380  * @extends Roo.bootstrap.Component
24381  * Bootstrap LocationPicker class
24382  * @cfg {Number} latitude Position when init default 0
24383  * @cfg {Number} longitude Position when init default 0
24384  * @cfg {Number} zoom default 15
24385  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24386  * @cfg {Boolean} mapTypeControl default false
24387  * @cfg {Boolean} disableDoubleClickZoom default false
24388  * @cfg {Boolean} scrollwheel default true
24389  * @cfg {Boolean} streetViewControl default false
24390  * @cfg {Number} radius default 0
24391  * @cfg {String} locationName
24392  * @cfg {Boolean} draggable default true
24393  * @cfg {Boolean} enableAutocomplete default false
24394  * @cfg {Boolean} enableReverseGeocode default true
24395  * @cfg {String} markerTitle
24396  * 
24397  * @constructor
24398  * Create a new LocationPicker
24399  * @param {Object} config The config object
24400  */
24401
24402
24403 Roo.bootstrap.LocationPicker = function(config){
24404     
24405     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24406     
24407     this.addEvents({
24408         /**
24409          * @event initial
24410          * Fires when the picker initialized.
24411          * @param {Roo.bootstrap.LocationPicker} this
24412          * @param {Google Location} location
24413          */
24414         initial : true,
24415         /**
24416          * @event positionchanged
24417          * Fires when the picker position changed.
24418          * @param {Roo.bootstrap.LocationPicker} this
24419          * @param {Google Location} location
24420          */
24421         positionchanged : true,
24422         /**
24423          * @event resize
24424          * Fires when the map resize.
24425          * @param {Roo.bootstrap.LocationPicker} this
24426          */
24427         resize : true,
24428         /**
24429          * @event show
24430          * Fires when the map show.
24431          * @param {Roo.bootstrap.LocationPicker} this
24432          */
24433         show : true,
24434         /**
24435          * @event hide
24436          * Fires when the map hide.
24437          * @param {Roo.bootstrap.LocationPicker} this
24438          */
24439         hide : true,
24440         /**
24441          * @event mapClick
24442          * Fires when click the map.
24443          * @param {Roo.bootstrap.LocationPicker} this
24444          * @param {Map event} e
24445          */
24446         mapClick : true,
24447         /**
24448          * @event mapRightClick
24449          * Fires when right click the map.
24450          * @param {Roo.bootstrap.LocationPicker} this
24451          * @param {Map event} e
24452          */
24453         mapRightClick : true,
24454         /**
24455          * @event markerClick
24456          * Fires when click the marker.
24457          * @param {Roo.bootstrap.LocationPicker} this
24458          * @param {Map event} e
24459          */
24460         markerClick : true,
24461         /**
24462          * @event markerRightClick
24463          * Fires when right click the marker.
24464          * @param {Roo.bootstrap.LocationPicker} this
24465          * @param {Map event} e
24466          */
24467         markerRightClick : true,
24468         /**
24469          * @event OverlayViewDraw
24470          * Fires when OverlayView Draw
24471          * @param {Roo.bootstrap.LocationPicker} this
24472          */
24473         OverlayViewDraw : true,
24474         /**
24475          * @event OverlayViewOnAdd
24476          * Fires when OverlayView Draw
24477          * @param {Roo.bootstrap.LocationPicker} this
24478          */
24479         OverlayViewOnAdd : true,
24480         /**
24481          * @event OverlayViewOnRemove
24482          * Fires when OverlayView Draw
24483          * @param {Roo.bootstrap.LocationPicker} this
24484          */
24485         OverlayViewOnRemove : true,
24486         /**
24487          * @event OverlayViewShow
24488          * Fires when OverlayView Draw
24489          * @param {Roo.bootstrap.LocationPicker} this
24490          * @param {Pixel} cpx
24491          */
24492         OverlayViewShow : true,
24493         /**
24494          * @event OverlayViewHide
24495          * Fires when OverlayView Draw
24496          * @param {Roo.bootstrap.LocationPicker} this
24497          */
24498         OverlayViewHide : true,
24499         /**
24500          * @event loadexception
24501          * Fires when load google lib failed.
24502          * @param {Roo.bootstrap.LocationPicker} this
24503          */
24504         loadexception : true
24505     });
24506         
24507 };
24508
24509 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24510     
24511     gMapContext: false,
24512     
24513     latitude: 0,
24514     longitude: 0,
24515     zoom: 15,
24516     mapTypeId: false,
24517     mapTypeControl: false,
24518     disableDoubleClickZoom: false,
24519     scrollwheel: true,
24520     streetViewControl: false,
24521     radius: 0,
24522     locationName: '',
24523     draggable: true,
24524     enableAutocomplete: false,
24525     enableReverseGeocode: true,
24526     markerTitle: '',
24527     
24528     getAutoCreate: function()
24529     {
24530
24531         var cfg = {
24532             tag: 'div',
24533             cls: 'roo-location-picker'
24534         };
24535         
24536         return cfg
24537     },
24538     
24539     initEvents: function(ct, position)
24540     {       
24541         if(!this.el.getWidth() || this.isApplied()){
24542             return;
24543         }
24544         
24545         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24546         
24547         this.initial();
24548     },
24549     
24550     initial: function()
24551     {
24552         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24553             this.fireEvent('loadexception', this);
24554             return;
24555         }
24556         
24557         if(!this.mapTypeId){
24558             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24559         }
24560         
24561         this.gMapContext = this.GMapContext();
24562         
24563         this.initOverlayView();
24564         
24565         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24566         
24567         var _this = this;
24568                 
24569         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24570             _this.setPosition(_this.gMapContext.marker.position);
24571         });
24572         
24573         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24574             _this.fireEvent('mapClick', this, event);
24575             
24576         });
24577
24578         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24579             _this.fireEvent('mapRightClick', this, event);
24580             
24581         });
24582         
24583         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24584             _this.fireEvent('markerClick', this, event);
24585             
24586         });
24587
24588         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24589             _this.fireEvent('markerRightClick', this, event);
24590             
24591         });
24592         
24593         this.setPosition(this.gMapContext.location);
24594         
24595         this.fireEvent('initial', this, this.gMapContext.location);
24596     },
24597     
24598     initOverlayView: function()
24599     {
24600         var _this = this;
24601         
24602         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24603             
24604             draw: function()
24605             {
24606                 _this.fireEvent('OverlayViewDraw', _this);
24607             },
24608             
24609             onAdd: function()
24610             {
24611                 _this.fireEvent('OverlayViewOnAdd', _this);
24612             },
24613             
24614             onRemove: function()
24615             {
24616                 _this.fireEvent('OverlayViewOnRemove', _this);
24617             },
24618             
24619             show: function(cpx)
24620             {
24621                 _this.fireEvent('OverlayViewShow', _this, cpx);
24622             },
24623             
24624             hide: function()
24625             {
24626                 _this.fireEvent('OverlayViewHide', _this);
24627             }
24628             
24629         });
24630     },
24631     
24632     fromLatLngToContainerPixel: function(event)
24633     {
24634         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24635     },
24636     
24637     isApplied: function() 
24638     {
24639         return this.getGmapContext() == false ? false : true;
24640     },
24641     
24642     getGmapContext: function() 
24643     {
24644         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24645     },
24646     
24647     GMapContext: function() 
24648     {
24649         var position = new google.maps.LatLng(this.latitude, this.longitude);
24650         
24651         var _map = new google.maps.Map(this.el.dom, {
24652             center: position,
24653             zoom: this.zoom,
24654             mapTypeId: this.mapTypeId,
24655             mapTypeControl: this.mapTypeControl,
24656             disableDoubleClickZoom: this.disableDoubleClickZoom,
24657             scrollwheel: this.scrollwheel,
24658             streetViewControl: this.streetViewControl,
24659             locationName: this.locationName,
24660             draggable: this.draggable,
24661             enableAutocomplete: this.enableAutocomplete,
24662             enableReverseGeocode: this.enableReverseGeocode
24663         });
24664         
24665         var _marker = new google.maps.Marker({
24666             position: position,
24667             map: _map,
24668             title: this.markerTitle,
24669             draggable: this.draggable
24670         });
24671         
24672         return {
24673             map: _map,
24674             marker: _marker,
24675             circle: null,
24676             location: position,
24677             radius: this.radius,
24678             locationName: this.locationName,
24679             addressComponents: {
24680                 formatted_address: null,
24681                 addressLine1: null,
24682                 addressLine2: null,
24683                 streetName: null,
24684                 streetNumber: null,
24685                 city: null,
24686                 district: null,
24687                 state: null,
24688                 stateOrProvince: null
24689             },
24690             settings: this,
24691             domContainer: this.el.dom,
24692             geodecoder: new google.maps.Geocoder()
24693         };
24694     },
24695     
24696     drawCircle: function(center, radius, options) 
24697     {
24698         if (this.gMapContext.circle != null) {
24699             this.gMapContext.circle.setMap(null);
24700         }
24701         if (radius > 0) {
24702             radius *= 1;
24703             options = Roo.apply({}, options, {
24704                 strokeColor: "#0000FF",
24705                 strokeOpacity: .35,
24706                 strokeWeight: 2,
24707                 fillColor: "#0000FF",
24708                 fillOpacity: .2
24709             });
24710             
24711             options.map = this.gMapContext.map;
24712             options.radius = radius;
24713             options.center = center;
24714             this.gMapContext.circle = new google.maps.Circle(options);
24715             return this.gMapContext.circle;
24716         }
24717         
24718         return null;
24719     },
24720     
24721     setPosition: function(location) 
24722     {
24723         this.gMapContext.location = location;
24724         this.gMapContext.marker.setPosition(location);
24725         this.gMapContext.map.panTo(location);
24726         this.drawCircle(location, this.gMapContext.radius, {});
24727         
24728         var _this = this;
24729         
24730         if (this.gMapContext.settings.enableReverseGeocode) {
24731             this.gMapContext.geodecoder.geocode({
24732                 latLng: this.gMapContext.location
24733             }, function(results, status) {
24734                 
24735                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24736                     _this.gMapContext.locationName = results[0].formatted_address;
24737                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24738                     
24739                     _this.fireEvent('positionchanged', this, location);
24740                 }
24741             });
24742             
24743             return;
24744         }
24745         
24746         this.fireEvent('positionchanged', this, location);
24747     },
24748     
24749     resize: function()
24750     {
24751         google.maps.event.trigger(this.gMapContext.map, "resize");
24752         
24753         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24754         
24755         this.fireEvent('resize', this);
24756     },
24757     
24758     setPositionByLatLng: function(latitude, longitude)
24759     {
24760         this.setPosition(new google.maps.LatLng(latitude, longitude));
24761     },
24762     
24763     getCurrentPosition: function() 
24764     {
24765         return {
24766             latitude: this.gMapContext.location.lat(),
24767             longitude: this.gMapContext.location.lng()
24768         };
24769     },
24770     
24771     getAddressName: function() 
24772     {
24773         return this.gMapContext.locationName;
24774     },
24775     
24776     getAddressComponents: function() 
24777     {
24778         return this.gMapContext.addressComponents;
24779     },
24780     
24781     address_component_from_google_geocode: function(address_components) 
24782     {
24783         var result = {};
24784         
24785         for (var i = 0; i < address_components.length; i++) {
24786             var component = address_components[i];
24787             if (component.types.indexOf("postal_code") >= 0) {
24788                 result.postalCode = component.short_name;
24789             } else if (component.types.indexOf("street_number") >= 0) {
24790                 result.streetNumber = component.short_name;
24791             } else if (component.types.indexOf("route") >= 0) {
24792                 result.streetName = component.short_name;
24793             } else if (component.types.indexOf("neighborhood") >= 0) {
24794                 result.city = component.short_name;
24795             } else if (component.types.indexOf("locality") >= 0) {
24796                 result.city = component.short_name;
24797             } else if (component.types.indexOf("sublocality") >= 0) {
24798                 result.district = component.short_name;
24799             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24800                 result.stateOrProvince = component.short_name;
24801             } else if (component.types.indexOf("country") >= 0) {
24802                 result.country = component.short_name;
24803             }
24804         }
24805         
24806         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24807         result.addressLine2 = "";
24808         return result;
24809     },
24810     
24811     setZoomLevel: function(zoom)
24812     {
24813         this.gMapContext.map.setZoom(zoom);
24814     },
24815     
24816     show: function()
24817     {
24818         if(!this.el){
24819             return;
24820         }
24821         
24822         this.el.show();
24823         
24824         this.resize();
24825         
24826         this.fireEvent('show', this);
24827     },
24828     
24829     hide: function()
24830     {
24831         if(!this.el){
24832             return;
24833         }
24834         
24835         this.el.hide();
24836         
24837         this.fireEvent('hide', this);
24838     }
24839     
24840 });
24841
24842 Roo.apply(Roo.bootstrap.LocationPicker, {
24843     
24844     OverlayView : function(map, options)
24845     {
24846         options = options || {};
24847         
24848         this.setMap(map);
24849     }
24850     
24851     
24852 });/*
24853  * - LGPL
24854  *
24855  * Alert
24856  * 
24857  */
24858
24859 /**
24860  * @class Roo.bootstrap.Alert
24861  * @extends Roo.bootstrap.Component
24862  * Bootstrap Alert class
24863  * @cfg {String} title The title of alert
24864  * @cfg {String} html The content of alert
24865  * @cfg {String} weight (  success | info | warning | danger )
24866  * @cfg {String} faicon font-awesomeicon
24867  * 
24868  * @constructor
24869  * Create a new alert
24870  * @param {Object} config The config object
24871  */
24872
24873
24874 Roo.bootstrap.Alert = function(config){
24875     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24876     
24877 };
24878
24879 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24880     
24881     title: '',
24882     html: '',
24883     weight: false,
24884     faicon: false,
24885     
24886     getAutoCreate : function()
24887     {
24888         
24889         var cfg = {
24890             tag : 'div',
24891             cls : 'alert',
24892             cn : [
24893                 {
24894                     tag : 'i',
24895                     cls : 'roo-alert-icon'
24896                     
24897                 },
24898                 {
24899                     tag : 'b',
24900                     cls : 'roo-alert-title',
24901                     html : this.title
24902                 },
24903                 {
24904                     tag : 'span',
24905                     cls : 'roo-alert-text',
24906                     html : this.html
24907                 }
24908             ]
24909         };
24910         
24911         if(this.faicon){
24912             cfg.cn[0].cls += ' fa ' + this.faicon;
24913         }
24914         
24915         if(this.weight){
24916             cfg.cls += ' alert-' + this.weight;
24917         }
24918         
24919         return cfg;
24920     },
24921     
24922     initEvents: function() 
24923     {
24924         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24925     },
24926     
24927     setTitle : function(str)
24928     {
24929         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24930     },
24931     
24932     setText : function(str)
24933     {
24934         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24935     },
24936     
24937     setWeight : function(weight)
24938     {
24939         if(this.weight){
24940             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24941         }
24942         
24943         this.weight = weight;
24944         
24945         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24946     },
24947     
24948     setIcon : function(icon)
24949     {
24950         if(this.faicon){
24951             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24952         }
24953         
24954         this.faicon = icon;
24955         
24956         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24957     },
24958     
24959     hide: function() 
24960     {
24961         this.el.hide();   
24962     },
24963     
24964     show: function() 
24965     {  
24966         this.el.show();   
24967     }
24968     
24969 });
24970
24971  
24972 /*
24973 * Licence: LGPL
24974 */
24975
24976 /**
24977  * @class Roo.bootstrap.UploadCropbox
24978  * @extends Roo.bootstrap.Component
24979  * Bootstrap UploadCropbox class
24980  * @cfg {String} emptyText show when image has been loaded
24981  * @cfg {String} rotateNotify show when image too small to rotate
24982  * @cfg {Number} errorTimeout default 3000
24983  * @cfg {Number} minWidth default 300
24984  * @cfg {Number} minHeight default 300
24985  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24986  * @cfg {Boolean} isDocument (true|false) default false
24987  * @cfg {String} url action url
24988  * @cfg {String} paramName default 'imageUpload'
24989  * @cfg {String} method default POST
24990  * @cfg {Boolean} loadMask (true|false) default true
24991  * @cfg {Boolean} loadingText default 'Loading...'
24992  * 
24993  * @constructor
24994  * Create a new UploadCropbox
24995  * @param {Object} config The config object
24996  */
24997
24998 Roo.bootstrap.UploadCropbox = function(config){
24999     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25000     
25001     this.addEvents({
25002         /**
25003          * @event beforeselectfile
25004          * Fire before select file
25005          * @param {Roo.bootstrap.UploadCropbox} this
25006          */
25007         "beforeselectfile" : true,
25008         /**
25009          * @event initial
25010          * Fire after initEvent
25011          * @param {Roo.bootstrap.UploadCropbox} this
25012          */
25013         "initial" : true,
25014         /**
25015          * @event crop
25016          * Fire after initEvent
25017          * @param {Roo.bootstrap.UploadCropbox} this
25018          * @param {String} data
25019          */
25020         "crop" : true,
25021         /**
25022          * @event prepare
25023          * Fire when preparing the file data
25024          * @param {Roo.bootstrap.UploadCropbox} this
25025          * @param {Object} file
25026          */
25027         "prepare" : true,
25028         /**
25029          * @event exception
25030          * Fire when get exception
25031          * @param {Roo.bootstrap.UploadCropbox} this
25032          * @param {XMLHttpRequest} xhr
25033          */
25034         "exception" : true,
25035         /**
25036          * @event beforeloadcanvas
25037          * Fire before load the canvas
25038          * @param {Roo.bootstrap.UploadCropbox} this
25039          * @param {String} src
25040          */
25041         "beforeloadcanvas" : true,
25042         /**
25043          * @event trash
25044          * Fire when trash image
25045          * @param {Roo.bootstrap.UploadCropbox} this
25046          */
25047         "trash" : true,
25048         /**
25049          * @event download
25050          * Fire when download the image
25051          * @param {Roo.bootstrap.UploadCropbox} this
25052          */
25053         "download" : true,
25054         /**
25055          * @event footerbuttonclick
25056          * Fire when footerbuttonclick
25057          * @param {Roo.bootstrap.UploadCropbox} this
25058          * @param {String} type
25059          */
25060         "footerbuttonclick" : true,
25061         /**
25062          * @event resize
25063          * Fire when resize
25064          * @param {Roo.bootstrap.UploadCropbox} this
25065          */
25066         "resize" : true,
25067         /**
25068          * @event rotate
25069          * Fire when rotate the image
25070          * @param {Roo.bootstrap.UploadCropbox} this
25071          * @param {String} pos
25072          */
25073         "rotate" : true,
25074         /**
25075          * @event inspect
25076          * Fire when inspect the file
25077          * @param {Roo.bootstrap.UploadCropbox} this
25078          * @param {Object} file
25079          */
25080         "inspect" : true,
25081         /**
25082          * @event upload
25083          * Fire when xhr upload the file
25084          * @param {Roo.bootstrap.UploadCropbox} this
25085          * @param {Object} data
25086          */
25087         "upload" : true,
25088         /**
25089          * @event arrange
25090          * Fire when arrange the file data
25091          * @param {Roo.bootstrap.UploadCropbox} this
25092          * @param {Object} formData
25093          */
25094         "arrange" : true
25095     });
25096     
25097     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25098 };
25099
25100 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25101     
25102     emptyText : 'Click to upload image',
25103     rotateNotify : 'Image is too small to rotate',
25104     errorTimeout : 3000,
25105     scale : 0,
25106     baseScale : 1,
25107     rotate : 0,
25108     dragable : false,
25109     pinching : false,
25110     mouseX : 0,
25111     mouseY : 0,
25112     cropData : false,
25113     minWidth : 300,
25114     minHeight : 300,
25115     file : false,
25116     exif : {},
25117     baseRotate : 1,
25118     cropType : 'image/jpeg',
25119     buttons : false,
25120     canvasLoaded : false,
25121     isDocument : false,
25122     method : 'POST',
25123     paramName : 'imageUpload',
25124     loadMask : true,
25125     loadingText : 'Loading...',
25126     maskEl : false,
25127     
25128     getAutoCreate : function()
25129     {
25130         var cfg = {
25131             tag : 'div',
25132             cls : 'roo-upload-cropbox',
25133             cn : [
25134                 {
25135                     tag : 'input',
25136                     cls : 'roo-upload-cropbox-selector',
25137                     type : 'file'
25138                 },
25139                 {
25140                     tag : 'div',
25141                     cls : 'roo-upload-cropbox-body',
25142                     style : 'cursor:pointer',
25143                     cn : [
25144                         {
25145                             tag : 'div',
25146                             cls : 'roo-upload-cropbox-preview'
25147                         },
25148                         {
25149                             tag : 'div',
25150                             cls : 'roo-upload-cropbox-thumb'
25151                         },
25152                         {
25153                             tag : 'div',
25154                             cls : 'roo-upload-cropbox-empty-notify',
25155                             html : this.emptyText
25156                         },
25157                         {
25158                             tag : 'div',
25159                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25160                             html : this.rotateNotify
25161                         }
25162                     ]
25163                 },
25164                 {
25165                     tag : 'div',
25166                     cls : 'roo-upload-cropbox-footer',
25167                     cn : {
25168                         tag : 'div',
25169                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25170                         cn : []
25171                     }
25172                 }
25173             ]
25174         };
25175         
25176         return cfg;
25177     },
25178     
25179     onRender : function(ct, position)
25180     {
25181         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25182         
25183         if (this.buttons.length) {
25184             
25185             Roo.each(this.buttons, function(bb) {
25186                 
25187                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25188                 
25189                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25190                 
25191             }, this);
25192         }
25193         
25194         if(this.loadMask){
25195             this.maskEl = this.el;
25196         }
25197     },
25198     
25199     initEvents : function()
25200     {
25201         this.urlAPI = (window.createObjectURL && window) || 
25202                                 (window.URL && URL.revokeObjectURL && URL) || 
25203                                 (window.webkitURL && webkitURL);
25204                         
25205         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25206         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25207         
25208         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25209         this.selectorEl.hide();
25210         
25211         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25212         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25213         
25214         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25215         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25216         this.thumbEl.hide();
25217         
25218         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25219         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25220         
25221         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25222         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25223         this.errorEl.hide();
25224         
25225         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25226         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25227         this.footerEl.hide();
25228         
25229         this.setThumbBoxSize();
25230         
25231         this.bind();
25232         
25233         this.resize();
25234         
25235         this.fireEvent('initial', this);
25236     },
25237
25238     bind : function()
25239     {
25240         var _this = this;
25241         
25242         window.addEventListener("resize", function() { _this.resize(); } );
25243         
25244         this.bodyEl.on('click', this.beforeSelectFile, this);
25245         
25246         if(Roo.isTouch){
25247             this.bodyEl.on('touchstart', this.onTouchStart, this);
25248             this.bodyEl.on('touchmove', this.onTouchMove, this);
25249             this.bodyEl.on('touchend', this.onTouchEnd, this);
25250         }
25251         
25252         if(!Roo.isTouch){
25253             this.bodyEl.on('mousedown', this.onMouseDown, this);
25254             this.bodyEl.on('mousemove', this.onMouseMove, this);
25255             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25256             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25257             Roo.get(document).on('mouseup', this.onMouseUp, this);
25258         }
25259         
25260         this.selectorEl.on('change', this.onFileSelected, this);
25261     },
25262     
25263     reset : function()
25264     {    
25265         this.scale = 0;
25266         this.baseScale = 1;
25267         this.rotate = 0;
25268         this.baseRotate = 1;
25269         this.dragable = false;
25270         this.pinching = false;
25271         this.mouseX = 0;
25272         this.mouseY = 0;
25273         this.cropData = false;
25274         this.notifyEl.dom.innerHTML = this.emptyText;
25275         
25276         this.selectorEl.dom.value = '';
25277         
25278     },
25279     
25280     resize : function()
25281     {
25282         if(this.fireEvent('resize', this) != false){
25283             this.setThumbBoxPosition();
25284             this.setCanvasPosition();
25285         }
25286     },
25287     
25288     onFooterButtonClick : function(e, el, o, type)
25289     {
25290         switch (type) {
25291             case 'rotate-left' :
25292                 this.onRotateLeft(e);
25293                 break;
25294             case 'rotate-right' :
25295                 this.onRotateRight(e);
25296                 break;
25297             case 'picture' :
25298                 this.beforeSelectFile(e);
25299                 break;
25300             case 'trash' :
25301                 this.trash(e);
25302                 break;
25303             case 'crop' :
25304                 this.crop(e);
25305                 break;
25306             case 'download' :
25307                 this.download(e);
25308                 break;
25309             default :
25310                 break;
25311         }
25312         
25313         this.fireEvent('footerbuttonclick', this, type);
25314     },
25315     
25316     beforeSelectFile : function(e)
25317     {
25318         e.preventDefault();
25319         
25320         if(this.fireEvent('beforeselectfile', this) != false){
25321             this.selectorEl.dom.click();
25322         }
25323     },
25324     
25325     onFileSelected : function(e)
25326     {
25327         e.preventDefault();
25328         
25329         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25330             return;
25331         }
25332         
25333         var file = this.selectorEl.dom.files[0];
25334         
25335         if(this.fireEvent('inspect', this, file) != false){
25336             this.prepare(file);
25337         }
25338         
25339     },
25340     
25341     trash : function(e)
25342     {
25343         this.fireEvent('trash', this);
25344     },
25345     
25346     download : function(e)
25347     {
25348         this.fireEvent('download', this);
25349     },
25350     
25351     loadCanvas : function(src)
25352     {   
25353         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25354             
25355             this.reset();
25356             
25357             this.imageEl = document.createElement('img');
25358             
25359             var _this = this;
25360             
25361             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25362             
25363             this.imageEl.src = src;
25364         }
25365     },
25366     
25367     onLoadCanvas : function()
25368     {   
25369         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25370         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25371         
25372         this.bodyEl.un('click', this.beforeSelectFile, this);
25373         
25374         this.notifyEl.hide();
25375         this.thumbEl.show();
25376         this.footerEl.show();
25377         
25378         this.baseRotateLevel();
25379         
25380         if(this.isDocument){
25381             this.setThumbBoxSize();
25382         }
25383         
25384         this.setThumbBoxPosition();
25385         
25386         this.baseScaleLevel();
25387         
25388         this.draw();
25389         
25390         this.resize();
25391         
25392         this.canvasLoaded = true;
25393         
25394         if(this.loadMask){
25395             this.maskEl.unmask();
25396         }
25397         
25398     },
25399     
25400     setCanvasPosition : function()
25401     {   
25402         if(!this.canvasEl){
25403             return;
25404         }
25405         
25406         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25407         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25408         
25409         this.previewEl.setLeft(pw);
25410         this.previewEl.setTop(ph);
25411         
25412     },
25413     
25414     onMouseDown : function(e)
25415     {   
25416         e.stopEvent();
25417         
25418         this.dragable = true;
25419         this.pinching = false;
25420         
25421         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25422             this.dragable = false;
25423             return;
25424         }
25425         
25426         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25427         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25428         
25429     },
25430     
25431     onMouseMove : function(e)
25432     {   
25433         e.stopEvent();
25434         
25435         if(!this.canvasLoaded){
25436             return;
25437         }
25438         
25439         if (!this.dragable){
25440             return;
25441         }
25442         
25443         var minX = Math.ceil(this.thumbEl.getLeft(true));
25444         var minY = Math.ceil(this.thumbEl.getTop(true));
25445         
25446         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25447         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25448         
25449         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25450         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25451         
25452         x = x - this.mouseX;
25453         y = y - this.mouseY;
25454         
25455         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25456         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25457         
25458         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25459         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25460         
25461         this.previewEl.setLeft(bgX);
25462         this.previewEl.setTop(bgY);
25463         
25464         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25465         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25466     },
25467     
25468     onMouseUp : function(e)
25469     {   
25470         e.stopEvent();
25471         
25472         this.dragable = false;
25473     },
25474     
25475     onMouseWheel : function(e)
25476     {   
25477         e.stopEvent();
25478         
25479         this.startScale = this.scale;
25480         
25481         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25482         
25483         if(!this.zoomable()){
25484             this.scale = this.startScale;
25485             return;
25486         }
25487         
25488         this.draw();
25489         
25490         return;
25491     },
25492     
25493     zoomable : function()
25494     {
25495         var minScale = this.thumbEl.getWidth() / this.minWidth;
25496         
25497         if(this.minWidth < this.minHeight){
25498             minScale = this.thumbEl.getHeight() / this.minHeight;
25499         }
25500         
25501         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25502         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25503         
25504         if(
25505                 this.isDocument &&
25506                 (this.rotate == 0 || this.rotate == 180) && 
25507                 (
25508                     width > this.imageEl.OriginWidth || 
25509                     height > this.imageEl.OriginHeight ||
25510                     (width < this.minWidth && height < this.minHeight)
25511                 )
25512         ){
25513             return false;
25514         }
25515         
25516         if(
25517                 this.isDocument &&
25518                 (this.rotate == 90 || this.rotate == 270) && 
25519                 (
25520                     width > this.imageEl.OriginWidth || 
25521                     height > this.imageEl.OriginHeight ||
25522                     (width < this.minHeight && height < this.minWidth)
25523                 )
25524         ){
25525             return false;
25526         }
25527         
25528         if(
25529                 !this.isDocument &&
25530                 (this.rotate == 0 || this.rotate == 180) && 
25531                 (
25532                     width < this.minWidth || 
25533                     width > this.imageEl.OriginWidth || 
25534                     height < this.minHeight || 
25535                     height > this.imageEl.OriginHeight
25536                 )
25537         ){
25538             return false;
25539         }
25540         
25541         if(
25542                 !this.isDocument &&
25543                 (this.rotate == 90 || this.rotate == 270) && 
25544                 (
25545                     width < this.minHeight || 
25546                     width > this.imageEl.OriginWidth || 
25547                     height < this.minWidth || 
25548                     height > this.imageEl.OriginHeight
25549                 )
25550         ){
25551             return false;
25552         }
25553         
25554         return true;
25555         
25556     },
25557     
25558     onRotateLeft : function(e)
25559     {   
25560         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25561             
25562             var minScale = this.thumbEl.getWidth() / this.minWidth;
25563             
25564             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25565             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25566             
25567             this.startScale = this.scale;
25568             
25569             while (this.getScaleLevel() < minScale){
25570             
25571                 this.scale = this.scale + 1;
25572                 
25573                 if(!this.zoomable()){
25574                     break;
25575                 }
25576                 
25577                 if(
25578                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25579                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25580                 ){
25581                     continue;
25582                 }
25583                 
25584                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25585
25586                 this.draw();
25587                 
25588                 return;
25589             }
25590             
25591             this.scale = this.startScale;
25592             
25593             this.onRotateFail();
25594             
25595             return false;
25596         }
25597         
25598         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25599
25600         if(this.isDocument){
25601             this.setThumbBoxSize();
25602             this.setThumbBoxPosition();
25603             this.setCanvasPosition();
25604         }
25605         
25606         this.draw();
25607         
25608         this.fireEvent('rotate', this, 'left');
25609         
25610     },
25611     
25612     onRotateRight : function(e)
25613     {
25614         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25615             
25616             var minScale = this.thumbEl.getWidth() / this.minWidth;
25617         
25618             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25619             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25620             
25621             this.startScale = this.scale;
25622             
25623             while (this.getScaleLevel() < minScale){
25624             
25625                 this.scale = this.scale + 1;
25626                 
25627                 if(!this.zoomable()){
25628                     break;
25629                 }
25630                 
25631                 if(
25632                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25633                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25634                 ){
25635                     continue;
25636                 }
25637                 
25638                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25639
25640                 this.draw();
25641                 
25642                 return;
25643             }
25644             
25645             this.scale = this.startScale;
25646             
25647             this.onRotateFail();
25648             
25649             return false;
25650         }
25651         
25652         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25653
25654         if(this.isDocument){
25655             this.setThumbBoxSize();
25656             this.setThumbBoxPosition();
25657             this.setCanvasPosition();
25658         }
25659         
25660         this.draw();
25661         
25662         this.fireEvent('rotate', this, 'right');
25663     },
25664     
25665     onRotateFail : function()
25666     {
25667         this.errorEl.show(true);
25668         
25669         var _this = this;
25670         
25671         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25672     },
25673     
25674     draw : function()
25675     {
25676         this.previewEl.dom.innerHTML = '';
25677         
25678         var canvasEl = document.createElement("canvas");
25679         
25680         var contextEl = canvasEl.getContext("2d");
25681         
25682         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25683         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25684         var center = this.imageEl.OriginWidth / 2;
25685         
25686         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25687             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25688             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25689             center = this.imageEl.OriginHeight / 2;
25690         }
25691         
25692         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25693         
25694         contextEl.translate(center, center);
25695         contextEl.rotate(this.rotate * Math.PI / 180);
25696
25697         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25698         
25699         this.canvasEl = document.createElement("canvas");
25700         
25701         this.contextEl = this.canvasEl.getContext("2d");
25702         
25703         switch (this.rotate) {
25704             case 0 :
25705                 
25706                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25707                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25708                 
25709                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25710                 
25711                 break;
25712             case 90 : 
25713                 
25714                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25715                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25716                 
25717                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25718                     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);
25719                     break;
25720                 }
25721                 
25722                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25723                 
25724                 break;
25725             case 180 :
25726                 
25727                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25728                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25729                 
25730                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25731                     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);
25732                     break;
25733                 }
25734                 
25735                 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);
25736                 
25737                 break;
25738             case 270 :
25739                 
25740                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25741                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25742         
25743                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25744                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25745                     break;
25746                 }
25747                 
25748                 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);
25749                 
25750                 break;
25751             default : 
25752                 break;
25753         }
25754         
25755         this.previewEl.appendChild(this.canvasEl);
25756         
25757         this.setCanvasPosition();
25758     },
25759     
25760     crop : function()
25761     {
25762         if(!this.canvasLoaded){
25763             return;
25764         }
25765         
25766         var imageCanvas = document.createElement("canvas");
25767         
25768         var imageContext = imageCanvas.getContext("2d");
25769         
25770         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25771         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25772         
25773         var center = imageCanvas.width / 2;
25774         
25775         imageContext.translate(center, center);
25776         
25777         imageContext.rotate(this.rotate * Math.PI / 180);
25778         
25779         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25780         
25781         var canvas = document.createElement("canvas");
25782         
25783         var context = canvas.getContext("2d");
25784                 
25785         canvas.width = this.minWidth;
25786         canvas.height = this.minHeight;
25787
25788         switch (this.rotate) {
25789             case 0 :
25790                 
25791                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25792                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25793                 
25794                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25795                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25796                 
25797                 var targetWidth = this.minWidth - 2 * x;
25798                 var targetHeight = this.minHeight - 2 * y;
25799                 
25800                 var scale = 1;
25801                 
25802                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25803                     scale = targetWidth / width;
25804                 }
25805                 
25806                 if(x > 0 && y == 0){
25807                     scale = targetHeight / height;
25808                 }
25809                 
25810                 if(x > 0 && y > 0){
25811                     scale = targetWidth / width;
25812                     
25813                     if(width < height){
25814                         scale = targetHeight / height;
25815                     }
25816                 }
25817                 
25818                 context.scale(scale, scale);
25819                 
25820                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25821                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25822
25823                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25824                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25825
25826                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25827                 
25828                 break;
25829             case 90 : 
25830                 
25831                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25832                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25833                 
25834                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25835                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25836                 
25837                 var targetWidth = this.minWidth - 2 * x;
25838                 var targetHeight = this.minHeight - 2 * y;
25839                 
25840                 var scale = 1;
25841                 
25842                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25843                     scale = targetWidth / width;
25844                 }
25845                 
25846                 if(x > 0 && y == 0){
25847                     scale = targetHeight / height;
25848                 }
25849                 
25850                 if(x > 0 && y > 0){
25851                     scale = targetWidth / width;
25852                     
25853                     if(width < height){
25854                         scale = targetHeight / height;
25855                     }
25856                 }
25857                 
25858                 context.scale(scale, scale);
25859                 
25860                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25861                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25862
25863                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25864                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25865                 
25866                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25867                 
25868                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25869                 
25870                 break;
25871             case 180 :
25872                 
25873                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25874                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25875                 
25876                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25877                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25878                 
25879                 var targetWidth = this.minWidth - 2 * x;
25880                 var targetHeight = this.minHeight - 2 * y;
25881                 
25882                 var scale = 1;
25883                 
25884                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25885                     scale = targetWidth / width;
25886                 }
25887                 
25888                 if(x > 0 && y == 0){
25889                     scale = targetHeight / height;
25890                 }
25891                 
25892                 if(x > 0 && y > 0){
25893                     scale = targetWidth / width;
25894                     
25895                     if(width < height){
25896                         scale = targetHeight / height;
25897                     }
25898                 }
25899                 
25900                 context.scale(scale, scale);
25901                 
25902                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25903                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25904
25905                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25906                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25907
25908                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25909                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25910                 
25911                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25912                 
25913                 break;
25914             case 270 :
25915                 
25916                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25917                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25918                 
25919                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25920                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25921                 
25922                 var targetWidth = this.minWidth - 2 * x;
25923                 var targetHeight = this.minHeight - 2 * y;
25924                 
25925                 var scale = 1;
25926                 
25927                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25928                     scale = targetWidth / width;
25929                 }
25930                 
25931                 if(x > 0 && y == 0){
25932                     scale = targetHeight / height;
25933                 }
25934                 
25935                 if(x > 0 && y > 0){
25936                     scale = targetWidth / width;
25937                     
25938                     if(width < height){
25939                         scale = targetHeight / height;
25940                     }
25941                 }
25942                 
25943                 context.scale(scale, scale);
25944                 
25945                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25946                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25947
25948                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25949                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25950                 
25951                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25952                 
25953                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25954                 
25955                 break;
25956             default : 
25957                 break;
25958         }
25959         
25960         this.cropData = canvas.toDataURL(this.cropType);
25961         
25962         if(this.fireEvent('crop', this, this.cropData) !== false){
25963             this.process(this.file, this.cropData);
25964         }
25965         
25966         return;
25967         
25968     },
25969     
25970     setThumbBoxSize : function()
25971     {
25972         var width, height;
25973         
25974         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25975             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25976             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25977             
25978             this.minWidth = width;
25979             this.minHeight = height;
25980             
25981             if(this.rotate == 90 || this.rotate == 270){
25982                 this.minWidth = height;
25983                 this.minHeight = width;
25984             }
25985         }
25986         
25987         height = 300;
25988         width = Math.ceil(this.minWidth * height / this.minHeight);
25989         
25990         if(this.minWidth > this.minHeight){
25991             width = 300;
25992             height = Math.ceil(this.minHeight * width / this.minWidth);
25993         }
25994         
25995         this.thumbEl.setStyle({
25996             width : width + 'px',
25997             height : height + 'px'
25998         });
25999
26000         return;
26001             
26002     },
26003     
26004     setThumbBoxPosition : function()
26005     {
26006         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26007         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26008         
26009         this.thumbEl.setLeft(x);
26010         this.thumbEl.setTop(y);
26011         
26012     },
26013     
26014     baseRotateLevel : function()
26015     {
26016         this.baseRotate = 1;
26017         
26018         if(
26019                 typeof(this.exif) != 'undefined' &&
26020                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26021                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26022         ){
26023             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26024         }
26025         
26026         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26027         
26028     },
26029     
26030     baseScaleLevel : function()
26031     {
26032         var width, height;
26033         
26034         if(this.isDocument){
26035             
26036             if(this.baseRotate == 6 || this.baseRotate == 8){
26037             
26038                 height = this.thumbEl.getHeight();
26039                 this.baseScale = height / this.imageEl.OriginWidth;
26040
26041                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26042                     width = this.thumbEl.getWidth();
26043                     this.baseScale = width / this.imageEl.OriginHeight;
26044                 }
26045
26046                 return;
26047             }
26048
26049             height = this.thumbEl.getHeight();
26050             this.baseScale = height / this.imageEl.OriginHeight;
26051
26052             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26053                 width = this.thumbEl.getWidth();
26054                 this.baseScale = width / this.imageEl.OriginWidth;
26055             }
26056
26057             return;
26058         }
26059         
26060         if(this.baseRotate == 6 || this.baseRotate == 8){
26061             
26062             width = this.thumbEl.getHeight();
26063             this.baseScale = width / this.imageEl.OriginHeight;
26064             
26065             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26066                 height = this.thumbEl.getWidth();
26067                 this.baseScale = height / this.imageEl.OriginHeight;
26068             }
26069             
26070             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26071                 height = this.thumbEl.getWidth();
26072                 this.baseScale = height / this.imageEl.OriginHeight;
26073                 
26074                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26075                     width = this.thumbEl.getHeight();
26076                     this.baseScale = width / this.imageEl.OriginWidth;
26077                 }
26078             }
26079             
26080             return;
26081         }
26082         
26083         width = this.thumbEl.getWidth();
26084         this.baseScale = width / this.imageEl.OriginWidth;
26085         
26086         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26087             height = this.thumbEl.getHeight();
26088             this.baseScale = height / this.imageEl.OriginHeight;
26089         }
26090         
26091         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26092             
26093             height = this.thumbEl.getHeight();
26094             this.baseScale = height / this.imageEl.OriginHeight;
26095             
26096             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26097                 width = this.thumbEl.getWidth();
26098                 this.baseScale = width / this.imageEl.OriginWidth;
26099             }
26100             
26101         }
26102         
26103         return;
26104     },
26105     
26106     getScaleLevel : function()
26107     {
26108         return this.baseScale * Math.pow(1.1, this.scale);
26109     },
26110     
26111     onTouchStart : function(e)
26112     {
26113         if(!this.canvasLoaded){
26114             this.beforeSelectFile(e);
26115             return;
26116         }
26117         
26118         var touches = e.browserEvent.touches;
26119         
26120         if(!touches){
26121             return;
26122         }
26123         
26124         if(touches.length == 1){
26125             this.onMouseDown(e);
26126             return;
26127         }
26128         
26129         if(touches.length != 2){
26130             return;
26131         }
26132         
26133         var coords = [];
26134         
26135         for(var i = 0, finger; finger = touches[i]; i++){
26136             coords.push(finger.pageX, finger.pageY);
26137         }
26138         
26139         var x = Math.pow(coords[0] - coords[2], 2);
26140         var y = Math.pow(coords[1] - coords[3], 2);
26141         
26142         this.startDistance = Math.sqrt(x + y);
26143         
26144         this.startScale = this.scale;
26145         
26146         this.pinching = true;
26147         this.dragable = false;
26148         
26149     },
26150     
26151     onTouchMove : function(e)
26152     {
26153         if(!this.pinching && !this.dragable){
26154             return;
26155         }
26156         
26157         var touches = e.browserEvent.touches;
26158         
26159         if(!touches){
26160             return;
26161         }
26162         
26163         if(this.dragable){
26164             this.onMouseMove(e);
26165             return;
26166         }
26167         
26168         var coords = [];
26169         
26170         for(var i = 0, finger; finger = touches[i]; i++){
26171             coords.push(finger.pageX, finger.pageY);
26172         }
26173         
26174         var x = Math.pow(coords[0] - coords[2], 2);
26175         var y = Math.pow(coords[1] - coords[3], 2);
26176         
26177         this.endDistance = Math.sqrt(x + y);
26178         
26179         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26180         
26181         if(!this.zoomable()){
26182             this.scale = this.startScale;
26183             return;
26184         }
26185         
26186         this.draw();
26187         
26188     },
26189     
26190     onTouchEnd : function(e)
26191     {
26192         this.pinching = false;
26193         this.dragable = false;
26194         
26195     },
26196     
26197     process : function(file, crop)
26198     {
26199         if(this.loadMask){
26200             this.maskEl.mask(this.loadingText);
26201         }
26202         
26203         this.xhr = new XMLHttpRequest();
26204         
26205         file.xhr = this.xhr;
26206
26207         this.xhr.open(this.method, this.url, true);
26208         
26209         var headers = {
26210             "Accept": "application/json",
26211             "Cache-Control": "no-cache",
26212             "X-Requested-With": "XMLHttpRequest"
26213         };
26214         
26215         for (var headerName in headers) {
26216             var headerValue = headers[headerName];
26217             if (headerValue) {
26218                 this.xhr.setRequestHeader(headerName, headerValue);
26219             }
26220         }
26221         
26222         var _this = this;
26223         
26224         this.xhr.onload = function()
26225         {
26226             _this.xhrOnLoad(_this.xhr);
26227         }
26228         
26229         this.xhr.onerror = function()
26230         {
26231             _this.xhrOnError(_this.xhr);
26232         }
26233         
26234         var formData = new FormData();
26235
26236         formData.append('returnHTML', 'NO');
26237         
26238         if(crop){
26239             formData.append('crop', crop);
26240         }
26241         
26242         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26243             formData.append(this.paramName, file, file.name);
26244         }
26245         
26246         if(typeof(file.filename) != 'undefined'){
26247             formData.append('filename', file.filename);
26248         }
26249         
26250         if(typeof(file.mimetype) != 'undefined'){
26251             formData.append('mimetype', file.mimetype);
26252         }
26253         
26254         if(this.fireEvent('arrange', this, formData) != false){
26255             this.xhr.send(formData);
26256         };
26257     },
26258     
26259     xhrOnLoad : function(xhr)
26260     {
26261         if(this.loadMask){
26262             this.maskEl.unmask();
26263         }
26264         
26265         if (xhr.readyState !== 4) {
26266             this.fireEvent('exception', this, xhr);
26267             return;
26268         }
26269
26270         var response = Roo.decode(xhr.responseText);
26271         
26272         if(!response.success){
26273             this.fireEvent('exception', this, xhr);
26274             return;
26275         }
26276         
26277         var response = Roo.decode(xhr.responseText);
26278         
26279         this.fireEvent('upload', this, response);
26280         
26281     },
26282     
26283     xhrOnError : function()
26284     {
26285         if(this.loadMask){
26286             this.maskEl.unmask();
26287         }
26288         
26289         Roo.log('xhr on error');
26290         
26291         var response = Roo.decode(xhr.responseText);
26292           
26293         Roo.log(response);
26294         
26295     },
26296     
26297     prepare : function(file)
26298     {   
26299         if(this.loadMask){
26300             this.maskEl.mask(this.loadingText);
26301         }
26302         
26303         this.file = false;
26304         this.exif = {};
26305         
26306         if(typeof(file) === 'string'){
26307             this.loadCanvas(file);
26308             return;
26309         }
26310         
26311         if(!file || !this.urlAPI){
26312             return;
26313         }
26314         
26315         this.file = file;
26316         this.cropType = file.type;
26317         
26318         var _this = this;
26319         
26320         if(this.fireEvent('prepare', this, this.file) != false){
26321             
26322             var reader = new FileReader();
26323             
26324             reader.onload = function (e) {
26325                 if (e.target.error) {
26326                     Roo.log(e.target.error);
26327                     return;
26328                 }
26329                 
26330                 var buffer = e.target.result,
26331                     dataView = new DataView(buffer),
26332                     offset = 2,
26333                     maxOffset = dataView.byteLength - 4,
26334                     markerBytes,
26335                     markerLength;
26336                 
26337                 if (dataView.getUint16(0) === 0xffd8) {
26338                     while (offset < maxOffset) {
26339                         markerBytes = dataView.getUint16(offset);
26340                         
26341                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26342                             markerLength = dataView.getUint16(offset + 2) + 2;
26343                             if (offset + markerLength > dataView.byteLength) {
26344                                 Roo.log('Invalid meta data: Invalid segment size.');
26345                                 break;
26346                             }
26347                             
26348                             if(markerBytes == 0xffe1){
26349                                 _this.parseExifData(
26350                                     dataView,
26351                                     offset,
26352                                     markerLength
26353                                 );
26354                             }
26355                             
26356                             offset += markerLength;
26357                             
26358                             continue;
26359                         }
26360                         
26361                         break;
26362                     }
26363                     
26364                 }
26365                 
26366                 var url = _this.urlAPI.createObjectURL(_this.file);
26367                 
26368                 _this.loadCanvas(url);
26369                 
26370                 return;
26371             }
26372             
26373             reader.readAsArrayBuffer(this.file);
26374             
26375         }
26376         
26377     },
26378     
26379     parseExifData : function(dataView, offset, length)
26380     {
26381         var tiffOffset = offset + 10,
26382             littleEndian,
26383             dirOffset;
26384     
26385         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26386             // No Exif data, might be XMP data instead
26387             return;
26388         }
26389         
26390         // Check for the ASCII code for "Exif" (0x45786966):
26391         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26392             // No Exif data, might be XMP data instead
26393             return;
26394         }
26395         if (tiffOffset + 8 > dataView.byteLength) {
26396             Roo.log('Invalid Exif data: Invalid segment size.');
26397             return;
26398         }
26399         // Check for the two null bytes:
26400         if (dataView.getUint16(offset + 8) !== 0x0000) {
26401             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26402             return;
26403         }
26404         // Check the byte alignment:
26405         switch (dataView.getUint16(tiffOffset)) {
26406         case 0x4949:
26407             littleEndian = true;
26408             break;
26409         case 0x4D4D:
26410             littleEndian = false;
26411             break;
26412         default:
26413             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26414             return;
26415         }
26416         // Check for the TIFF tag marker (0x002A):
26417         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26418             Roo.log('Invalid Exif data: Missing TIFF marker.');
26419             return;
26420         }
26421         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26422         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26423         
26424         this.parseExifTags(
26425             dataView,
26426             tiffOffset,
26427             tiffOffset + dirOffset,
26428             littleEndian
26429         );
26430     },
26431     
26432     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26433     {
26434         var tagsNumber,
26435             dirEndOffset,
26436             i;
26437         if (dirOffset + 6 > dataView.byteLength) {
26438             Roo.log('Invalid Exif data: Invalid directory offset.');
26439             return;
26440         }
26441         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26442         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26443         if (dirEndOffset + 4 > dataView.byteLength) {
26444             Roo.log('Invalid Exif data: Invalid directory size.');
26445             return;
26446         }
26447         for (i = 0; i < tagsNumber; i += 1) {
26448             this.parseExifTag(
26449                 dataView,
26450                 tiffOffset,
26451                 dirOffset + 2 + 12 * i, // tag offset
26452                 littleEndian
26453             );
26454         }
26455         // Return the offset to the next directory:
26456         return dataView.getUint32(dirEndOffset, littleEndian);
26457     },
26458     
26459     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26460     {
26461         var tag = dataView.getUint16(offset, littleEndian);
26462         
26463         this.exif[tag] = this.getExifValue(
26464             dataView,
26465             tiffOffset,
26466             offset,
26467             dataView.getUint16(offset + 2, littleEndian), // tag type
26468             dataView.getUint32(offset + 4, littleEndian), // tag length
26469             littleEndian
26470         );
26471     },
26472     
26473     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26474     {
26475         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26476             tagSize,
26477             dataOffset,
26478             values,
26479             i,
26480             str,
26481             c;
26482     
26483         if (!tagType) {
26484             Roo.log('Invalid Exif data: Invalid tag type.');
26485             return;
26486         }
26487         
26488         tagSize = tagType.size * length;
26489         // Determine if the value is contained in the dataOffset bytes,
26490         // or if the value at the dataOffset is a pointer to the actual data:
26491         dataOffset = tagSize > 4 ?
26492                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26493         if (dataOffset + tagSize > dataView.byteLength) {
26494             Roo.log('Invalid Exif data: Invalid data offset.');
26495             return;
26496         }
26497         if (length === 1) {
26498             return tagType.getValue(dataView, dataOffset, littleEndian);
26499         }
26500         values = [];
26501         for (i = 0; i < length; i += 1) {
26502             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26503         }
26504         
26505         if (tagType.ascii) {
26506             str = '';
26507             // Concatenate the chars:
26508             for (i = 0; i < values.length; i += 1) {
26509                 c = values[i];
26510                 // Ignore the terminating NULL byte(s):
26511                 if (c === '\u0000') {
26512                     break;
26513                 }
26514                 str += c;
26515             }
26516             return str;
26517         }
26518         return values;
26519     }
26520     
26521 });
26522
26523 Roo.apply(Roo.bootstrap.UploadCropbox, {
26524     tags : {
26525         'Orientation': 0x0112
26526     },
26527     
26528     Orientation: {
26529             1: 0, //'top-left',
26530 //            2: 'top-right',
26531             3: 180, //'bottom-right',
26532 //            4: 'bottom-left',
26533 //            5: 'left-top',
26534             6: 90, //'right-top',
26535 //            7: 'right-bottom',
26536             8: 270 //'left-bottom'
26537     },
26538     
26539     exifTagTypes : {
26540         // byte, 8-bit unsigned int:
26541         1: {
26542             getValue: function (dataView, dataOffset) {
26543                 return dataView.getUint8(dataOffset);
26544             },
26545             size: 1
26546         },
26547         // ascii, 8-bit byte:
26548         2: {
26549             getValue: function (dataView, dataOffset) {
26550                 return String.fromCharCode(dataView.getUint8(dataOffset));
26551             },
26552             size: 1,
26553             ascii: true
26554         },
26555         // short, 16 bit int:
26556         3: {
26557             getValue: function (dataView, dataOffset, littleEndian) {
26558                 return dataView.getUint16(dataOffset, littleEndian);
26559             },
26560             size: 2
26561         },
26562         // long, 32 bit int:
26563         4: {
26564             getValue: function (dataView, dataOffset, littleEndian) {
26565                 return dataView.getUint32(dataOffset, littleEndian);
26566             },
26567             size: 4
26568         },
26569         // rational = two long values, first is numerator, second is denominator:
26570         5: {
26571             getValue: function (dataView, dataOffset, littleEndian) {
26572                 return dataView.getUint32(dataOffset, littleEndian) /
26573                     dataView.getUint32(dataOffset + 4, littleEndian);
26574             },
26575             size: 8
26576         },
26577         // slong, 32 bit signed int:
26578         9: {
26579             getValue: function (dataView, dataOffset, littleEndian) {
26580                 return dataView.getInt32(dataOffset, littleEndian);
26581             },
26582             size: 4
26583         },
26584         // srational, two slongs, first is numerator, second is denominator:
26585         10: {
26586             getValue: function (dataView, dataOffset, littleEndian) {
26587                 return dataView.getInt32(dataOffset, littleEndian) /
26588                     dataView.getInt32(dataOffset + 4, littleEndian);
26589             },
26590             size: 8
26591         }
26592     },
26593     
26594     footer : {
26595         STANDARD : [
26596             {
26597                 tag : 'div',
26598                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26599                 action : 'rotate-left',
26600                 cn : [
26601                     {
26602                         tag : 'button',
26603                         cls : 'btn btn-default',
26604                         html : '<i class="fa fa-undo"></i>'
26605                     }
26606                 ]
26607             },
26608             {
26609                 tag : 'div',
26610                 cls : 'btn-group roo-upload-cropbox-picture',
26611                 action : 'picture',
26612                 cn : [
26613                     {
26614                         tag : 'button',
26615                         cls : 'btn btn-default',
26616                         html : '<i class="fa fa-picture-o"></i>'
26617                     }
26618                 ]
26619             },
26620             {
26621                 tag : 'div',
26622                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26623                 action : 'rotate-right',
26624                 cn : [
26625                     {
26626                         tag : 'button',
26627                         cls : 'btn btn-default',
26628                         html : '<i class="fa fa-repeat"></i>'
26629                     }
26630                 ]
26631             }
26632         ],
26633         DOCUMENT : [
26634             {
26635                 tag : 'div',
26636                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26637                 action : 'rotate-left',
26638                 cn : [
26639                     {
26640                         tag : 'button',
26641                         cls : 'btn btn-default',
26642                         html : '<i class="fa fa-undo"></i>'
26643                     }
26644                 ]
26645             },
26646             {
26647                 tag : 'div',
26648                 cls : 'btn-group roo-upload-cropbox-download',
26649                 action : 'download',
26650                 cn : [
26651                     {
26652                         tag : 'button',
26653                         cls : 'btn btn-default',
26654                         html : '<i class="fa fa-download"></i>'
26655                     }
26656                 ]
26657             },
26658             {
26659                 tag : 'div',
26660                 cls : 'btn-group roo-upload-cropbox-crop',
26661                 action : 'crop',
26662                 cn : [
26663                     {
26664                         tag : 'button',
26665                         cls : 'btn btn-default',
26666                         html : '<i class="fa fa-crop"></i>'
26667                     }
26668                 ]
26669             },
26670             {
26671                 tag : 'div',
26672                 cls : 'btn-group roo-upload-cropbox-trash',
26673                 action : 'trash',
26674                 cn : [
26675                     {
26676                         tag : 'button',
26677                         cls : 'btn btn-default',
26678                         html : '<i class="fa fa-trash"></i>'
26679                     }
26680                 ]
26681             },
26682             {
26683                 tag : 'div',
26684                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26685                 action : 'rotate-right',
26686                 cn : [
26687                     {
26688                         tag : 'button',
26689                         cls : 'btn btn-default',
26690                         html : '<i class="fa fa-repeat"></i>'
26691                     }
26692                 ]
26693             }
26694         ],
26695         ROTATOR : [
26696             {
26697                 tag : 'div',
26698                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26699                 action : 'rotate-left',
26700                 cn : [
26701                     {
26702                         tag : 'button',
26703                         cls : 'btn btn-default',
26704                         html : '<i class="fa fa-undo"></i>'
26705                     }
26706                 ]
26707             },
26708             {
26709                 tag : 'div',
26710                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26711                 action : 'rotate-right',
26712                 cn : [
26713                     {
26714                         tag : 'button',
26715                         cls : 'btn btn-default',
26716                         html : '<i class="fa fa-repeat"></i>'
26717                     }
26718                 ]
26719             }
26720         ]
26721     }
26722 });
26723
26724 /*
26725 * Licence: LGPL
26726 */
26727
26728 /**
26729  * @class Roo.bootstrap.DocumentManager
26730  * @extends Roo.bootstrap.Component
26731  * Bootstrap DocumentManager class
26732  * @cfg {String} paramName default 'imageUpload'
26733  * @cfg {String} method default POST
26734  * @cfg {String} url action url
26735  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26736  * @cfg {Boolean} multiple multiple upload default true
26737  * @cfg {Number} thumbSize default 300
26738  * @cfg {String} fieldLabel
26739  * @cfg {Number} labelWidth default 4
26740  * @cfg {String} labelAlign (left|top) default left
26741  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26742  * 
26743  * @constructor
26744  * Create a new DocumentManager
26745  * @param {Object} config The config object
26746  */
26747
26748 Roo.bootstrap.DocumentManager = function(config){
26749     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26750     
26751     this.addEvents({
26752         /**
26753          * @event initial
26754          * Fire when initial the DocumentManager
26755          * @param {Roo.bootstrap.DocumentManager} this
26756          */
26757         "initial" : true,
26758         /**
26759          * @event inspect
26760          * inspect selected file
26761          * @param {Roo.bootstrap.DocumentManager} this
26762          * @param {File} file
26763          */
26764         "inspect" : true,
26765         /**
26766          * @event exception
26767          * Fire when xhr load exception
26768          * @param {Roo.bootstrap.DocumentManager} this
26769          * @param {XMLHttpRequest} xhr
26770          */
26771         "exception" : true,
26772         /**
26773          * @event prepare
26774          * prepare the form data
26775          * @param {Roo.bootstrap.DocumentManager} this
26776          * @param {Object} formData
26777          */
26778         "prepare" : true,
26779         /**
26780          * @event remove
26781          * Fire when remove the file
26782          * @param {Roo.bootstrap.DocumentManager} this
26783          * @param {Object} file
26784          */
26785         "remove" : true,
26786         /**
26787          * @event refresh
26788          * Fire after refresh the file
26789          * @param {Roo.bootstrap.DocumentManager} this
26790          */
26791         "refresh" : true,
26792         /**
26793          * @event click
26794          * Fire after click the image
26795          * @param {Roo.bootstrap.DocumentManager} this
26796          * @param {Object} file
26797          */
26798         "click" : true,
26799         /**
26800          * @event edit
26801          * Fire when upload a image and editable set to true
26802          * @param {Roo.bootstrap.DocumentManager} this
26803          * @param {Object} file
26804          */
26805         "edit" : true,
26806         /**
26807          * @event beforeselectfile
26808          * Fire before select file
26809          * @param {Roo.bootstrap.DocumentManager} this
26810          */
26811         "beforeselectfile" : true,
26812         /**
26813          * @event process
26814          * Fire before process file
26815          * @param {Roo.bootstrap.DocumentManager} this
26816          * @param {Object} file
26817          */
26818         "process" : true
26819         
26820     });
26821 };
26822
26823 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26824     
26825     boxes : 0,
26826     inputName : '',
26827     thumbSize : 300,
26828     multiple : true,
26829     files : [],
26830     method : 'POST',
26831     url : '',
26832     paramName : 'imageUpload',
26833     fieldLabel : '',
26834     labelWidth : 4,
26835     labelAlign : 'left',
26836     editable : true,
26837     delegates : [],
26838     
26839     
26840     xhr : false, 
26841     
26842     getAutoCreate : function()
26843     {   
26844         var managerWidget = {
26845             tag : 'div',
26846             cls : 'roo-document-manager',
26847             cn : [
26848                 {
26849                     tag : 'input',
26850                     cls : 'roo-document-manager-selector',
26851                     type : 'file'
26852                 },
26853                 {
26854                     tag : 'div',
26855                     cls : 'roo-document-manager-uploader',
26856                     cn : [
26857                         {
26858                             tag : 'div',
26859                             cls : 'roo-document-manager-upload-btn',
26860                             html : '<i class="fa fa-plus"></i>'
26861                         }
26862                     ]
26863                     
26864                 }
26865             ]
26866         };
26867         
26868         var content = [
26869             {
26870                 tag : 'div',
26871                 cls : 'column col-md-12',
26872                 cn : managerWidget
26873             }
26874         ];
26875         
26876         if(this.fieldLabel.length){
26877             
26878             content = [
26879                 {
26880                     tag : 'div',
26881                     cls : 'column col-md-12',
26882                     html : this.fieldLabel
26883                 },
26884                 {
26885                     tag : 'div',
26886                     cls : 'column col-md-12',
26887                     cn : managerWidget
26888                 }
26889             ];
26890
26891             if(this.labelAlign == 'left'){
26892                 content = [
26893                     {
26894                         tag : 'div',
26895                         cls : 'column col-md-' + this.labelWidth,
26896                         html : this.fieldLabel
26897                     },
26898                     {
26899                         tag : 'div',
26900                         cls : 'column col-md-' + (12 - this.labelWidth),
26901                         cn : managerWidget
26902                     }
26903                 ];
26904                 
26905             }
26906         }
26907         
26908         var cfg = {
26909             tag : 'div',
26910             cls : 'row clearfix',
26911             cn : content
26912         };
26913         
26914         return cfg;
26915         
26916     },
26917     
26918     initEvents : function()
26919     {
26920         this.managerEl = this.el.select('.roo-document-manager', true).first();
26921         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26922         
26923         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26924         this.selectorEl.hide();
26925         
26926         if(this.multiple){
26927             this.selectorEl.attr('multiple', 'multiple');
26928         }
26929         
26930         this.selectorEl.on('change', this.onFileSelected, this);
26931         
26932         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26933         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26934         
26935         this.uploader.on('click', this.onUploaderClick, this);
26936         
26937         this.renderProgressDialog();
26938         
26939         var _this = this;
26940         
26941         window.addEventListener("resize", function() { _this.refresh(); } );
26942         
26943         this.fireEvent('initial', this);
26944     },
26945     
26946     renderProgressDialog : function()
26947     {
26948         var _this = this;
26949         
26950         this.progressDialog = new Roo.bootstrap.Modal({
26951             cls : 'roo-document-manager-progress-dialog',
26952             allow_close : false,
26953             title : '',
26954             buttons : [
26955                 {
26956                     name  :'cancel',
26957                     weight : 'danger',
26958                     html : 'Cancel'
26959                 }
26960             ], 
26961             listeners : { 
26962                 btnclick : function() {
26963                     _this.uploadCancel();
26964                     this.hide();
26965                 }
26966             }
26967         });
26968          
26969         this.progressDialog.render(Roo.get(document.body));
26970          
26971         this.progress = new Roo.bootstrap.Progress({
26972             cls : 'roo-document-manager-progress',
26973             active : true,
26974             striped : true
26975         });
26976         
26977         this.progress.render(this.progressDialog.getChildContainer());
26978         
26979         this.progressBar = new Roo.bootstrap.ProgressBar({
26980             cls : 'roo-document-manager-progress-bar',
26981             aria_valuenow : 0,
26982             aria_valuemin : 0,
26983             aria_valuemax : 12,
26984             panel : 'success'
26985         });
26986         
26987         this.progressBar.render(this.progress.getChildContainer());
26988     },
26989     
26990     onUploaderClick : function(e)
26991     {
26992         e.preventDefault();
26993      
26994         if(this.fireEvent('beforeselectfile', this) != false){
26995             this.selectorEl.dom.click();
26996         }
26997         
26998     },
26999     
27000     onFileSelected : function(e)
27001     {
27002         e.preventDefault();
27003         
27004         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27005             return;
27006         }
27007         
27008         Roo.each(this.selectorEl.dom.files, function(file){
27009             if(this.fireEvent('inspect', this, file) != false){
27010                 this.files.push(file);
27011             }
27012         }, this);
27013         
27014         this.queue();
27015         
27016     },
27017     
27018     queue : function()
27019     {
27020         this.selectorEl.dom.value = '';
27021         
27022         if(!this.files.length){
27023             return;
27024         }
27025         
27026         if(this.boxes > 0 && this.files.length > this.boxes){
27027             this.files = this.files.slice(0, this.boxes);
27028         }
27029         
27030         this.uploader.show();
27031         
27032         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27033             this.uploader.hide();
27034         }
27035         
27036         var _this = this;
27037         
27038         var files = [];
27039         
27040         var docs = [];
27041         
27042         Roo.each(this.files, function(file){
27043             
27044             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27045                 var f = this.renderPreview(file);
27046                 files.push(f);
27047                 return;
27048             }
27049             
27050             if(file.type.indexOf('image') != -1){
27051                 this.delegates.push(
27052                     (function(){
27053                         _this.process(file);
27054                     }).createDelegate(this)
27055                 );
27056         
27057                 return;
27058             }
27059             
27060             docs.push(
27061                 (function(){
27062                     _this.process(file);
27063                 }).createDelegate(this)
27064             );
27065             
27066         }, this);
27067         
27068         this.files = files;
27069         
27070         this.delegates = this.delegates.concat(docs);
27071         
27072         if(!this.delegates.length){
27073             this.refresh();
27074             return;
27075         }
27076         
27077         this.progressBar.aria_valuemax = this.delegates.length;
27078         
27079         this.arrange();
27080         
27081         return;
27082     },
27083     
27084     arrange : function()
27085     {
27086         if(!this.delegates.length){
27087             this.progressDialog.hide();
27088             this.refresh();
27089             return;
27090         }
27091         
27092         var delegate = this.delegates.shift();
27093         
27094         this.progressDialog.show();
27095         
27096         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27097         
27098         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27099         
27100         delegate();
27101     },
27102     
27103     refresh : function()
27104     {
27105         this.uploader.show();
27106         
27107         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27108             this.uploader.hide();
27109         }
27110         
27111         Roo.isTouch ? this.closable(false) : this.closable(true);
27112         
27113         this.fireEvent('refresh', this);
27114     },
27115     
27116     onRemove : function(e, el, o)
27117     {
27118         e.preventDefault();
27119         
27120         this.fireEvent('remove', this, o);
27121         
27122     },
27123     
27124     remove : function(o)
27125     {
27126         var files = [];
27127         
27128         Roo.each(this.files, function(file){
27129             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27130                 files.push(file);
27131                 return;
27132             }
27133
27134             o.target.remove();
27135
27136         }, this);
27137         
27138         this.files = files;
27139         
27140         this.refresh();
27141     },
27142     
27143     clear : function()
27144     {
27145         Roo.each(this.files, function(file){
27146             if(!file.target){
27147                 return;
27148             }
27149             
27150             file.target.remove();
27151
27152         }, this);
27153         
27154         this.files = [];
27155         
27156         this.refresh();
27157     },
27158     
27159     onClick : function(e, el, o)
27160     {
27161         e.preventDefault();
27162         
27163         this.fireEvent('click', this, o);
27164         
27165     },
27166     
27167     closable : function(closable)
27168     {
27169         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27170             
27171             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27172             
27173             if(closable){
27174                 el.show();
27175                 return;
27176             }
27177             
27178             el.hide();
27179             
27180         }, this);
27181     },
27182     
27183     xhrOnLoad : function(xhr)
27184     {
27185         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27186             el.remove();
27187         }, this);
27188         
27189         if (xhr.readyState !== 4) {
27190             this.arrange();
27191             this.fireEvent('exception', this, xhr);
27192             return;
27193         }
27194
27195         var response = Roo.decode(xhr.responseText);
27196         
27197         if(!response.success){
27198             this.arrange();
27199             this.fireEvent('exception', this, xhr);
27200             return;
27201         }
27202         
27203         var file = this.renderPreview(response.data);
27204         
27205         this.files.push(file);
27206         
27207         this.arrange();
27208         
27209     },
27210     
27211     xhrOnError : function(xhr)
27212     {
27213         Roo.log('xhr on error');
27214         
27215         var response = Roo.decode(xhr.responseText);
27216           
27217         Roo.log(response);
27218         
27219         this.arrange();
27220     },
27221     
27222     process : function(file)
27223     {
27224         if(this.fireEvent('process', this, file) !== false){
27225             if(this.editable && file.type.indexOf('image') != -1){
27226                 this.fireEvent('edit', this, file);
27227                 return;
27228             }
27229
27230             this.uploadStart(file, false);
27231
27232             return;
27233         }
27234         
27235     },
27236     
27237     uploadStart : function(file, crop)
27238     {
27239         this.xhr = new XMLHttpRequest();
27240         
27241         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27242             this.arrange();
27243             return;
27244         }
27245         
27246         file.xhr = this.xhr;
27247             
27248         this.managerEl.createChild({
27249             tag : 'div',
27250             cls : 'roo-document-manager-loading',
27251             cn : [
27252                 {
27253                     tag : 'div',
27254                     tooltip : file.name,
27255                     cls : 'roo-document-manager-thumb',
27256                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27257                 }
27258             ]
27259
27260         });
27261
27262         this.xhr.open(this.method, this.url, true);
27263         
27264         var headers = {
27265             "Accept": "application/json",
27266             "Cache-Control": "no-cache",
27267             "X-Requested-With": "XMLHttpRequest"
27268         };
27269         
27270         for (var headerName in headers) {
27271             var headerValue = headers[headerName];
27272             if (headerValue) {
27273                 this.xhr.setRequestHeader(headerName, headerValue);
27274             }
27275         }
27276         
27277         var _this = this;
27278         
27279         this.xhr.onload = function()
27280         {
27281             _this.xhrOnLoad(_this.xhr);
27282         }
27283         
27284         this.xhr.onerror = function()
27285         {
27286             _this.xhrOnError(_this.xhr);
27287         }
27288         
27289         var formData = new FormData();
27290
27291         formData.append('returnHTML', 'NO');
27292         
27293         if(crop){
27294             formData.append('crop', crop);
27295         }
27296         
27297         formData.append(this.paramName, file, file.name);
27298         
27299         if(this.fireEvent('prepare', this, formData) != false){
27300             this.xhr.send(formData);
27301         };
27302     },
27303     
27304     uploadCancel : function()
27305     {
27306         if (this.xhr) {
27307             this.xhr.abort();
27308         }
27309         
27310         
27311         this.delegates = [];
27312         
27313         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27314             el.remove();
27315         }, this);
27316         
27317         this.arrange();
27318     },
27319     
27320     renderPreview : function(file)
27321     {
27322         if(typeof(file.target) != 'undefined' && file.target){
27323             return file;
27324         }
27325         
27326         var previewEl = this.managerEl.createChild({
27327             tag : 'div',
27328             cls : 'roo-document-manager-preview',
27329             cn : [
27330                 {
27331                     tag : 'div',
27332                     tooltip : file.filename,
27333                     cls : 'roo-document-manager-thumb',
27334                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27335                 },
27336                 {
27337                     tag : 'button',
27338                     cls : 'close',
27339                     html : '<i class="fa fa-times-circle"></i>'
27340                 }
27341             ]
27342         });
27343
27344         var close = previewEl.select('button.close', true).first();
27345
27346         close.on('click', this.onRemove, this, file);
27347
27348         file.target = previewEl;
27349
27350         var image = previewEl.select('img', true).first();
27351         
27352         var _this = this;
27353         
27354         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27355         
27356         image.on('click', this.onClick, this, file);
27357         
27358         return file;
27359         
27360     },
27361     
27362     onPreviewLoad : function(file, image)
27363     {
27364         if(typeof(file.target) == 'undefined' || !file.target){
27365             return;
27366         }
27367         
27368         var width = image.dom.naturalWidth || image.dom.width;
27369         var height = image.dom.naturalHeight || image.dom.height;
27370         
27371         if(width > height){
27372             file.target.addClass('wide');
27373             return;
27374         }
27375         
27376         file.target.addClass('tall');
27377         return;
27378         
27379     },
27380     
27381     uploadFromSource : function(file, crop)
27382     {
27383         this.xhr = new XMLHttpRequest();
27384         
27385         this.managerEl.createChild({
27386             tag : 'div',
27387             cls : 'roo-document-manager-loading',
27388             cn : [
27389                 {
27390                     tag : 'div',
27391                     tooltip : file.name,
27392                     cls : 'roo-document-manager-thumb',
27393                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27394                 }
27395             ]
27396
27397         });
27398
27399         this.xhr.open(this.method, this.url, true);
27400         
27401         var headers = {
27402             "Accept": "application/json",
27403             "Cache-Control": "no-cache",
27404             "X-Requested-With": "XMLHttpRequest"
27405         };
27406         
27407         for (var headerName in headers) {
27408             var headerValue = headers[headerName];
27409             if (headerValue) {
27410                 this.xhr.setRequestHeader(headerName, headerValue);
27411             }
27412         }
27413         
27414         var _this = this;
27415         
27416         this.xhr.onload = function()
27417         {
27418             _this.xhrOnLoad(_this.xhr);
27419         }
27420         
27421         this.xhr.onerror = function()
27422         {
27423             _this.xhrOnError(_this.xhr);
27424         }
27425         
27426         var formData = new FormData();
27427
27428         formData.append('returnHTML', 'NO');
27429         
27430         formData.append('crop', crop);
27431         
27432         if(typeof(file.filename) != 'undefined'){
27433             formData.append('filename', file.filename);
27434         }
27435         
27436         if(typeof(file.mimetype) != 'undefined'){
27437             formData.append('mimetype', file.mimetype);
27438         }
27439         
27440         if(this.fireEvent('prepare', this, formData) != false){
27441             this.xhr.send(formData);
27442         };
27443     }
27444 });
27445
27446 /*
27447 * Licence: LGPL
27448 */
27449
27450 /**
27451  * @class Roo.bootstrap.DocumentViewer
27452  * @extends Roo.bootstrap.Component
27453  * Bootstrap DocumentViewer class
27454  * 
27455  * @constructor
27456  * Create a new DocumentViewer
27457  * @param {Object} config The config object
27458  */
27459
27460 Roo.bootstrap.DocumentViewer = function(config){
27461     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27462     
27463     this.addEvents({
27464         /**
27465          * @event initial
27466          * Fire after initEvent
27467          * @param {Roo.bootstrap.DocumentViewer} this
27468          */
27469         "initial" : true,
27470         /**
27471          * @event click
27472          * Fire after click
27473          * @param {Roo.bootstrap.DocumentViewer} this
27474          */
27475         "click" : true,
27476         /**
27477          * @event trash
27478          * Fire after trash button
27479          * @param {Roo.bootstrap.DocumentViewer} this
27480          */
27481         "trash" : true
27482         
27483     });
27484 };
27485
27486 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27487     
27488     getAutoCreate : function()
27489     {
27490         var cfg = {
27491             tag : 'div',
27492             cls : 'roo-document-viewer',
27493             cn : [
27494                 {
27495                     tag : 'div',
27496                     cls : 'roo-document-viewer-body',
27497                     cn : [
27498                         {
27499                             tag : 'div',
27500                             cls : 'roo-document-viewer-thumb',
27501                             cn : [
27502                                 {
27503                                     tag : 'img',
27504                                     cls : 'roo-document-viewer-image'
27505                                 }
27506                             ]
27507                         }
27508                     ]
27509                 },
27510                 {
27511                     tag : 'div',
27512                     cls : 'roo-document-viewer-footer',
27513                     cn : {
27514                         tag : 'div',
27515                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27516                         cn : [
27517                             {
27518                                 tag : 'div',
27519                                 cls : 'btn-group',
27520                                 cn : [
27521                                     {
27522                                         tag : 'button',
27523                                         cls : 'btn btn-default roo-document-viewer-trash',
27524                                         html : '<i class="fa fa-trash"></i>'
27525                                     }
27526                                 ]
27527                             }
27528                         ]
27529                     }
27530                 }
27531             ]
27532         };
27533         
27534         return cfg;
27535     },
27536     
27537     initEvents : function()
27538     {
27539         
27540         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27541         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27542         
27543         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27544         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27545         
27546         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27547         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27548         
27549         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27550         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27551         
27552         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27553         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27554         
27555         this.bodyEl.on('click', this.onClick, this);
27556         
27557         this.trashBtn.on('click', this.onTrash, this);
27558         
27559     },
27560     
27561     initial : function()
27562     {
27563 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27564         
27565         
27566         this.fireEvent('initial', this);
27567         
27568     },
27569     
27570     onClick : function(e)
27571     {
27572         e.preventDefault();
27573         
27574         this.fireEvent('click', this);
27575     },
27576     
27577     onTrash : function(e)
27578     {
27579         e.preventDefault();
27580         
27581         this.fireEvent('trash', this);
27582     }
27583     
27584 });
27585 /*
27586  * - LGPL
27587  *
27588  * nav progress bar
27589  * 
27590  */
27591
27592 /**
27593  * @class Roo.bootstrap.NavProgressBar
27594  * @extends Roo.bootstrap.Component
27595  * Bootstrap NavProgressBar class
27596  * 
27597  * @constructor
27598  * Create a new nav progress bar
27599  * @param {Object} config The config object
27600  */
27601
27602 Roo.bootstrap.NavProgressBar = function(config){
27603     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27604
27605     this.bullets = this.bullets || [];
27606    
27607 //    Roo.bootstrap.NavProgressBar.register(this);
27608      this.addEvents({
27609         /**
27610              * @event changed
27611              * Fires when the active item changes
27612              * @param {Roo.bootstrap.NavProgressBar} this
27613              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27614              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27615          */
27616         'changed': true
27617      });
27618     
27619 };
27620
27621 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27622     
27623     bullets : [],
27624     barItems : [],
27625     
27626     getAutoCreate : function()
27627     {
27628         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27629         
27630         cfg = {
27631             tag : 'div',
27632             cls : 'roo-navigation-bar-group',
27633             cn : [
27634                 {
27635                     tag : 'div',
27636                     cls : 'roo-navigation-top-bar'
27637                 },
27638                 {
27639                     tag : 'div',
27640                     cls : 'roo-navigation-bullets-bar',
27641                     cn : [
27642                         {
27643                             tag : 'ul',
27644                             cls : 'roo-navigation-bar'
27645                         }
27646                     ]
27647                 },
27648                 
27649                 {
27650                     tag : 'div',
27651                     cls : 'roo-navigation-bottom-bar'
27652                 }
27653             ]
27654             
27655         };
27656         
27657         return cfg;
27658         
27659     },
27660     
27661     initEvents: function() 
27662     {
27663         
27664     },
27665     
27666     onRender : function(ct, position) 
27667     {
27668         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27669         
27670         if(this.bullets.length){
27671             Roo.each(this.bullets, function(b){
27672                this.addItem(b);
27673             }, this);
27674         }
27675         
27676         this.format();
27677         
27678     },
27679     
27680     addItem : function(cfg)
27681     {
27682         var item = new Roo.bootstrap.NavProgressItem(cfg);
27683         
27684         item.parentId = this.id;
27685         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27686         
27687         if(cfg.html){
27688             var top = new Roo.bootstrap.Element({
27689                 tag : 'div',
27690                 cls : 'roo-navigation-bar-text'
27691             });
27692             
27693             var bottom = new Roo.bootstrap.Element({
27694                 tag : 'div',
27695                 cls : 'roo-navigation-bar-text'
27696             });
27697             
27698             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27699             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27700             
27701             var topText = new Roo.bootstrap.Element({
27702                 tag : 'span',
27703                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27704             });
27705             
27706             var bottomText = new Roo.bootstrap.Element({
27707                 tag : 'span',
27708                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27709             });
27710             
27711             topText.onRender(top.el, null);
27712             bottomText.onRender(bottom.el, null);
27713             
27714             item.topEl = top;
27715             item.bottomEl = bottom;
27716         }
27717         
27718         this.barItems.push(item);
27719         
27720         return item;
27721     },
27722     
27723     getActive : function()
27724     {
27725         var active = false;
27726         
27727         Roo.each(this.barItems, function(v){
27728             
27729             if (!v.isActive()) {
27730                 return;
27731             }
27732             
27733             active = v;
27734             return false;
27735             
27736         });
27737         
27738         return active;
27739     },
27740     
27741     setActiveItem : function(item)
27742     {
27743         var prev = false;
27744         
27745         Roo.each(this.barItems, function(v){
27746             if (v.rid == item.rid) {
27747                 return ;
27748             }
27749             
27750             if (v.isActive()) {
27751                 v.setActive(false);
27752                 prev = v;
27753             }
27754         });
27755
27756         item.setActive(true);
27757         
27758         this.fireEvent('changed', this, item, prev);
27759     },
27760     
27761     getBarItem: function(rid)
27762     {
27763         var ret = false;
27764         
27765         Roo.each(this.barItems, function(e) {
27766             if (e.rid != rid) {
27767                 return;
27768             }
27769             
27770             ret =  e;
27771             return false;
27772         });
27773         
27774         return ret;
27775     },
27776     
27777     indexOfItem : function(item)
27778     {
27779         var index = false;
27780         
27781         Roo.each(this.barItems, function(v, i){
27782             
27783             if (v.rid != item.rid) {
27784                 return;
27785             }
27786             
27787             index = i;
27788             return false
27789         });
27790         
27791         return index;
27792     },
27793     
27794     setActiveNext : function()
27795     {
27796         var i = this.indexOfItem(this.getActive());
27797         
27798         if (i > this.barItems.length) {
27799             return;
27800         }
27801         
27802         this.setActiveItem(this.barItems[i+1]);
27803     },
27804     
27805     setActivePrev : function()
27806     {
27807         var i = this.indexOfItem(this.getActive());
27808         
27809         if (i  < 1) {
27810             return;
27811         }
27812         
27813         this.setActiveItem(this.barItems[i-1]);
27814     },
27815     
27816     format : function()
27817     {
27818         if(!this.barItems.length){
27819             return;
27820         }
27821      
27822         var width = 100 / this.barItems.length;
27823         
27824         Roo.each(this.barItems, function(i){
27825             i.el.setStyle('width', width + '%');
27826             i.topEl.el.setStyle('width', width + '%');
27827             i.bottomEl.el.setStyle('width', width + '%');
27828         }, this);
27829         
27830     }
27831     
27832 });
27833 /*
27834  * - LGPL
27835  *
27836  * Nav Progress Item
27837  * 
27838  */
27839
27840 /**
27841  * @class Roo.bootstrap.NavProgressItem
27842  * @extends Roo.bootstrap.Component
27843  * Bootstrap NavProgressItem class
27844  * @cfg {String} rid the reference id
27845  * @cfg {Boolean} active (true|false) Is item active default false
27846  * @cfg {Boolean} disabled (true|false) Is item active default false
27847  * @cfg {String} html
27848  * @cfg {String} position (top|bottom) text position default bottom
27849  * @cfg {String} icon show icon instead of number
27850  * 
27851  * @constructor
27852  * Create a new NavProgressItem
27853  * @param {Object} config The config object
27854  */
27855 Roo.bootstrap.NavProgressItem = function(config){
27856     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27857     this.addEvents({
27858         // raw events
27859         /**
27860          * @event click
27861          * The raw click event for the entire grid.
27862          * @param {Roo.bootstrap.NavProgressItem} this
27863          * @param {Roo.EventObject} e
27864          */
27865         "click" : true
27866     });
27867    
27868 };
27869
27870 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27871     
27872     rid : '',
27873     active : false,
27874     disabled : false,
27875     html : '',
27876     position : 'bottom',
27877     icon : false,
27878     
27879     getAutoCreate : function()
27880     {
27881         var iconCls = 'roo-navigation-bar-item-icon';
27882         
27883         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27884         
27885         var cfg = {
27886             tag: 'li',
27887             cls: 'roo-navigation-bar-item',
27888             cn : [
27889                 {
27890                     tag : 'i',
27891                     cls : iconCls
27892                 }
27893             ]
27894         };
27895         
27896         if(this.active){
27897             cfg.cls += ' active';
27898         }
27899         if(this.disabled){
27900             cfg.cls += ' disabled';
27901         }
27902         
27903         return cfg;
27904     },
27905     
27906     disable : function()
27907     {
27908         this.setDisabled(true);
27909     },
27910     
27911     enable : function()
27912     {
27913         this.setDisabled(false);
27914     },
27915     
27916     initEvents: function() 
27917     {
27918         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27919         
27920         this.iconEl.on('click', this.onClick, this);
27921     },
27922     
27923     onClick : function(e)
27924     {
27925         e.preventDefault();
27926         
27927         if(this.disabled){
27928             return;
27929         }
27930         
27931         if(this.fireEvent('click', this, e) === false){
27932             return;
27933         };
27934         
27935         this.parent().setActiveItem(this);
27936     },
27937     
27938     isActive: function () 
27939     {
27940         return this.active;
27941     },
27942     
27943     setActive : function(state)
27944     {
27945         if(this.active == state){
27946             return;
27947         }
27948         
27949         this.active = state;
27950         
27951         if (state) {
27952             this.el.addClass('active');
27953             return;
27954         }
27955         
27956         this.el.removeClass('active');
27957         
27958         return;
27959     },
27960     
27961     setDisabled : function(state)
27962     {
27963         if(this.disabled == state){
27964             return;
27965         }
27966         
27967         this.disabled = state;
27968         
27969         if (state) {
27970             this.el.addClass('disabled');
27971             return;
27972         }
27973         
27974         this.el.removeClass('disabled');
27975     },
27976     
27977     tooltipEl : function()
27978     {
27979         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27980     }
27981 });
27982  
27983
27984  /*
27985  * - LGPL
27986  *
27987  * FieldLabel
27988  * 
27989  */
27990
27991 /**
27992  * @class Roo.bootstrap.FieldLabel
27993  * @extends Roo.bootstrap.Component
27994  * Bootstrap FieldLabel class
27995  * @cfg {String} html contents of the element
27996  * @cfg {String} tag tag of the element default label
27997  * @cfg {String} cls class of the element
27998  * @cfg {String} target label target 
27999  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28000  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28001  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28002  * @cfg {String} iconTooltip default "This field is required"
28003  * 
28004  * @constructor
28005  * Create a new FieldLabel
28006  * @param {Object} config The config object
28007  */
28008
28009 Roo.bootstrap.FieldLabel = function(config){
28010     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28011     
28012     this.addEvents({
28013             /**
28014              * @event invalid
28015              * Fires after the field has been marked as invalid.
28016              * @param {Roo.form.FieldLabel} this
28017              * @param {String} msg The validation message
28018              */
28019             invalid : true,
28020             /**
28021              * @event valid
28022              * Fires after the field has been validated with no errors.
28023              * @param {Roo.form.FieldLabel} this
28024              */
28025             valid : true
28026         });
28027 };
28028
28029 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28030     
28031     tag: 'label',
28032     cls: '',
28033     html: '',
28034     target: '',
28035     allowBlank : true,
28036     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28037     validClass : 'text-success fa fa-lg fa-check',
28038     iconTooltip : 'This field is required',
28039     
28040     getAutoCreate : function(){
28041         
28042         var cfg = {
28043             tag : this.tag,
28044             cls : 'roo-bootstrap-field-label ' + this.cls,
28045             for : this.target,
28046             cn : [
28047                 {
28048                     tag : 'i',
28049                     cls : '',
28050                     tooltip : this.iconTooltip
28051                 },
28052                 {
28053                     tag : 'span',
28054                     html : this.html
28055                 }
28056             ] 
28057         };
28058         
28059         return cfg;
28060     },
28061     
28062     initEvents: function() 
28063     {
28064         Roo.bootstrap.Element.superclass.initEvents.call(this);
28065         
28066         this.iconEl = this.el.select('i', true).first();
28067         
28068         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28069         
28070         Roo.bootstrap.FieldLabel.register(this);
28071     },
28072     
28073     /**
28074      * Mark this field as valid
28075      */
28076     markValid : function()
28077     {
28078         this.iconEl.show();
28079         
28080         this.iconEl.removeClass(this.invalidClass);
28081         
28082         this.iconEl.addClass(this.validClass);
28083         
28084         this.fireEvent('valid', this);
28085     },
28086     
28087     /**
28088      * Mark this field as invalid
28089      * @param {String} msg The validation message
28090      */
28091     markInvalid : function(msg)
28092     {
28093         this.iconEl.show();
28094         
28095         this.iconEl.removeClass(this.validClass);
28096         
28097         this.iconEl.addClass(this.invalidClass);
28098         
28099         this.fireEvent('invalid', this, msg);
28100     }
28101     
28102    
28103 });
28104
28105 Roo.apply(Roo.bootstrap.FieldLabel, {
28106     
28107     groups: {},
28108     
28109      /**
28110     * register a FieldLabel Group
28111     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28112     */
28113     register : function(label)
28114     {
28115         if(this.groups.hasOwnProperty(label.target)){
28116             return;
28117         }
28118      
28119         this.groups[label.target] = label;
28120         
28121     },
28122     /**
28123     * fetch a FieldLabel Group based on the target
28124     * @param {string} target
28125     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28126     */
28127     get: function(target) {
28128         if (typeof(this.groups[target]) == 'undefined') {
28129             return false;
28130         }
28131         
28132         return this.groups[target] ;
28133     }
28134 });
28135
28136  
28137
28138  /*
28139  * - LGPL
28140  *
28141  * page DateSplitField.
28142  * 
28143  */
28144
28145
28146 /**
28147  * @class Roo.bootstrap.DateSplitField
28148  * @extends Roo.bootstrap.Component
28149  * Bootstrap DateSplitField class
28150  * @cfg {string} fieldLabel - the label associated
28151  * @cfg {Number} labelWidth set the width of label (0-12)
28152  * @cfg {String} labelAlign (top|left)
28153  * @cfg {Boolean} dayAllowBlank (true|false) default false
28154  * @cfg {Boolean} monthAllowBlank (true|false) default false
28155  * @cfg {Boolean} yearAllowBlank (true|false) default false
28156  * @cfg {string} dayPlaceholder 
28157  * @cfg {string} monthPlaceholder
28158  * @cfg {string} yearPlaceholder
28159  * @cfg {string} dayFormat default 'd'
28160  * @cfg {string} monthFormat default 'm'
28161  * @cfg {string} yearFormat default 'Y'
28162
28163  *     
28164  * @constructor
28165  * Create a new DateSplitField
28166  * @param {Object} config The config object
28167  */
28168
28169 Roo.bootstrap.DateSplitField = function(config){
28170     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28171     
28172     this.addEvents({
28173         // raw events
28174          /**
28175          * @event years
28176          * getting the data of years
28177          * @param {Roo.bootstrap.DateSplitField} this
28178          * @param {Object} years
28179          */
28180         "years" : true,
28181         /**
28182          * @event days
28183          * getting the data of days
28184          * @param {Roo.bootstrap.DateSplitField} this
28185          * @param {Object} days
28186          */
28187         "days" : true,
28188         /**
28189          * @event invalid
28190          * Fires after the field has been marked as invalid.
28191          * @param {Roo.form.Field} this
28192          * @param {String} msg The validation message
28193          */
28194         invalid : true,
28195        /**
28196          * @event valid
28197          * Fires after the field has been validated with no errors.
28198          * @param {Roo.form.Field} this
28199          */
28200         valid : true
28201     });
28202 };
28203
28204 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28205     
28206     fieldLabel : '',
28207     labelAlign : 'top',
28208     labelWidth : 3,
28209     dayAllowBlank : false,
28210     monthAllowBlank : false,
28211     yearAllowBlank : false,
28212     dayPlaceholder : '',
28213     monthPlaceholder : '',
28214     yearPlaceholder : '',
28215     dayFormat : 'd',
28216     monthFormat : 'm',
28217     yearFormat : 'Y',
28218     isFormField : true,
28219     
28220     getAutoCreate : function()
28221     {
28222         var cfg = {
28223             tag : 'div',
28224             cls : 'row roo-date-split-field-group',
28225             cn : [
28226                 {
28227                     tag : 'input',
28228                     type : 'hidden',
28229                     cls : 'form-hidden-field roo-date-split-field-group-value',
28230                     name : this.name
28231                 }
28232             ]
28233         };
28234         
28235         if(this.fieldLabel){
28236             cfg.cn.push({
28237                 tag : 'div',
28238                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28239                 cn : [
28240                     {
28241                         tag : 'label',
28242                         html : this.fieldLabel
28243                     }
28244                 ]
28245             });
28246         }
28247         
28248         Roo.each(['day', 'month', 'year'], function(t){
28249             cfg.cn.push({
28250                 tag : 'div',
28251                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28252             });
28253         }, this);
28254         
28255         return cfg;
28256     },
28257     
28258     inputEl: function ()
28259     {
28260         return this.el.select('.roo-date-split-field-group-value', true).first();
28261     },
28262     
28263     onRender : function(ct, position) 
28264     {
28265         var _this = this;
28266         
28267         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28268         
28269         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28270         
28271         this.dayField = new Roo.bootstrap.ComboBox({
28272             allowBlank : this.dayAllowBlank,
28273             alwaysQuery : true,
28274             displayField : 'value',
28275             editable : false,
28276             fieldLabel : '',
28277             forceSelection : true,
28278             mode : 'local',
28279             placeholder : this.dayPlaceholder,
28280             selectOnFocus : true,
28281             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28282             triggerAction : 'all',
28283             typeAhead : true,
28284             valueField : 'value',
28285             store : new Roo.data.SimpleStore({
28286                 data : (function() {    
28287                     var days = [];
28288                     _this.fireEvent('days', _this, days);
28289                     return days;
28290                 })(),
28291                 fields : [ 'value' ]
28292             }),
28293             listeners : {
28294                 select : function (_self, record, index)
28295                 {
28296                     _this.setValue(_this.getValue());
28297                 }
28298             }
28299         });
28300
28301         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28302         
28303         this.monthField = new Roo.bootstrap.MonthField({
28304             after : '<i class=\"fa fa-calendar\"></i>',
28305             allowBlank : this.monthAllowBlank,
28306             placeholder : this.monthPlaceholder,
28307             readOnly : true,
28308             listeners : {
28309                 render : function (_self)
28310                 {
28311                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28312                         e.preventDefault();
28313                         _self.focus();
28314                     });
28315                 },
28316                 select : function (_self, oldvalue, newvalue)
28317                 {
28318                     _this.setValue(_this.getValue());
28319                 }
28320             }
28321         });
28322         
28323         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28324         
28325         this.yearField = new Roo.bootstrap.ComboBox({
28326             allowBlank : this.yearAllowBlank,
28327             alwaysQuery : true,
28328             displayField : 'value',
28329             editable : false,
28330             fieldLabel : '',
28331             forceSelection : true,
28332             mode : 'local',
28333             placeholder : this.yearPlaceholder,
28334             selectOnFocus : true,
28335             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28336             triggerAction : 'all',
28337             typeAhead : true,
28338             valueField : 'value',
28339             store : new Roo.data.SimpleStore({
28340                 data : (function() {
28341                     var years = [];
28342                     _this.fireEvent('years', _this, years);
28343                     return years;
28344                 })(),
28345                 fields : [ 'value' ]
28346             }),
28347             listeners : {
28348                 select : function (_self, record, index)
28349                 {
28350                     _this.setValue(_this.getValue());
28351                 }
28352             }
28353         });
28354
28355         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28356     },
28357     
28358     setValue : function(v, format)
28359     {
28360         this.inputEl.dom.value = v;
28361         
28362         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28363         
28364         var d = Date.parseDate(v, f);
28365         
28366         if(!d){
28367             this.validate();
28368             return;
28369         }
28370         
28371         this.setDay(d.format(this.dayFormat));
28372         this.setMonth(d.format(this.monthFormat));
28373         this.setYear(d.format(this.yearFormat));
28374         
28375         this.validate();
28376         
28377         return;
28378     },
28379     
28380     setDay : function(v)
28381     {
28382         this.dayField.setValue(v);
28383         this.inputEl.dom.value = this.getValue();
28384         this.validate();
28385         return;
28386     },
28387     
28388     setMonth : function(v)
28389     {
28390         this.monthField.setValue(v, true);
28391         this.inputEl.dom.value = this.getValue();
28392         this.validate();
28393         return;
28394     },
28395     
28396     setYear : function(v)
28397     {
28398         this.yearField.setValue(v);
28399         this.inputEl.dom.value = this.getValue();
28400         this.validate();
28401         return;
28402     },
28403     
28404     getDay : function()
28405     {
28406         return this.dayField.getValue();
28407     },
28408     
28409     getMonth : function()
28410     {
28411         return this.monthField.getValue();
28412     },
28413     
28414     getYear : function()
28415     {
28416         return this.yearField.getValue();
28417     },
28418     
28419     getValue : function()
28420     {
28421         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28422         
28423         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28424         
28425         return date;
28426     },
28427     
28428     reset : function()
28429     {
28430         this.setDay('');
28431         this.setMonth('');
28432         this.setYear('');
28433         this.inputEl.dom.value = '';
28434         this.validate();
28435         return;
28436     },
28437     
28438     validate : function()
28439     {
28440         var d = this.dayField.validate();
28441         var m = this.monthField.validate();
28442         var y = this.yearField.validate();
28443         
28444         var valid = true;
28445         
28446         if(
28447                 (!this.dayAllowBlank && !d) ||
28448                 (!this.monthAllowBlank && !m) ||
28449                 (!this.yearAllowBlank && !y)
28450         ){
28451             valid = false;
28452         }
28453         
28454         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28455             return valid;
28456         }
28457         
28458         if(valid){
28459             this.markValid();
28460             return valid;
28461         }
28462         
28463         this.markInvalid();
28464         
28465         return valid;
28466     },
28467     
28468     markValid : function()
28469     {
28470         
28471         var label = this.el.select('label', true).first();
28472         var icon = this.el.select('i.fa-star', true).first();
28473
28474         if(label && icon){
28475             icon.remove();
28476         }
28477         
28478         this.fireEvent('valid', this);
28479     },
28480     
28481      /**
28482      * Mark this field as invalid
28483      * @param {String} msg The validation message
28484      */
28485     markInvalid : function(msg)
28486     {
28487         
28488         var label = this.el.select('label', true).first();
28489         var icon = this.el.select('i.fa-star', true).first();
28490
28491         if(label && !icon){
28492             this.el.select('.roo-date-split-field-label', true).createChild({
28493                 tag : 'i',
28494                 cls : 'text-danger fa fa-lg fa-star',
28495                 tooltip : 'This field is required',
28496                 style : 'margin-right:5px;'
28497             }, label, true);
28498         }
28499         
28500         this.fireEvent('invalid', this, msg);
28501     },
28502     
28503     clearInvalid : function()
28504     {
28505         var label = this.el.select('label', true).first();
28506         var icon = this.el.select('i.fa-star', true).first();
28507
28508         if(label && icon){
28509             icon.remove();
28510         }
28511         
28512         this.fireEvent('valid', this);
28513     },
28514     
28515     getName: function()
28516     {
28517         return this.name;
28518     }
28519     
28520 });
28521
28522  /**
28523  *
28524  * This is based on 
28525  * http://masonry.desandro.com
28526  *
28527  * The idea is to render all the bricks based on vertical width...
28528  *
28529  * The original code extends 'outlayer' - we might need to use that....
28530  * 
28531  */
28532
28533
28534 /**
28535  * @class Roo.bootstrap.LayoutMasonry
28536  * @extends Roo.bootstrap.Component
28537  * Bootstrap Layout Masonry class
28538  * 
28539  * @constructor
28540  * Create a new Element
28541  * @param {Object} config The config object
28542  */
28543
28544 Roo.bootstrap.LayoutMasonry = function(config){
28545     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28546     
28547     this.bricks = [];
28548     
28549 };
28550
28551 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28552     
28553     /**
28554      * @cfg {Boolean} isLayoutInstant = no animation?
28555      */   
28556     isLayoutInstant : false, // needed?
28557    
28558     /**
28559      * @cfg {Number} boxWidth  width of the columns
28560      */   
28561     boxWidth : 450,
28562     
28563       /**
28564      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28565      */   
28566     boxHeight : 0,
28567     
28568     /**
28569      * @cfg {Number} padWidth padding below box..
28570      */   
28571     padWidth : 10, 
28572     
28573     /**
28574      * @cfg {Number} gutter gutter width..
28575      */   
28576     gutter : 10,
28577     
28578      /**
28579      * @cfg {Number} maxCols maximum number of columns
28580      */   
28581     
28582     maxCols: 0,
28583     
28584     /**
28585      * @cfg {Boolean} isAutoInitial defalut true
28586      */   
28587     isAutoInitial : true, 
28588     
28589     containerWidth: 0,
28590     
28591     /**
28592      * @cfg {Boolean} isHorizontal defalut false
28593      */   
28594     isHorizontal : false, 
28595
28596     currentSize : null,
28597     
28598     tag: 'div',
28599     
28600     cls: '',
28601     
28602     bricks: null, //CompositeElement
28603     
28604     cols : 1,
28605     
28606     _isLayoutInited : false,
28607     
28608 //    isAlternative : false, // only use for vertical layout...
28609     
28610     /**
28611      * @cfg {Number} alternativePadWidth padding below box..
28612      */   
28613     alternativePadWidth : 50, 
28614     
28615     getAutoCreate : function(){
28616         
28617         var cfg = {
28618             tag: this.tag,
28619             cls: 'blog-masonary-wrapper ' + this.cls,
28620             cn : {
28621                 cls : 'mas-boxes masonary'
28622             }
28623         };
28624         
28625         return cfg;
28626     },
28627     
28628     getChildContainer: function( )
28629     {
28630         if (this.boxesEl) {
28631             return this.boxesEl;
28632         }
28633         
28634         this.boxesEl = this.el.select('.mas-boxes').first();
28635         
28636         return this.boxesEl;
28637     },
28638     
28639     
28640     initEvents : function()
28641     {
28642         var _this = this;
28643         
28644         if(this.isAutoInitial){
28645             Roo.log('hook children rendered');
28646             this.on('childrenrendered', function() {
28647                 Roo.log('children rendered');
28648                 _this.initial();
28649             } ,this);
28650         }
28651     },
28652     
28653     initial : function()
28654     {
28655         this.currentSize = this.el.getBox(true);
28656         
28657         Roo.EventManager.onWindowResize(this.resize, this); 
28658
28659         if(!this.isAutoInitial){
28660             this.layout();
28661             return;
28662         }
28663         
28664         this.layout();
28665         
28666         return;
28667         //this.layout.defer(500,this);
28668         
28669     },
28670     
28671     resize : function()
28672     {
28673         Roo.log('resize');
28674         
28675         var cs = this.el.getBox(true);
28676         
28677         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28678             Roo.log("no change in with or X");
28679             return;
28680         }
28681         
28682         this.currentSize = cs;
28683         
28684         this.layout();
28685         
28686     },
28687     
28688     layout : function()
28689     {   
28690         this._resetLayout();
28691         
28692         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28693         
28694         this.layoutItems( isInstant );
28695       
28696         this._isLayoutInited = true;
28697         
28698     },
28699     
28700     _resetLayout : function()
28701     {
28702         if(this.isHorizontal){
28703             this.horizontalMeasureColumns();
28704             return;
28705         }
28706         
28707         this.verticalMeasureColumns();
28708         
28709     },
28710     
28711     verticalMeasureColumns : function()
28712     {
28713         this.getContainerWidth();
28714         
28715 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28716 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28717 //            return;
28718 //        }
28719         
28720         var boxWidth = this.boxWidth + this.padWidth;
28721         
28722         if(this.containerWidth < this.boxWidth){
28723             boxWidth = this.containerWidth
28724         }
28725         
28726         var containerWidth = this.containerWidth;
28727         
28728         var cols = Math.floor(containerWidth / boxWidth);
28729         
28730         this.cols = Math.max( cols, 1 );
28731         
28732         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28733         
28734         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28735         
28736         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28737         
28738         this.colWidth = boxWidth + avail - this.padWidth;
28739         
28740         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28741         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28742     },
28743     
28744     horizontalMeasureColumns : function()
28745     {
28746         this.getContainerWidth();
28747         
28748         var boxWidth = this.boxWidth;
28749         
28750         if(this.containerWidth < boxWidth){
28751             boxWidth = this.containerWidth;
28752         }
28753         
28754         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28755         
28756         this.el.setHeight(boxWidth);
28757         
28758     },
28759     
28760     getContainerWidth : function()
28761     {
28762         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
28763     },
28764     
28765     layoutItems : function( isInstant )
28766     {
28767         var items = Roo.apply([], this.bricks);
28768         
28769         if(this.isHorizontal){
28770             this._horizontalLayoutItems( items , isInstant );
28771             return;
28772         }
28773         
28774 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28775 //            this._verticalAlternativeLayoutItems( items , isInstant );
28776 //            return;
28777 //        }
28778         
28779         this._verticalLayoutItems( items , isInstant );
28780         
28781     },
28782     
28783     _verticalLayoutItems : function ( items , isInstant)
28784     {
28785         if ( !items || !items.length ) {
28786             return;
28787         }
28788         
28789         var standard = [
28790             ['xs', 'xs', 'xs', 'tall'],
28791             ['xs', 'xs', 'tall'],
28792             ['xs', 'xs', 'sm'],
28793             ['xs', 'xs', 'xs'],
28794             ['xs', 'tall'],
28795             ['xs', 'sm'],
28796             ['xs', 'xs'],
28797             ['xs'],
28798             
28799             ['sm', 'xs', 'xs'],
28800             ['sm', 'xs'],
28801             ['sm'],
28802             
28803             ['tall', 'xs', 'xs', 'xs'],
28804             ['tall', 'xs', 'xs'],
28805             ['tall', 'xs'],
28806             ['tall']
28807             
28808         ];
28809         
28810         var queue = [];
28811         
28812         var boxes = [];
28813         
28814         var box = [];
28815         
28816         Roo.each(items, function(item, k){
28817             
28818             switch (item.size) {
28819                 // these layouts take up a full box,
28820                 case 'md' :
28821                 case 'md-left' :
28822                 case 'md-right' :
28823                 case 'wide' :
28824                     
28825                     if(box.length){
28826                         boxes.push(box);
28827                         box = [];
28828                     }
28829                     
28830                     boxes.push([item]);
28831                     
28832                     break;
28833                     
28834                 case 'xs' :
28835                 case 'sm' :
28836                 case 'tall' :
28837                     
28838                     box.push(item);
28839                     
28840                     break;
28841                 default :
28842                     break;
28843                     
28844             }
28845             
28846         }, this);
28847         
28848         if(box.length){
28849             boxes.push(box);
28850             box = [];
28851         }
28852         
28853         var filterPattern = function(box, length)
28854         {
28855             if(!box.length){
28856                 return;
28857             }
28858             
28859             var match = false;
28860             
28861             var pattern = box.slice(0, length);
28862             
28863             var format = [];
28864             
28865             Roo.each(pattern, function(i){
28866                 format.push(i.size);
28867             }, this);
28868             
28869             Roo.each(standard, function(s){
28870                 
28871                 if(String(s) != String(format)){
28872                     return;
28873                 }
28874                 
28875                 match = true;
28876                 return false;
28877                 
28878             }, this);
28879             
28880             if(!match && length == 1){
28881                 return;
28882             }
28883             
28884             if(!match){
28885                 filterPattern(box, length - 1);
28886                 return;
28887             }
28888                 
28889             queue.push(pattern);
28890
28891             box = box.slice(length, box.length);
28892
28893             filterPattern(box, 4);
28894
28895             return;
28896             
28897         }
28898         
28899         Roo.each(boxes, function(box, k){
28900             
28901             if(!box.length){
28902                 return;
28903             }
28904             
28905             if(box.length == 1){
28906                 queue.push(box);
28907                 return;
28908             }
28909             
28910             filterPattern(box, 4);
28911             
28912         }, this);
28913         
28914         this._processVerticalLayoutQueue( queue, isInstant );
28915         
28916     },
28917     
28918 //    _verticalAlternativeLayoutItems : function( items , isInstant )
28919 //    {
28920 //        if ( !items || !items.length ) {
28921 //            return;
28922 //        }
28923 //
28924 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
28925 //        
28926 //    },
28927     
28928     _horizontalLayoutItems : function ( items , isInstant)
28929     {
28930         if ( !items || !items.length || items.length < 3) {
28931             return;
28932         }
28933         
28934         items.reverse();
28935         
28936         var eItems = items.slice(0, 3);
28937         
28938         items = items.slice(3, items.length);
28939         
28940         var standard = [
28941             ['xs', 'xs', 'xs', 'wide'],
28942             ['xs', 'xs', 'wide'],
28943             ['xs', 'xs', 'sm'],
28944             ['xs', 'xs', 'xs'],
28945             ['xs', 'wide'],
28946             ['xs', 'sm'],
28947             ['xs', 'xs'],
28948             ['xs'],
28949             
28950             ['sm', 'xs', 'xs'],
28951             ['sm', 'xs'],
28952             ['sm'],
28953             
28954             ['wide', 'xs', 'xs', 'xs'],
28955             ['wide', 'xs', 'xs'],
28956             ['wide', 'xs'],
28957             ['wide'],
28958             
28959             ['wide-thin']
28960         ];
28961         
28962         var queue = [];
28963         
28964         var boxes = [];
28965         
28966         var box = [];
28967         
28968         Roo.each(items, function(item, k){
28969             
28970             switch (item.size) {
28971                 case 'md' :
28972                 case 'md-left' :
28973                 case 'md-right' :
28974                 case 'tall' :
28975                     
28976                     if(box.length){
28977                         boxes.push(box);
28978                         box = [];
28979                     }
28980                     
28981                     boxes.push([item]);
28982                     
28983                     break;
28984                     
28985                 case 'xs' :
28986                 case 'sm' :
28987                 case 'wide' :
28988                 case 'wide-thin' :
28989                     
28990                     box.push(item);
28991                     
28992                     break;
28993                 default :
28994                     break;
28995                     
28996             }
28997             
28998         }, this);
28999         
29000         if(box.length){
29001             boxes.push(box);
29002             box = [];
29003         }
29004         
29005         var filterPattern = function(box, length)
29006         {
29007             if(!box.length){
29008                 return;
29009             }
29010             
29011             var match = false;
29012             
29013             var pattern = box.slice(0, length);
29014             
29015             var format = [];
29016             
29017             Roo.each(pattern, function(i){
29018                 format.push(i.size);
29019             }, this);
29020             
29021             Roo.each(standard, function(s){
29022                 
29023                 if(String(s) != String(format)){
29024                     return;
29025                 }
29026                 
29027                 match = true;
29028                 return false;
29029                 
29030             }, this);
29031             
29032             if(!match && length == 1){
29033                 return;
29034             }
29035             
29036             if(!match){
29037                 filterPattern(box, length - 1);
29038                 return;
29039             }
29040                 
29041             queue.push(pattern);
29042
29043             box = box.slice(length, box.length);
29044
29045             filterPattern(box, 4);
29046
29047             return;
29048             
29049         }
29050         
29051         Roo.each(boxes, function(box, k){
29052             
29053             if(!box.length){
29054                 return;
29055             }
29056             
29057             if(box.length == 1){
29058                 queue.push(box);
29059                 return;
29060             }
29061             
29062             filterPattern(box, 4);
29063             
29064         }, this);
29065         
29066         
29067         var prune = [];
29068         
29069         var pos = this.el.getBox(true);
29070         
29071         var minX = pos.x;
29072         
29073         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29074         
29075         var hit_end = false;
29076         
29077         Roo.each(queue, function(box){
29078             
29079             if(hit_end){
29080                 
29081                 Roo.each(box, function(b){
29082                 
29083                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29084                     b.el.hide();
29085
29086                 }, this);
29087
29088                 return;
29089             }
29090             
29091             var mx = 0;
29092             
29093             Roo.each(box, function(b){
29094                 
29095                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29096                 b.el.show();
29097
29098                 mx = Math.max(mx, b.x);
29099                 
29100             }, this);
29101             
29102             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29103             
29104             if(maxX < minX){
29105                 
29106                 Roo.each(box, function(b){
29107                 
29108                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29109                     b.el.hide();
29110                     
29111                 }, this);
29112                 
29113                 hit_end = true;
29114                 
29115                 return;
29116             }
29117             
29118             prune.push(box);
29119             
29120         }, this);
29121         
29122         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29123     },
29124     
29125     /** Sets position of item in DOM
29126     * @param {Element} item
29127     * @param {Number} x - horizontal position
29128     * @param {Number} y - vertical position
29129     * @param {Boolean} isInstant - disables transitions
29130     */
29131     _processVerticalLayoutQueue : function( queue, isInstant )
29132     {
29133         var pos = this.el.getBox(true);
29134         var x = pos.x;
29135         var y = pos.y;
29136         var maxY = [];
29137         
29138         for (var i = 0; i < this.cols; i++){
29139             maxY[i] = pos.y;
29140         }
29141         
29142         Roo.each(queue, function(box, k){
29143             
29144             var col = k % this.cols;
29145             
29146             Roo.each(box, function(b,kk){
29147                 
29148                 b.el.position('absolute');
29149                 
29150                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29151                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29152                 
29153                 if(b.size == 'md-left' || b.size == 'md-right'){
29154                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29155                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29156                 }
29157                 
29158                 b.el.setWidth(width);
29159                 b.el.setHeight(height);
29160                 // iframe?
29161                 b.el.select('iframe',true).setSize(width,height);
29162                 
29163             }, this);
29164             
29165             for (var i = 0; i < this.cols; i++){
29166                 
29167                 if(maxY[i] < maxY[col]){
29168                     col = i;
29169                     continue;
29170                 }
29171                 
29172                 col = Math.min(col, i);
29173                 
29174             }
29175             
29176             x = pos.x + col * (this.colWidth + this.padWidth);
29177             
29178             y = maxY[col];
29179             
29180             var positions = [];
29181             
29182             switch (box.length){
29183                 case 1 :
29184                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29185                     break;
29186                 case 2 :
29187                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29188                     break;
29189                 case 3 :
29190                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29191                     break;
29192                 case 4 :
29193                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29194                     break;
29195                 default :
29196                     break;
29197             }
29198             
29199             Roo.each(box, function(b,kk){
29200                 
29201                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29202                 
29203                 var sz = b.el.getSize();
29204                 
29205                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29206                 
29207             }, this);
29208             
29209         }, this);
29210         
29211         var mY = 0;
29212         
29213         for (var i = 0; i < this.cols; i++){
29214             mY = Math.max(mY, maxY[i]);
29215         }
29216         
29217         this.el.setHeight(mY - pos.y);
29218         
29219     },
29220     
29221 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29222 //    {
29223 //        var pos = this.el.getBox(true);
29224 //        var x = pos.x;
29225 //        var y = pos.y;
29226 //        var maxX = pos.right;
29227 //        
29228 //        var maxHeight = 0;
29229 //        
29230 //        Roo.each(items, function(item, k){
29231 //            
29232 //            var c = k % 2;
29233 //            
29234 //            item.el.position('absolute');
29235 //                
29236 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29237 //
29238 //            item.el.setWidth(width);
29239 //
29240 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29241 //
29242 //            item.el.setHeight(height);
29243 //            
29244 //            if(c == 0){
29245 //                item.el.setXY([x, y], isInstant ? false : true);
29246 //            } else {
29247 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29248 //            }
29249 //            
29250 //            y = y + height + this.alternativePadWidth;
29251 //            
29252 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29253 //            
29254 //        }, this);
29255 //        
29256 //        this.el.setHeight(maxHeight);
29257 //        
29258 //    },
29259     
29260     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29261     {
29262         var pos = this.el.getBox(true);
29263         
29264         var minX = pos.x;
29265         var minY = pos.y;
29266         
29267         var maxX = pos.right;
29268         
29269         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29270         
29271         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29272         
29273         Roo.each(queue, function(box, k){
29274             
29275             Roo.each(box, function(b, kk){
29276                 
29277                 b.el.position('absolute');
29278                 
29279                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29280                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29281                 
29282                 if(b.size == 'md-left' || b.size == 'md-right'){
29283                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29284                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29285                 }
29286                 
29287                 b.el.setWidth(width);
29288                 b.el.setHeight(height);
29289                 
29290             }, this);
29291             
29292             if(!box.length){
29293                 return;
29294             }
29295             
29296             var positions = [];
29297             
29298             switch (box.length){
29299                 case 1 :
29300                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29301                     break;
29302                 case 2 :
29303                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29304                     break;
29305                 case 3 :
29306                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29307                     break;
29308                 case 4 :
29309                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29310                     break;
29311                 default :
29312                     break;
29313             }
29314             
29315             Roo.each(box, function(b,kk){
29316                 
29317                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29318                 
29319                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29320                 
29321             }, this);
29322             
29323         }, this);
29324         
29325     },
29326     
29327     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29328     {
29329         Roo.each(eItems, function(b,k){
29330             
29331             b.size = (k == 0) ? 'sm' : 'xs';
29332             b.x = (k == 0) ? 2 : 1;
29333             b.y = (k == 0) ? 2 : 1;
29334             
29335             b.el.position('absolute');
29336             
29337             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29338                 
29339             b.el.setWidth(width);
29340             
29341             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29342             
29343             b.el.setHeight(height);
29344             
29345         }, this);
29346
29347         var positions = [];
29348         
29349         positions.push({
29350             x : maxX - this.unitWidth * 2 - this.gutter,
29351             y : minY
29352         });
29353         
29354         positions.push({
29355             x : maxX - this.unitWidth,
29356             y : minY + (this.unitWidth + this.gutter) * 2
29357         });
29358         
29359         positions.push({
29360             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29361             y : minY
29362         });
29363         
29364         Roo.each(eItems, function(b,k){
29365             
29366             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29367
29368         }, this);
29369         
29370     },
29371     
29372     getVerticalOneBoxColPositions : function(x, y, box)
29373     {
29374         var pos = [];
29375         
29376         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29377         
29378         if(box[0].size == 'md-left'){
29379             rand = 0;
29380         }
29381         
29382         if(box[0].size == 'md-right'){
29383             rand = 1;
29384         }
29385         
29386         pos.push({
29387             x : x + (this.unitWidth + this.gutter) * rand,
29388             y : y
29389         });
29390         
29391         return pos;
29392     },
29393     
29394     getVerticalTwoBoxColPositions : function(x, y, box)
29395     {
29396         var pos = [];
29397         
29398         if(box[0].size == 'xs'){
29399             
29400             pos.push({
29401                 x : x,
29402                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29403             });
29404
29405             pos.push({
29406                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29407                 y : y
29408             });
29409             
29410             return pos;
29411             
29412         }
29413         
29414         pos.push({
29415             x : x,
29416             y : y
29417         });
29418
29419         pos.push({
29420             x : x + (this.unitWidth + this.gutter) * 2,
29421             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29422         });
29423         
29424         return pos;
29425         
29426     },
29427     
29428     getVerticalThreeBoxColPositions : function(x, y, box)
29429     {
29430         var pos = [];
29431         
29432         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29433             
29434             pos.push({
29435                 x : x,
29436                 y : y
29437             });
29438
29439             pos.push({
29440                 x : x + (this.unitWidth + this.gutter) * 1,
29441                 y : y
29442             });
29443             
29444             pos.push({
29445                 x : x + (this.unitWidth + this.gutter) * 2,
29446                 y : y
29447             });
29448             
29449             return pos;
29450             
29451         }
29452         
29453         if(box[0].size == 'xs' && box[1].size == 'xs'){
29454             
29455             pos.push({
29456                 x : x,
29457                 y : y
29458             });
29459
29460             pos.push({
29461                 x : x,
29462                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29463             });
29464             
29465             pos.push({
29466                 x : x + (this.unitWidth + this.gutter) * 1,
29467                 y : y
29468             });
29469             
29470             return pos;
29471             
29472         }
29473         
29474         pos.push({
29475             x : x,
29476             y : y
29477         });
29478
29479         pos.push({
29480             x : x + (this.unitWidth + this.gutter) * 2,
29481             y : y
29482         });
29483
29484         pos.push({
29485             x : x + (this.unitWidth + this.gutter) * 2,
29486             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29487         });
29488             
29489         return pos;
29490         
29491     },
29492     
29493     getVerticalFourBoxColPositions : function(x, y, box)
29494     {
29495         var pos = [];
29496         
29497         if(box[0].size == 'xs'){
29498             
29499             pos.push({
29500                 x : x,
29501                 y : y
29502             });
29503
29504             pos.push({
29505                 x : x,
29506                 y : y + (this.unitHeight + this.gutter) * 1
29507             });
29508             
29509             pos.push({
29510                 x : x,
29511                 y : y + (this.unitHeight + this.gutter) * 2
29512             });
29513             
29514             pos.push({
29515                 x : x + (this.unitWidth + this.gutter) * 1,
29516                 y : y
29517             });
29518             
29519             return pos;
29520             
29521         }
29522         
29523         pos.push({
29524             x : x,
29525             y : y
29526         });
29527
29528         pos.push({
29529             x : x + (this.unitWidth + this.gutter) * 2,
29530             y : y
29531         });
29532
29533         pos.push({
29534             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29535             y : y + (this.unitHeight + this.gutter) * 1
29536         });
29537
29538         pos.push({
29539             x : x + (this.unitWidth + this.gutter) * 2,
29540             y : y + (this.unitWidth + this.gutter) * 2
29541         });
29542
29543         return pos;
29544         
29545     },
29546     
29547     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29548     {
29549         var pos = [];
29550         
29551         if(box[0].size == 'md-left'){
29552             pos.push({
29553                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29554                 y : minY
29555             });
29556             
29557             return pos;
29558         }
29559         
29560         if(box[0].size == 'md-right'){
29561             pos.push({
29562                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29563                 y : minY + (this.unitWidth + this.gutter) * 1
29564             });
29565             
29566             return pos;
29567         }
29568         
29569         var rand = Math.floor(Math.random() * (4 - box[0].y));
29570         
29571         pos.push({
29572             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29573             y : minY + (this.unitWidth + this.gutter) * rand
29574         });
29575         
29576         return pos;
29577         
29578     },
29579     
29580     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29581     {
29582         var pos = [];
29583         
29584         if(box[0].size == 'xs'){
29585             
29586             pos.push({
29587                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29588                 y : minY
29589             });
29590
29591             pos.push({
29592                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29593                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29594             });
29595             
29596             return pos;
29597             
29598         }
29599         
29600         pos.push({
29601             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29602             y : minY
29603         });
29604
29605         pos.push({
29606             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29607             y : minY + (this.unitWidth + this.gutter) * 2
29608         });
29609         
29610         return pos;
29611         
29612     },
29613     
29614     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29615     {
29616         var pos = [];
29617         
29618         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29619             
29620             pos.push({
29621                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29622                 y : minY
29623             });
29624
29625             pos.push({
29626                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29627                 y : minY + (this.unitWidth + this.gutter) * 1
29628             });
29629             
29630             pos.push({
29631                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29632                 y : minY + (this.unitWidth + this.gutter) * 2
29633             });
29634             
29635             return pos;
29636             
29637         }
29638         
29639         if(box[0].size == 'xs' && box[1].size == 'xs'){
29640             
29641             pos.push({
29642                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29643                 y : minY
29644             });
29645
29646             pos.push({
29647                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29648                 y : minY
29649             });
29650             
29651             pos.push({
29652                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29653                 y : minY + (this.unitWidth + this.gutter) * 1
29654             });
29655             
29656             return pos;
29657             
29658         }
29659         
29660         pos.push({
29661             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29662             y : minY
29663         });
29664
29665         pos.push({
29666             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29667             y : minY + (this.unitWidth + this.gutter) * 2
29668         });
29669
29670         pos.push({
29671             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29672             y : minY + (this.unitWidth + this.gutter) * 2
29673         });
29674             
29675         return pos;
29676         
29677     },
29678     
29679     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29680     {
29681         var pos = [];
29682         
29683         if(box[0].size == 'xs'){
29684             
29685             pos.push({
29686                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29687                 y : minY
29688             });
29689
29690             pos.push({
29691                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29692                 y : minY
29693             });
29694             
29695             pos.push({
29696                 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),
29697                 y : minY
29698             });
29699             
29700             pos.push({
29701                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29702                 y : minY + (this.unitWidth + this.gutter) * 1
29703             });
29704             
29705             return pos;
29706             
29707         }
29708         
29709         pos.push({
29710             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29711             y : minY
29712         });
29713         
29714         pos.push({
29715             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29716             y : minY + (this.unitWidth + this.gutter) * 2
29717         });
29718         
29719         pos.push({
29720             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29721             y : minY + (this.unitWidth + this.gutter) * 2
29722         });
29723         
29724         pos.push({
29725             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),
29726             y : minY + (this.unitWidth + this.gutter) * 2
29727         });
29728
29729         return pos;
29730         
29731     }
29732     
29733 });
29734
29735  
29736
29737  /**
29738  *
29739  * This is based on 
29740  * http://masonry.desandro.com
29741  *
29742  * The idea is to render all the bricks based on vertical width...
29743  *
29744  * The original code extends 'outlayer' - we might need to use that....
29745  * 
29746  */
29747
29748
29749 /**
29750  * @class Roo.bootstrap.LayoutMasonryAuto
29751  * @extends Roo.bootstrap.Component
29752  * Bootstrap Layout Masonry class
29753  * 
29754  * @constructor
29755  * Create a new Element
29756  * @param {Object} config The config object
29757  */
29758
29759 Roo.bootstrap.LayoutMasonryAuto = function(config){
29760     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29761 };
29762
29763 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
29764     
29765       /**
29766      * @cfg {Boolean} isFitWidth  - resize the width..
29767      */   
29768     isFitWidth : false,  // options..
29769     /**
29770      * @cfg {Boolean} isOriginLeft = left align?
29771      */   
29772     isOriginLeft : true,
29773     /**
29774      * @cfg {Boolean} isOriginTop = top align?
29775      */   
29776     isOriginTop : false,
29777     /**
29778      * @cfg {Boolean} isLayoutInstant = no animation?
29779      */   
29780     isLayoutInstant : false, // needed?
29781     /**
29782      * @cfg {Boolean} isResizingContainer = not sure if this is used..
29783      */   
29784     isResizingContainer : true,
29785     /**
29786      * @cfg {Number} columnWidth  width of the columns 
29787      */   
29788     
29789     columnWidth : 0,
29790     
29791     /**
29792      * @cfg {Number} maxCols maximum number of columns
29793      */   
29794     
29795     maxCols: 0,
29796     /**
29797      * @cfg {Number} padHeight padding below box..
29798      */   
29799     
29800     padHeight : 10, 
29801     
29802     /**
29803      * @cfg {Boolean} isAutoInitial defalut true
29804      */   
29805     
29806     isAutoInitial : true, 
29807     
29808     // private?
29809     gutter : 0,
29810     
29811     containerWidth: 0,
29812     initialColumnWidth : 0,
29813     currentSize : null,
29814     
29815     colYs : null, // array.
29816     maxY : 0,
29817     padWidth: 10,
29818     
29819     
29820     tag: 'div',
29821     cls: '',
29822     bricks: null, //CompositeElement
29823     cols : 0, // array?
29824     // element : null, // wrapped now this.el
29825     _isLayoutInited : null, 
29826     
29827     
29828     getAutoCreate : function(){
29829         
29830         var cfg = {
29831             tag: this.tag,
29832             cls: 'blog-masonary-wrapper ' + this.cls,
29833             cn : {
29834                 cls : 'mas-boxes masonary'
29835             }
29836         };
29837         
29838         return cfg;
29839     },
29840     
29841     getChildContainer: function( )
29842     {
29843         if (this.boxesEl) {
29844             return this.boxesEl;
29845         }
29846         
29847         this.boxesEl = this.el.select('.mas-boxes').first();
29848         
29849         return this.boxesEl;
29850     },
29851     
29852     
29853     initEvents : function()
29854     {
29855         var _this = this;
29856         
29857         if(this.isAutoInitial){
29858             Roo.log('hook children rendered');
29859             this.on('childrenrendered', function() {
29860                 Roo.log('children rendered');
29861                 _this.initial();
29862             } ,this);
29863         }
29864         
29865     },
29866     
29867     initial : function()
29868     {
29869         this.reloadItems();
29870
29871         this.currentSize = this.el.getBox(true);
29872
29873         /// was window resize... - let's see if this works..
29874         Roo.EventManager.onWindowResize(this.resize, this); 
29875
29876         if(!this.isAutoInitial){
29877             this.layout();
29878             return;
29879         }
29880         
29881         this.layout.defer(500,this);
29882     },
29883     
29884     reloadItems: function()
29885     {
29886         this.bricks = this.el.select('.masonry-brick', true);
29887         
29888         this.bricks.each(function(b) {
29889             //Roo.log(b.getSize());
29890             if (!b.attr('originalwidth')) {
29891                 b.attr('originalwidth',  b.getSize().width);
29892             }
29893             
29894         });
29895         
29896         Roo.log(this.bricks.elements.length);
29897     },
29898     
29899     resize : function()
29900     {
29901         Roo.log('resize');
29902         var cs = this.el.getBox(true);
29903         
29904         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29905             Roo.log("no change in with or X");
29906             return;
29907         }
29908         this.currentSize = cs;
29909         this.layout();
29910     },
29911     
29912     layout : function()
29913     {
29914          Roo.log('layout');
29915         this._resetLayout();
29916         //this._manageStamps();
29917       
29918         // don't animate first layout
29919         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29920         this.layoutItems( isInstant );
29921       
29922         // flag for initalized
29923         this._isLayoutInited = true;
29924     },
29925     
29926     layoutItems : function( isInstant )
29927     {
29928         //var items = this._getItemsForLayout( this.items );
29929         // original code supports filtering layout items.. we just ignore it..
29930         
29931         this._layoutItems( this.bricks , isInstant );
29932       
29933         this._postLayout();
29934     },
29935     _layoutItems : function ( items , isInstant)
29936     {
29937        //this.fireEvent( 'layout', this, items );
29938     
29939
29940         if ( !items || !items.elements.length ) {
29941           // no items, emit event with empty array
29942             return;
29943         }
29944
29945         var queue = [];
29946         items.each(function(item) {
29947             Roo.log("layout item");
29948             Roo.log(item);
29949             // get x/y object from method
29950             var position = this._getItemLayoutPosition( item );
29951             // enqueue
29952             position.item = item;
29953             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29954             queue.push( position );
29955         }, this);
29956       
29957         this._processLayoutQueue( queue );
29958     },
29959     /** Sets position of item in DOM
29960     * @param {Element} item
29961     * @param {Number} x - horizontal position
29962     * @param {Number} y - vertical position
29963     * @param {Boolean} isInstant - disables transitions
29964     */
29965     _processLayoutQueue : function( queue )
29966     {
29967         for ( var i=0, len = queue.length; i < len; i++ ) {
29968             var obj = queue[i];
29969             obj.item.position('absolute');
29970             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29971         }
29972     },
29973       
29974     
29975     /**
29976     * Any logic you want to do after each layout,
29977     * i.e. size the container
29978     */
29979     _postLayout : function()
29980     {
29981         this.resizeContainer();
29982     },
29983     
29984     resizeContainer : function()
29985     {
29986         if ( !this.isResizingContainer ) {
29987             return;
29988         }
29989         var size = this._getContainerSize();
29990         if ( size ) {
29991             this.el.setSize(size.width,size.height);
29992             this.boxesEl.setSize(size.width,size.height);
29993         }
29994     },
29995     
29996     
29997     
29998     _resetLayout : function()
29999     {
30000         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30001         this.colWidth = this.el.getWidth();
30002         //this.gutter = this.el.getWidth(); 
30003         
30004         this.measureColumns();
30005
30006         // reset column Y
30007         var i = this.cols;
30008         this.colYs = [];
30009         while (i--) {
30010             this.colYs.push( 0 );
30011         }
30012     
30013         this.maxY = 0;
30014     },
30015
30016     measureColumns : function()
30017     {
30018         this.getContainerWidth();
30019       // if columnWidth is 0, default to outerWidth of first item
30020         if ( !this.columnWidth ) {
30021             var firstItem = this.bricks.first();
30022             Roo.log(firstItem);
30023             this.columnWidth  = this.containerWidth;
30024             if (firstItem && firstItem.attr('originalwidth') ) {
30025                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30026             }
30027             // columnWidth fall back to item of first element
30028             Roo.log("set column width?");
30029                         this.initialColumnWidth = this.columnWidth  ;
30030
30031             // if first elem has no width, default to size of container
30032             
30033         }
30034         
30035         
30036         if (this.initialColumnWidth) {
30037             this.columnWidth = this.initialColumnWidth;
30038         }
30039         
30040         
30041             
30042         // column width is fixed at the top - however if container width get's smaller we should
30043         // reduce it...
30044         
30045         // this bit calcs how man columns..
30046             
30047         var columnWidth = this.columnWidth += this.gutter;
30048       
30049         // calculate columns
30050         var containerWidth = this.containerWidth + this.gutter;
30051         
30052         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30053         // fix rounding errors, typically with gutters
30054         var excess = columnWidth - containerWidth % columnWidth;
30055         
30056         
30057         // if overshoot is less than a pixel, round up, otherwise floor it
30058         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30059         cols = Math[ mathMethod ]( cols );
30060         this.cols = Math.max( cols, 1 );
30061         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30062         
30063          // padding positioning..
30064         var totalColWidth = this.cols * this.columnWidth;
30065         var padavail = this.containerWidth - totalColWidth;
30066         // so for 2 columns - we need 3 'pads'
30067         
30068         var padNeeded = (1+this.cols) * this.padWidth;
30069         
30070         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30071         
30072         this.columnWidth += padExtra
30073         //this.padWidth = Math.floor(padavail /  ( this.cols));
30074         
30075         // adjust colum width so that padding is fixed??
30076         
30077         // we have 3 columns ... total = width * 3
30078         // we have X left over... that should be used by 
30079         
30080         //if (this.expandC) {
30081             
30082         //}
30083         
30084         
30085         
30086     },
30087     
30088     getContainerWidth : function()
30089     {
30090        /* // container is parent if fit width
30091         var container = this.isFitWidth ? this.element.parentNode : this.element;
30092         // check that this.size and size are there
30093         // IE8 triggers resize on body size change, so they might not be
30094         
30095         var size = getSize( container );  //FIXME
30096         this.containerWidth = size && size.innerWidth; //FIXME
30097         */
30098          
30099         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30100         
30101     },
30102     
30103     _getItemLayoutPosition : function( item )  // what is item?
30104     {
30105         // we resize the item to our columnWidth..
30106       
30107         item.setWidth(this.columnWidth);
30108         item.autoBoxAdjust  = false;
30109         
30110         var sz = item.getSize();
30111  
30112         // how many columns does this brick span
30113         var remainder = this.containerWidth % this.columnWidth;
30114         
30115         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30116         // round if off by 1 pixel, otherwise use ceil
30117         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30118         colSpan = Math.min( colSpan, this.cols );
30119         
30120         // normally this should be '1' as we dont' currently allow multi width columns..
30121         
30122         var colGroup = this._getColGroup( colSpan );
30123         // get the minimum Y value from the columns
30124         var minimumY = Math.min.apply( Math, colGroup );
30125         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30126         
30127         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30128          
30129         // position the brick
30130         var position = {
30131             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30132             y: this.currentSize.y + minimumY + this.padHeight
30133         };
30134         
30135         Roo.log(position);
30136         // apply setHeight to necessary columns
30137         var setHeight = minimumY + sz.height + this.padHeight;
30138         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30139         
30140         var setSpan = this.cols + 1 - colGroup.length;
30141         for ( var i = 0; i < setSpan; i++ ) {
30142           this.colYs[ shortColIndex + i ] = setHeight ;
30143         }
30144       
30145         return position;
30146     },
30147     
30148     /**
30149      * @param {Number} colSpan - number of columns the element spans
30150      * @returns {Array} colGroup
30151      */
30152     _getColGroup : function( colSpan )
30153     {
30154         if ( colSpan < 2 ) {
30155           // if brick spans only one column, use all the column Ys
30156           return this.colYs;
30157         }
30158       
30159         var colGroup = [];
30160         // how many different places could this brick fit horizontally
30161         var groupCount = this.cols + 1 - colSpan;
30162         // for each group potential horizontal position
30163         for ( var i = 0; i < groupCount; i++ ) {
30164           // make an array of colY values for that one group
30165           var groupColYs = this.colYs.slice( i, i + colSpan );
30166           // and get the max value of the array
30167           colGroup[i] = Math.max.apply( Math, groupColYs );
30168         }
30169         return colGroup;
30170     },
30171     /*
30172     _manageStamp : function( stamp )
30173     {
30174         var stampSize =  stamp.getSize();
30175         var offset = stamp.getBox();
30176         // get the columns that this stamp affects
30177         var firstX = this.isOriginLeft ? offset.x : offset.right;
30178         var lastX = firstX + stampSize.width;
30179         var firstCol = Math.floor( firstX / this.columnWidth );
30180         firstCol = Math.max( 0, firstCol );
30181         
30182         var lastCol = Math.floor( lastX / this.columnWidth );
30183         // lastCol should not go over if multiple of columnWidth #425
30184         lastCol -= lastX % this.columnWidth ? 0 : 1;
30185         lastCol = Math.min( this.cols - 1, lastCol );
30186         
30187         // set colYs to bottom of the stamp
30188         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30189             stampSize.height;
30190             
30191         for ( var i = firstCol; i <= lastCol; i++ ) {
30192           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30193         }
30194     },
30195     */
30196     
30197     _getContainerSize : function()
30198     {
30199         this.maxY = Math.max.apply( Math, this.colYs );
30200         var size = {
30201             height: this.maxY
30202         };
30203       
30204         if ( this.isFitWidth ) {
30205             size.width = this._getContainerFitWidth();
30206         }
30207       
30208         return size;
30209     },
30210     
30211     _getContainerFitWidth : function()
30212     {
30213         var unusedCols = 0;
30214         // count unused columns
30215         var i = this.cols;
30216         while ( --i ) {
30217           if ( this.colYs[i] !== 0 ) {
30218             break;
30219           }
30220           unusedCols++;
30221         }
30222         // fit container to columns that have been used
30223         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30224     },
30225     
30226     needsResizeLayout : function()
30227     {
30228         var previousWidth = this.containerWidth;
30229         this.getContainerWidth();
30230         return previousWidth !== this.containerWidth;
30231     }
30232  
30233 });
30234
30235  
30236
30237  /*
30238  * - LGPL
30239  *
30240  * element
30241  * 
30242  */
30243
30244 /**
30245  * @class Roo.bootstrap.MasonryBrick
30246  * @extends Roo.bootstrap.Component
30247  * Bootstrap MasonryBrick class
30248  * 
30249  * @constructor
30250  * Create a new MasonryBrick
30251  * @param {Object} config The config object
30252  */
30253
30254 Roo.bootstrap.MasonryBrick = function(config){
30255     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30256     
30257     this.addEvents({
30258         // raw events
30259         /**
30260          * @event click
30261          * When a MasonryBrick is clcik
30262          * @param {Roo.bootstrap.MasonryBrick} this
30263          * @param {Roo.EventObject} e
30264          */
30265         "click" : true
30266     });
30267 };
30268
30269 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30270     
30271     /**
30272      * @cfg {String} title
30273      */   
30274     title : '',
30275     /**
30276      * @cfg {String} html
30277      */   
30278     html : '',
30279     /**
30280      * @cfg {String} bgimage
30281      */   
30282     bgimage : '',
30283     /**
30284      * @cfg {String} videourl
30285      */   
30286     videourl : '',
30287     /**
30288      * @cfg {String} cls
30289      */   
30290     cls : '',
30291     /**
30292      * @cfg {String} href
30293      */   
30294     href : '',
30295     /**
30296      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30297      */   
30298     size : 'xs',
30299     
30300     /**
30301      * @cfg {String} (center|bottom) placetitle
30302      */   
30303     placetitle : '',
30304     
30305     getAutoCreate : function()
30306     {
30307         var cls = 'masonry-brick';
30308         
30309         if(this.href.length){
30310             cls += ' masonry-brick-link';
30311         }
30312         
30313         if(this.bgimage.length){
30314             cls += ' masonry-brick-image';
30315         }
30316         
30317         if(this.size){
30318             cls += ' masonry-' + this.size + '-brick';
30319         }
30320         
30321         if(this.placetitle.length){
30322             
30323             switch (this.placetitle) {
30324                 case 'center' :
30325                     cls += ' masonry-center-title';
30326                     break;
30327                 case 'bottom' :
30328                     cls += ' masonry-bottom-title';
30329                     break;
30330                 default:
30331                     break;
30332             }
30333             
30334         } else {
30335             if(!this.html.length && !this.bgimage.length){
30336                 cls += ' masonry-center-title';
30337             }
30338
30339             if(!this.html.length && this.bgimage.length){
30340                 cls += ' masonry-bottom-title';
30341             }
30342         }
30343         
30344         if(this.cls){
30345             cls += ' ' + this.cls;
30346         }
30347         
30348         var cfg = {
30349             tag: (this.href.length) ? 'a' : 'div',
30350             cls: cls,
30351             cn: [
30352                 {
30353                     tag: 'div',
30354                     cls: 'masonry-brick-paragraph',
30355                     cn: []
30356                 }
30357             ]
30358         };
30359         
30360         if(this.href.length){
30361             cfg.href = this.href;
30362         }
30363         
30364         var cn = cfg.cn[0].cn;
30365         
30366         if(this.title.length){
30367             cn.push({
30368                 tag: 'h4',
30369                 cls: 'masonry-brick-title',
30370                 html: this.title
30371             });
30372         }
30373         
30374         if(this.html.length){
30375             cn.push({
30376                 tag: 'p',
30377                 cls: 'masonry-brick-text',
30378                 html: this.html
30379             });
30380         }  
30381         if (!this.title.length && !this.html.length) {
30382             cfg.cn[0].cls += ' hide';
30383         }
30384         
30385         if(this.bgimage.length){
30386             cfg.cn.push({
30387                 tag: 'img',
30388                 cls: 'masonry-brick-image-view',
30389                 src: this.bgimage
30390             });
30391         }
30392         if(this.videourl.length){
30393             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30394             // youtube support only?
30395             cfg.cn.push({
30396                 tag: 'iframe',
30397                 cls: 'masonry-brick-image-view',
30398                 src: vurl,
30399                 frameborder : 0,
30400                 allowfullscreen : true
30401             });
30402             
30403             
30404         }
30405         return cfg;
30406         
30407     },
30408     
30409     initEvents: function() 
30410     {
30411         switch (this.size) {
30412             case 'xs' :
30413 //                this.intSize = 1;
30414                 this.x = 1;
30415                 this.y = 1;
30416                 break;
30417             case 'sm' :
30418 //                this.intSize = 2;
30419                 this.x = 2;
30420                 this.y = 2;
30421                 break;
30422             case 'md' :
30423             case 'md-left' :
30424             case 'md-right' :
30425 //                this.intSize = 3;
30426                 this.x = 3;
30427                 this.y = 3;
30428                 break;
30429             case 'tall' :
30430 //                this.intSize = 3;
30431                 this.x = 2;
30432                 this.y = 3;
30433                 break;
30434             case 'wide' :
30435 //                this.intSize = 3;
30436                 this.x = 3;
30437                 this.y = 2;
30438                 break;
30439             case 'wide-thin' :
30440 //                this.intSize = 3;
30441                 this.x = 3;
30442                 this.y = 1;
30443                 break;
30444                         
30445             default :
30446                 break;
30447         }
30448         
30449         
30450         
30451         if(Roo.isTouch){
30452             this.el.on('touchstart', this.onTouchStart, this);
30453             this.el.on('touchmove', this.onTouchMove, this);
30454             this.el.on('touchend', this.onTouchEnd, this);
30455             this.el.on('contextmenu', this.onContextMenu, this);
30456         } else {
30457             this.el.on('mouseenter'  ,this.enter, this);
30458             this.el.on('mouseleave', this.leave, this);
30459         }
30460         
30461         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30462             this.parent().bricks.push(this);   
30463         }
30464         
30465     },
30466     
30467     onClick: function(e, el)
30468     {
30469         if(!Roo.isTouch){
30470             return;
30471         }
30472         
30473         var time = this.endTimer - this.startTimer;
30474         
30475         //alert(time);
30476         
30477         if(time < 1000){
30478             return;
30479         }
30480         
30481         e.preventDefault();
30482     },
30483     
30484     enter: function(e, el)
30485     {
30486         e.preventDefault();
30487         
30488         if(this.bgimage.length && this.html.length){
30489             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30490         }
30491     },
30492     
30493     leave: function(e, el)
30494     {
30495         e.preventDefault();
30496         
30497         if(this.bgimage.length && this.html.length){
30498             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30499         }
30500     },
30501     
30502     onTouchStart: function(e, el)
30503     {
30504 //        e.preventDefault();
30505         
30506         this.touchmoved = false;
30507         
30508         if(!this.bgimage.length || !this.html.length){
30509             return;
30510         }
30511         
30512         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30513         
30514         this.timer = new Date().getTime();
30515         
30516     },
30517     
30518     onTouchMove: function(e, el)
30519     {
30520         this.touchmoved = true;
30521     },
30522     
30523     onContextMenu : function(e,el)
30524     {
30525         e.preventDefault();
30526         e.stopPropagation();
30527         return false;
30528     },
30529     
30530     onTouchEnd: function(e, el)
30531     {
30532 //        e.preventDefault();
30533         
30534         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30535         
30536             this.leave(e,el);
30537             
30538             return;
30539         }
30540         
30541         if(!this.bgimage.length || !this.html.length){
30542             
30543             if(this.href.length){
30544                 window.location.href = this.href;
30545             }
30546             
30547             return;
30548         }
30549         
30550         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30551         
30552         window.location.href = this.href;
30553     }
30554     
30555 });
30556
30557  
30558
30559  /*
30560  * - LGPL
30561  *
30562  * element
30563  * 
30564  */
30565
30566 /**
30567  * @class Roo.bootstrap.Brick
30568  * @extends Roo.bootstrap.Component
30569  * Bootstrap Brick class
30570  * 
30571  * @constructor
30572  * Create a new Brick
30573  * @param {Object} config The config object
30574  */
30575
30576 Roo.bootstrap.Brick = function(config){
30577     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30578     
30579     this.addEvents({
30580         // raw events
30581         /**
30582          * @event click
30583          * When a Brick is click
30584          * @param {Roo.bootstrap.Brick} this
30585          * @param {Roo.EventObject} e
30586          */
30587         "click" : true
30588     });
30589 };
30590
30591 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30592     
30593     /**
30594      * @cfg {String} title
30595      */   
30596     title : '',
30597     /**
30598      * @cfg {String} html
30599      */   
30600     html : '',
30601     /**
30602      * @cfg {String} bgimage
30603      */   
30604     bgimage : '',
30605     /**
30606      * @cfg {String} cls
30607      */   
30608     cls : '',
30609     /**
30610      * @cfg {String} href
30611      */   
30612     href : '',
30613     /**
30614      * @cfg {String} video
30615      */   
30616     video : '',
30617     /**
30618      * @cfg {Boolean} square
30619      */   
30620     square : true,
30621     
30622     getAutoCreate : function()
30623     {
30624         var cls = 'roo-brick';
30625         
30626         if(this.href.length){
30627             cls += ' roo-brick-link';
30628         }
30629         
30630         if(this.bgimage.length){
30631             cls += ' roo-brick-image';
30632         }
30633         
30634         if(!this.html.length && !this.bgimage.length){
30635             cls += ' roo-brick-center-title';
30636         }
30637         
30638         if(!this.html.length && this.bgimage.length){
30639             cls += ' roo-brick-bottom-title';
30640         }
30641         
30642         if(this.cls){
30643             cls += ' ' + this.cls;
30644         }
30645         
30646         var cfg = {
30647             tag: (this.href.length) ? 'a' : 'div',
30648             cls: cls,
30649             cn: [
30650                 {
30651                     tag: 'div',
30652                     cls: 'roo-brick-paragraph',
30653                     cn: []
30654                 }
30655             ]
30656         };
30657         
30658         if(this.href.length){
30659             cfg.href = this.href;
30660         }
30661         
30662         var cn = cfg.cn[0].cn;
30663         
30664         if(this.title.length){
30665             cn.push({
30666                 tag: 'h4',
30667                 cls: 'roo-brick-title',
30668                 html: this.title
30669             });
30670         }
30671         
30672         if(this.html.length){
30673             cn.push({
30674                 tag: 'p',
30675                 cls: 'roo-brick-text',
30676                 html: this.html
30677             });
30678         } else {
30679             cn.cls += ' hide';
30680         }
30681         
30682         if(this.bgimage.length){
30683             cfg.cn.push({
30684                 tag: 'img',
30685                 cls: 'roo-brick-image-view',
30686                 src: this.bgimage
30687             });
30688         }
30689         
30690         return cfg;
30691     },
30692     
30693     initEvents: function() 
30694     {
30695         if(this.title.length || this.html.length){
30696             this.el.on('mouseenter'  ,this.enter, this);
30697             this.el.on('mouseleave', this.leave, this);
30698         }
30699         
30700         
30701         Roo.EventManager.onWindowResize(this.resize, this); 
30702         
30703         this.resize();
30704     },
30705     
30706     resize : function()
30707     {
30708         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30709         
30710         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30711 //        paragraph.setHeight(paragraph.getWidth());
30712         
30713         if(this.bgimage.length){
30714             var image = this.el.select('.roo-brick-image-view', true).first();
30715             image.setWidth(paragraph.getWidth());
30716             image.setHeight(paragraph.getWidth());
30717         }
30718         
30719     },
30720     
30721     enter: function(e, el)
30722     {
30723         e.preventDefault();
30724         
30725         if(this.bgimage.length){
30726             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30727             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30728         }
30729     },
30730     
30731     leave: function(e, el)
30732     {
30733         e.preventDefault();
30734         
30735         if(this.bgimage.length){
30736             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30737             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30738         }
30739     }
30740     
30741 });
30742
30743  
30744
30745  /*
30746  * Based on:
30747  * Ext JS Library 1.1.1
30748  * Copyright(c) 2006-2007, Ext JS, LLC.
30749  *
30750  * Originally Released Under LGPL - original licence link has changed is not relivant.
30751  *
30752  * Fork - LGPL
30753  * <script type="text/javascript">
30754  */
30755
30756
30757 /**
30758  * @class Roo.bootstrap.SplitBar
30759  * @extends Roo.util.Observable
30760  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30761  * <br><br>
30762  * Usage:
30763  * <pre><code>
30764 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30765                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30766 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30767 split.minSize = 100;
30768 split.maxSize = 600;
30769 split.animate = true;
30770 split.on('moved', splitterMoved);
30771 </code></pre>
30772  * @constructor
30773  * Create a new SplitBar
30774  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
30775  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
30776  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30777  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
30778                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30779                         position of the SplitBar).
30780  */
30781 Roo.bootstrap.SplitBar = function(cfg){
30782     
30783     /** @private */
30784     
30785     //{
30786     //  dragElement : elm
30787     //  resizingElement: el,
30788         // optional..
30789     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30790     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
30791         // existingProxy ???
30792     //}
30793     
30794     this.el = Roo.get(cfg.dragElement, true);
30795     this.el.dom.unselectable = "on";
30796     /** @private */
30797     this.resizingEl = Roo.get(cfg.resizingElement, true);
30798
30799     /**
30800      * @private
30801      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30802      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30803      * @type Number
30804      */
30805     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30806     
30807     /**
30808      * The minimum size of the resizing element. (Defaults to 0)
30809      * @type Number
30810      */
30811     this.minSize = 0;
30812     
30813     /**
30814      * The maximum size of the resizing element. (Defaults to 2000)
30815      * @type Number
30816      */
30817     this.maxSize = 2000;
30818     
30819     /**
30820      * Whether to animate the transition to the new size
30821      * @type Boolean
30822      */
30823     this.animate = false;
30824     
30825     /**
30826      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30827      * @type Boolean
30828      */
30829     this.useShim = false;
30830     
30831     /** @private */
30832     this.shim = null;
30833     
30834     if(!cfg.existingProxy){
30835         /** @private */
30836         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30837     }else{
30838         this.proxy = Roo.get(cfg.existingProxy).dom;
30839     }
30840     /** @private */
30841     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30842     
30843     /** @private */
30844     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30845     
30846     /** @private */
30847     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30848     
30849     /** @private */
30850     this.dragSpecs = {};
30851     
30852     /**
30853      * @private The adapter to use to positon and resize elements
30854      */
30855     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30856     this.adapter.init(this);
30857     
30858     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30859         /** @private */
30860         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30861         this.el.addClass("roo-splitbar-h");
30862     }else{
30863         /** @private */
30864         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30865         this.el.addClass("roo-splitbar-v");
30866     }
30867     
30868     this.addEvents({
30869         /**
30870          * @event resize
30871          * Fires when the splitter is moved (alias for {@link #event-moved})
30872          * @param {Roo.bootstrap.SplitBar} this
30873          * @param {Number} newSize the new width or height
30874          */
30875         "resize" : true,
30876         /**
30877          * @event moved
30878          * Fires when the splitter is moved
30879          * @param {Roo.bootstrap.SplitBar} this
30880          * @param {Number} newSize the new width or height
30881          */
30882         "moved" : true,
30883         /**
30884          * @event beforeresize
30885          * Fires before the splitter is dragged
30886          * @param {Roo.bootstrap.SplitBar} this
30887          */
30888         "beforeresize" : true,
30889
30890         "beforeapply" : true
30891     });
30892
30893     Roo.util.Observable.call(this);
30894 };
30895
30896 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30897     onStartProxyDrag : function(x, y){
30898         this.fireEvent("beforeresize", this);
30899         if(!this.overlay){
30900             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
30901             o.unselectable();
30902             o.enableDisplayMode("block");
30903             // all splitbars share the same overlay
30904             Roo.bootstrap.SplitBar.prototype.overlay = o;
30905         }
30906         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30907         this.overlay.show();
30908         Roo.get(this.proxy).setDisplayed("block");
30909         var size = this.adapter.getElementSize(this);
30910         this.activeMinSize = this.getMinimumSize();;
30911         this.activeMaxSize = this.getMaximumSize();;
30912         var c1 = size - this.activeMinSize;
30913         var c2 = Math.max(this.activeMaxSize - size, 0);
30914         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30915             this.dd.resetConstraints();
30916             this.dd.setXConstraint(
30917                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
30918                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30919             );
30920             this.dd.setYConstraint(0, 0);
30921         }else{
30922             this.dd.resetConstraints();
30923             this.dd.setXConstraint(0, 0);
30924             this.dd.setYConstraint(
30925                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
30926                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30927             );
30928          }
30929         this.dragSpecs.startSize = size;
30930         this.dragSpecs.startPoint = [x, y];
30931         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30932     },
30933     
30934     /** 
30935      * @private Called after the drag operation by the DDProxy
30936      */
30937     onEndProxyDrag : function(e){
30938         Roo.get(this.proxy).setDisplayed(false);
30939         var endPoint = Roo.lib.Event.getXY(e);
30940         if(this.overlay){
30941             this.overlay.hide();
30942         }
30943         var newSize;
30944         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30945             newSize = this.dragSpecs.startSize + 
30946                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30947                     endPoint[0] - this.dragSpecs.startPoint[0] :
30948                     this.dragSpecs.startPoint[0] - endPoint[0]
30949                 );
30950         }else{
30951             newSize = this.dragSpecs.startSize + 
30952                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30953                     endPoint[1] - this.dragSpecs.startPoint[1] :
30954                     this.dragSpecs.startPoint[1] - endPoint[1]
30955                 );
30956         }
30957         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30958         if(newSize != this.dragSpecs.startSize){
30959             if(this.fireEvent('beforeapply', this, newSize) !== false){
30960                 this.adapter.setElementSize(this, newSize);
30961                 this.fireEvent("moved", this, newSize);
30962                 this.fireEvent("resize", this, newSize);
30963             }
30964         }
30965     },
30966     
30967     /**
30968      * Get the adapter this SplitBar uses
30969      * @return The adapter object
30970      */
30971     getAdapter : function(){
30972         return this.adapter;
30973     },
30974     
30975     /**
30976      * Set the adapter this SplitBar uses
30977      * @param {Object} adapter A SplitBar adapter object
30978      */
30979     setAdapter : function(adapter){
30980         this.adapter = adapter;
30981         this.adapter.init(this);
30982     },
30983     
30984     /**
30985      * Gets the minimum size for the resizing element
30986      * @return {Number} The minimum size
30987      */
30988     getMinimumSize : function(){
30989         return this.minSize;
30990     },
30991     
30992     /**
30993      * Sets the minimum size for the resizing element
30994      * @param {Number} minSize The minimum size
30995      */
30996     setMinimumSize : function(minSize){
30997         this.minSize = minSize;
30998     },
30999     
31000     /**
31001      * Gets the maximum size for the resizing element
31002      * @return {Number} The maximum size
31003      */
31004     getMaximumSize : function(){
31005         return this.maxSize;
31006     },
31007     
31008     /**
31009      * Sets the maximum size for the resizing element
31010      * @param {Number} maxSize The maximum size
31011      */
31012     setMaximumSize : function(maxSize){
31013         this.maxSize = maxSize;
31014     },
31015     
31016     /**
31017      * Sets the initialize size for the resizing element
31018      * @param {Number} size The initial size
31019      */
31020     setCurrentSize : function(size){
31021         var oldAnimate = this.animate;
31022         this.animate = false;
31023         this.adapter.setElementSize(this, size);
31024         this.animate = oldAnimate;
31025     },
31026     
31027     /**
31028      * Destroy this splitbar. 
31029      * @param {Boolean} removeEl True to remove the element
31030      */
31031     destroy : function(removeEl){
31032         if(this.shim){
31033             this.shim.remove();
31034         }
31035         this.dd.unreg();
31036         this.proxy.parentNode.removeChild(this.proxy);
31037         if(removeEl){
31038             this.el.remove();
31039         }
31040     }
31041 });
31042
31043 /**
31044  * @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.
31045  */
31046 Roo.bootstrap.SplitBar.createProxy = function(dir){
31047     var proxy = new Roo.Element(document.createElement("div"));
31048     proxy.unselectable();
31049     var cls = 'roo-splitbar-proxy';
31050     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31051     document.body.appendChild(proxy.dom);
31052     return proxy.dom;
31053 };
31054
31055 /** 
31056  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31057  * Default Adapter. It assumes the splitter and resizing element are not positioned
31058  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31059  */
31060 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31061 };
31062
31063 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31064     // do nothing for now
31065     init : function(s){
31066     
31067     },
31068     /**
31069      * Called before drag operations to get the current size of the resizing element. 
31070      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31071      */
31072      getElementSize : function(s){
31073         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31074             return s.resizingEl.getWidth();
31075         }else{
31076             return s.resizingEl.getHeight();
31077         }
31078     },
31079     
31080     /**
31081      * Called after drag operations to set the size of the resizing element.
31082      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31083      * @param {Number} newSize The new size to set
31084      * @param {Function} onComplete A function to be invoked when resizing is complete
31085      */
31086     setElementSize : function(s, newSize, onComplete){
31087         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31088             if(!s.animate){
31089                 s.resizingEl.setWidth(newSize);
31090                 if(onComplete){
31091                     onComplete(s, newSize);
31092                 }
31093             }else{
31094                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31095             }
31096         }else{
31097             
31098             if(!s.animate){
31099                 s.resizingEl.setHeight(newSize);
31100                 if(onComplete){
31101                     onComplete(s, newSize);
31102                 }
31103             }else{
31104                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31105             }
31106         }
31107     }
31108 };
31109
31110 /** 
31111  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31112  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31113  * Adapter that  moves the splitter element to align with the resized sizing element. 
31114  * Used with an absolute positioned SplitBar.
31115  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31116  * document.body, make sure you assign an id to the body element.
31117  */
31118 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31119     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31120     this.container = Roo.get(container);
31121 };
31122
31123 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31124     init : function(s){
31125         this.basic.init(s);
31126     },
31127     
31128     getElementSize : function(s){
31129         return this.basic.getElementSize(s);
31130     },
31131     
31132     setElementSize : function(s, newSize, onComplete){
31133         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31134     },
31135     
31136     moveSplitter : function(s){
31137         var yes = Roo.bootstrap.SplitBar;
31138         switch(s.placement){
31139             case yes.LEFT:
31140                 s.el.setX(s.resizingEl.getRight());
31141                 break;
31142             case yes.RIGHT:
31143                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31144                 break;
31145             case yes.TOP:
31146                 s.el.setY(s.resizingEl.getBottom());
31147                 break;
31148             case yes.BOTTOM:
31149                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31150                 break;
31151         }
31152     }
31153 };
31154
31155 /**
31156  * Orientation constant - Create a vertical SplitBar
31157  * @static
31158  * @type Number
31159  */
31160 Roo.bootstrap.SplitBar.VERTICAL = 1;
31161
31162 /**
31163  * Orientation constant - Create a horizontal SplitBar
31164  * @static
31165  * @type Number
31166  */
31167 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31168
31169 /**
31170  * Placement constant - The resizing element is to the left of the splitter element
31171  * @static
31172  * @type Number
31173  */
31174 Roo.bootstrap.SplitBar.LEFT = 1;
31175
31176 /**
31177  * Placement constant - The resizing element is to the right of the splitter element
31178  * @static
31179  * @type Number
31180  */
31181 Roo.bootstrap.SplitBar.RIGHT = 2;
31182
31183 /**
31184  * Placement constant - The resizing element is positioned above the splitter element
31185  * @static
31186  * @type Number
31187  */
31188 Roo.bootstrap.SplitBar.TOP = 3;
31189
31190 /**
31191  * Placement constant - The resizing element is positioned under splitter element
31192  * @static
31193  * @type Number
31194  */
31195 Roo.bootstrap.SplitBar.BOTTOM = 4;
31196 Roo.namespace("Roo.bootstrap.layout");/*
31197  * Based on:
31198  * Ext JS Library 1.1.1
31199  * Copyright(c) 2006-2007, Ext JS, LLC.
31200  *
31201  * Originally Released Under LGPL - original licence link has changed is not relivant.
31202  *
31203  * Fork - LGPL
31204  * <script type="text/javascript">
31205  */
31206  
31207 /**
31208  * @class Roo.bootstrap.layout.Manager
31209  * @extends Roo.bootstrap.Component
31210  * Base class for layout managers.
31211  */
31212 Roo.bootstrap.layout.Manager = function(config)
31213 {
31214     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31215     
31216     
31217      
31218     
31219     
31220     /** false to disable window resize monitoring @type Boolean */
31221     this.monitorWindowResize = true;
31222     this.regions = {};
31223     this.addEvents({
31224         /**
31225          * @event layout
31226          * Fires when a layout is performed. 
31227          * @param {Roo.LayoutManager} this
31228          */
31229         "layout" : true,
31230         /**
31231          * @event regionresized
31232          * Fires when the user resizes a region. 
31233          * @param {Roo.LayoutRegion} region The resized region
31234          * @param {Number} newSize The new size (width for east/west, height for north/south)
31235          */
31236         "regionresized" : true,
31237         /**
31238          * @event regioncollapsed
31239          * Fires when a region is collapsed. 
31240          * @param {Roo.LayoutRegion} region The collapsed region
31241          */
31242         "regioncollapsed" : true,
31243         /**
31244          * @event regionexpanded
31245          * Fires when a region is expanded.  
31246          * @param {Roo.LayoutRegion} region The expanded region
31247          */
31248         "regionexpanded" : true
31249     });
31250     this.updating = false;
31251     
31252     if (config.el) {
31253         this.el = Roo.get(config.el);
31254         this.initEvents();
31255     }
31256     
31257 };
31258
31259 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31260     
31261     
31262     regions : null,
31263     
31264     monitorWindowResize : true,
31265     
31266     
31267     updating : false,
31268     
31269     
31270     onRender : function(ct, position)
31271     {
31272         if(!this.el){
31273             this.el = Roo.get(ct);
31274             this.initEvents();
31275         }
31276     },
31277     
31278     
31279     initEvents: function()
31280     {
31281         
31282         
31283         // ie scrollbar fix
31284         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31285             document.body.scroll = "no";
31286         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31287             this.el.position('relative');
31288         }
31289         this.id = this.el.id;
31290         this.el.addClass("roo-layout-container");
31291         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31292         if(this.el.dom != document.body ) {
31293             this.el.on('resize', this.layout,this);
31294             this.el.on('show', this.layout,this);
31295         }
31296
31297     },
31298     
31299     /**
31300      * Returns true if this layout is currently being updated
31301      * @return {Boolean}
31302      */
31303     isUpdating : function(){
31304         return this.updating; 
31305     },
31306     
31307     /**
31308      * Suspend the LayoutManager from doing auto-layouts while
31309      * making multiple add or remove calls
31310      */
31311     beginUpdate : function(){
31312         this.updating = true;    
31313     },
31314     
31315     /**
31316      * Restore auto-layouts and optionally disable the manager from performing a layout
31317      * @param {Boolean} noLayout true to disable a layout update 
31318      */
31319     endUpdate : function(noLayout){
31320         this.updating = false;
31321         if(!noLayout){
31322             this.layout();
31323         }    
31324     },
31325     
31326     layout: function(){
31327         // abstract...
31328     },
31329     
31330     onRegionResized : function(region, newSize){
31331         this.fireEvent("regionresized", region, newSize);
31332         this.layout();
31333     },
31334     
31335     onRegionCollapsed : function(region){
31336         this.fireEvent("regioncollapsed", region);
31337     },
31338     
31339     onRegionExpanded : function(region){
31340         this.fireEvent("regionexpanded", region);
31341     },
31342         
31343     /**
31344      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31345      * performs box-model adjustments.
31346      * @return {Object} The size as an object {width: (the width), height: (the height)}
31347      */
31348     getViewSize : function()
31349     {
31350         var size;
31351         if(this.el.dom != document.body){
31352             size = this.el.getSize();
31353         }else{
31354             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31355         }
31356         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31357         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31358         return size;
31359     },
31360     
31361     /**
31362      * Returns the Element this layout is bound to.
31363      * @return {Roo.Element}
31364      */
31365     getEl : function(){
31366         return this.el;
31367     },
31368     
31369     /**
31370      * Returns the specified region.
31371      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31372      * @return {Roo.LayoutRegion}
31373      */
31374     getRegion : function(target){
31375         return this.regions[target.toLowerCase()];
31376     },
31377     
31378     onWindowResize : function(){
31379         if(this.monitorWindowResize){
31380             this.layout();
31381         }
31382     }
31383 });/*
31384  * Based on:
31385  * Ext JS Library 1.1.1
31386  * Copyright(c) 2006-2007, Ext JS, LLC.
31387  *
31388  * Originally Released Under LGPL - original licence link has changed is not relivant.
31389  *
31390  * Fork - LGPL
31391  * <script type="text/javascript">
31392  */
31393 /**
31394  * @class Roo.bootstrap.layout.Border
31395  * @extends Roo.bootstrap.layout.Manager
31396  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31397  * please see: examples/bootstrap/nested.html<br><br>
31398  
31399 <b>The container the layout is rendered into can be either the body element or any other element.
31400 If it is not the body element, the container needs to either be an absolute positioned element,
31401 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31402 the container size if it is not the body element.</b>
31403
31404 * @constructor
31405 * Create a new Border
31406 * @param {Object} config Configuration options
31407  */
31408 Roo.bootstrap.layout.Border = function(config){
31409     config = config || {};
31410     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31411     
31412     
31413     
31414     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31415         if(config[region]){
31416             config[region].region = region;
31417             this.addRegion(config[region]);
31418         }
31419     },this);
31420     
31421 };
31422
31423 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31424
31425 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31426     /**
31427      * Creates and adds a new region if it doesn't already exist.
31428      * @param {String} target The target region key (north, south, east, west or center).
31429      * @param {Object} config The regions config object
31430      * @return {BorderLayoutRegion} The new region
31431      */
31432     addRegion : function(config)
31433     {
31434         if(!this.regions[config.region]){
31435             var r = this.factory(config);
31436             this.bindRegion(r);
31437         }
31438         return this.regions[config.region];
31439     },
31440
31441     // private (kinda)
31442     bindRegion : function(r){
31443         this.regions[r.config.region] = r;
31444         
31445         r.on("visibilitychange",    this.layout, this);
31446         r.on("paneladded",          this.layout, this);
31447         r.on("panelremoved",        this.layout, this);
31448         r.on("invalidated",         this.layout, this);
31449         r.on("resized",             this.onRegionResized, this);
31450         r.on("collapsed",           this.onRegionCollapsed, this);
31451         r.on("expanded",            this.onRegionExpanded, this);
31452     },
31453
31454     /**
31455      * Performs a layout update.
31456      */
31457     layout : function()
31458     {
31459         if(this.updating) {
31460             return;
31461         }
31462         var size = this.getViewSize();
31463         var w = size.width;
31464         var h = size.height;
31465         var centerW = w;
31466         var centerH = h;
31467         var centerY = 0;
31468         var centerX = 0;
31469         //var x = 0, y = 0;
31470
31471         var rs = this.regions;
31472         var north = rs["north"];
31473         var south = rs["south"]; 
31474         var west = rs["west"];
31475         var east = rs["east"];
31476         var center = rs["center"];
31477         //if(this.hideOnLayout){ // not supported anymore
31478             //c.el.setStyle("display", "none");
31479         //}
31480         if(north && north.isVisible()){
31481             var b = north.getBox();
31482             var m = north.getMargins();
31483             b.width = w - (m.left+m.right);
31484             b.x = m.left;
31485             b.y = m.top;
31486             centerY = b.height + b.y + m.bottom;
31487             centerH -= centerY;
31488             north.updateBox(this.safeBox(b));
31489         }
31490         if(south && south.isVisible()){
31491             var b = south.getBox();
31492             var m = south.getMargins();
31493             b.width = w - (m.left+m.right);
31494             b.x = m.left;
31495             var totalHeight = (b.height + m.top + m.bottom);
31496             b.y = h - totalHeight + m.top;
31497             centerH -= totalHeight;
31498             south.updateBox(this.safeBox(b));
31499         }
31500         if(west && west.isVisible()){
31501             var b = west.getBox();
31502             var m = west.getMargins();
31503             b.height = centerH - (m.top+m.bottom);
31504             b.x = m.left;
31505             b.y = centerY + m.top;
31506             var totalWidth = (b.width + m.left + m.right);
31507             centerX += totalWidth;
31508             centerW -= totalWidth;
31509             west.updateBox(this.safeBox(b));
31510         }
31511         if(east && east.isVisible()){
31512             var b = east.getBox();
31513             var m = east.getMargins();
31514             b.height = centerH - (m.top+m.bottom);
31515             var totalWidth = (b.width + m.left + m.right);
31516             b.x = w - totalWidth + m.left;
31517             b.y = centerY + m.top;
31518             centerW -= totalWidth;
31519             east.updateBox(this.safeBox(b));
31520         }
31521         if(center){
31522             var m = center.getMargins();
31523             var centerBox = {
31524                 x: centerX + m.left,
31525                 y: centerY + m.top,
31526                 width: centerW - (m.left+m.right),
31527                 height: centerH - (m.top+m.bottom)
31528             };
31529             //if(this.hideOnLayout){
31530                 //center.el.setStyle("display", "block");
31531             //}
31532             center.updateBox(this.safeBox(centerBox));
31533         }
31534         this.el.repaint();
31535         this.fireEvent("layout", this);
31536     },
31537
31538     // private
31539     safeBox : function(box){
31540         box.width = Math.max(0, box.width);
31541         box.height = Math.max(0, box.height);
31542         return box;
31543     },
31544
31545     /**
31546      * Adds a ContentPanel (or subclass) to this layout.
31547      * @param {String} target The target region key (north, south, east, west or center).
31548      * @param {Roo.ContentPanel} panel The panel to add
31549      * @return {Roo.ContentPanel} The added panel
31550      */
31551     add : function(target, panel){
31552          
31553         target = target.toLowerCase();
31554         return this.regions[target].add(panel);
31555     },
31556
31557     /**
31558      * Remove a ContentPanel (or subclass) to this layout.
31559      * @param {String} target The target region key (north, south, east, west or center).
31560      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31561      * @return {Roo.ContentPanel} The removed panel
31562      */
31563     remove : function(target, panel){
31564         target = target.toLowerCase();
31565         return this.regions[target].remove(panel);
31566     },
31567
31568     /**
31569      * Searches all regions for a panel with the specified id
31570      * @param {String} panelId
31571      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31572      */
31573     findPanel : function(panelId){
31574         var rs = this.regions;
31575         for(var target in rs){
31576             if(typeof rs[target] != "function"){
31577                 var p = rs[target].getPanel(panelId);
31578                 if(p){
31579                     return p;
31580                 }
31581             }
31582         }
31583         return null;
31584     },
31585
31586     /**
31587      * Searches all regions for a panel with the specified id and activates (shows) it.
31588      * @param {String/ContentPanel} panelId The panels id or the panel itself
31589      * @return {Roo.ContentPanel} The shown panel or null
31590      */
31591     showPanel : function(panelId) {
31592       var rs = this.regions;
31593       for(var target in rs){
31594          var r = rs[target];
31595          if(typeof r != "function"){
31596             if(r.hasPanel(panelId)){
31597                return r.showPanel(panelId);
31598             }
31599          }
31600       }
31601       return null;
31602    },
31603
31604    /**
31605      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31606      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31607      */
31608    /*
31609     restoreState : function(provider){
31610         if(!provider){
31611             provider = Roo.state.Manager;
31612         }
31613         var sm = new Roo.LayoutStateManager();
31614         sm.init(this, provider);
31615     },
31616 */
31617  
31618  
31619     /**
31620      * Adds a xtype elements to the layout.
31621      * <pre><code>
31622
31623 layout.addxtype({
31624        xtype : 'ContentPanel',
31625        region: 'west',
31626        items: [ .... ]
31627    }
31628 );
31629
31630 layout.addxtype({
31631         xtype : 'NestedLayoutPanel',
31632         region: 'west',
31633         layout: {
31634            center: { },
31635            west: { }   
31636         },
31637         items : [ ... list of content panels or nested layout panels.. ]
31638    }
31639 );
31640 </code></pre>
31641      * @param {Object} cfg Xtype definition of item to add.
31642      */
31643     addxtype : function(cfg)
31644     {
31645         // basically accepts a pannel...
31646         // can accept a layout region..!?!?
31647         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31648         
31649         
31650         // theory?  children can only be panels??
31651         
31652         //if (!cfg.xtype.match(/Panel$/)) {
31653         //    return false;
31654         //}
31655         var ret = false;
31656         
31657         if (typeof(cfg.region) == 'undefined') {
31658             Roo.log("Failed to add Panel, region was not set");
31659             Roo.log(cfg);
31660             return false;
31661         }
31662         var region = cfg.region;
31663         delete cfg.region;
31664         
31665           
31666         var xitems = [];
31667         if (cfg.items) {
31668             xitems = cfg.items;
31669             delete cfg.items;
31670         }
31671         var nb = false;
31672         
31673         switch(cfg.xtype) 
31674         {
31675             case 'Content':  // ContentPanel (el, cfg)
31676             case 'Scroll':  // ContentPanel (el, cfg)
31677             case 'View': 
31678                 cfg.autoCreate = true;
31679                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31680                 //} else {
31681                 //    var el = this.el.createChild();
31682                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31683                 //}
31684                 
31685                 this.add(region, ret);
31686                 break;
31687             
31688             /*
31689             case 'TreePanel': // our new panel!
31690                 cfg.el = this.el.createChild();
31691                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31692                 this.add(region, ret);
31693                 break;
31694             */
31695             
31696             case 'Nest': 
31697                 // create a new Layout (which is  a Border Layout...
31698                 
31699                 var clayout = cfg.layout;
31700                 clayout.el  = this.el.createChild();
31701                 clayout.items   = clayout.items  || [];
31702                 
31703                 delete cfg.layout;
31704                 
31705                 // replace this exitems with the clayout ones..
31706                 xitems = clayout.items;
31707                  
31708                 // force background off if it's in center...
31709                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31710                     cfg.background = false;
31711                 }
31712                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31713                 
31714                 
31715                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31716                 //console.log('adding nested layout panel '  + cfg.toSource());
31717                 this.add(region, ret);
31718                 nb = {}; /// find first...
31719                 break;
31720             
31721             case 'Grid':
31722                 
31723                 // needs grid and region
31724                 
31725                 //var el = this.getRegion(region).el.createChild();
31726                 /*
31727                  *var el = this.el.createChild();
31728                 // create the grid first...
31729                 cfg.grid.container = el;
31730                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31731                 */
31732                 
31733                 if (region == 'center' && this.active ) {
31734                     cfg.background = false;
31735                 }
31736                 
31737                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31738                 
31739                 this.add(region, ret);
31740                 /*
31741                 if (cfg.background) {
31742                     // render grid on panel activation (if panel background)
31743                     ret.on('activate', function(gp) {
31744                         if (!gp.grid.rendered) {
31745                     //        gp.grid.render(el);
31746                         }
31747                     });
31748                 } else {
31749                   //  cfg.grid.render(el);
31750                 }
31751                 */
31752                 break;
31753            
31754            
31755             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31756                 // it was the old xcomponent building that caused this before.
31757                 // espeically if border is the top element in the tree.
31758                 ret = this;
31759                 break; 
31760                 
31761                     
31762                 
31763                 
31764                 
31765             default:
31766                 /*
31767                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31768                     
31769                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31770                     this.add(region, ret);
31771                 } else {
31772                 */
31773                     Roo.log(cfg);
31774                     throw "Can not add '" + cfg.xtype + "' to Border";
31775                     return null;
31776              
31777                                 
31778              
31779         }
31780         this.beginUpdate();
31781         // add children..
31782         var region = '';
31783         var abn = {};
31784         Roo.each(xitems, function(i)  {
31785             region = nb && i.region ? i.region : false;
31786             
31787             var add = ret.addxtype(i);
31788            
31789             if (region) {
31790                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31791                 if (!i.background) {
31792                     abn[region] = nb[region] ;
31793                 }
31794             }
31795             
31796         });
31797         this.endUpdate();
31798
31799         // make the last non-background panel active..
31800         //if (nb) { Roo.log(abn); }
31801         if (nb) {
31802             
31803             for(var r in abn) {
31804                 region = this.getRegion(r);
31805                 if (region) {
31806                     // tried using nb[r], but it does not work..
31807                      
31808                     region.showPanel(abn[r]);
31809                    
31810                 }
31811             }
31812         }
31813         return ret;
31814         
31815     },
31816     
31817     
31818 // private
31819     factory : function(cfg)
31820     {
31821         
31822         var validRegions = Roo.bootstrap.layout.Border.regions;
31823
31824         var target = cfg.region;
31825         cfg.mgr = this;
31826         
31827         var r = Roo.bootstrap.layout;
31828         Roo.log(target);
31829         switch(target){
31830             case "north":
31831                 return new r.North(cfg);
31832             case "south":
31833                 return new r.South(cfg);
31834             case "east":
31835                 return new r.East(cfg);
31836             case "west":
31837                 return new r.West(cfg);
31838             case "center":
31839                 return new r.Center(cfg);
31840         }
31841         throw 'Layout region "'+target+'" not supported.';
31842     }
31843     
31844     
31845 });
31846  /*
31847  * Based on:
31848  * Ext JS Library 1.1.1
31849  * Copyright(c) 2006-2007, Ext JS, LLC.
31850  *
31851  * Originally Released Under LGPL - original licence link has changed is not relivant.
31852  *
31853  * Fork - LGPL
31854  * <script type="text/javascript">
31855  */
31856  
31857 /**
31858  * @class Roo.bootstrap.layout.Basic
31859  * @extends Roo.util.Observable
31860  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31861  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31862  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31863  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
31864  * @cfg {string}   region  the region that it inhabits..
31865  * @cfg {bool}   skipConfig skip config?
31866  * 
31867
31868  */
31869 Roo.bootstrap.layout.Basic = function(config){
31870     
31871     this.mgr = config.mgr;
31872     
31873     this.position = config.region;
31874     
31875     var skipConfig = config.skipConfig;
31876     
31877     this.events = {
31878         /**
31879          * @scope Roo.BasicLayoutRegion
31880          */
31881         
31882         /**
31883          * @event beforeremove
31884          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31885          * @param {Roo.LayoutRegion} this
31886          * @param {Roo.ContentPanel} panel The panel
31887          * @param {Object} e The cancel event object
31888          */
31889         "beforeremove" : true,
31890         /**
31891          * @event invalidated
31892          * Fires when the layout for this region is changed.
31893          * @param {Roo.LayoutRegion} this
31894          */
31895         "invalidated" : true,
31896         /**
31897          * @event visibilitychange
31898          * Fires when this region is shown or hidden 
31899          * @param {Roo.LayoutRegion} this
31900          * @param {Boolean} visibility true or false
31901          */
31902         "visibilitychange" : true,
31903         /**
31904          * @event paneladded
31905          * Fires when a panel is added. 
31906          * @param {Roo.LayoutRegion} this
31907          * @param {Roo.ContentPanel} panel The panel
31908          */
31909         "paneladded" : true,
31910         /**
31911          * @event panelremoved
31912          * Fires when a panel is removed. 
31913          * @param {Roo.LayoutRegion} this
31914          * @param {Roo.ContentPanel} panel The panel
31915          */
31916         "panelremoved" : true,
31917         /**
31918          * @event beforecollapse
31919          * Fires when this region before collapse.
31920          * @param {Roo.LayoutRegion} this
31921          */
31922         "beforecollapse" : true,
31923         /**
31924          * @event collapsed
31925          * Fires when this region is collapsed.
31926          * @param {Roo.LayoutRegion} this
31927          */
31928         "collapsed" : true,
31929         /**
31930          * @event expanded
31931          * Fires when this region is expanded.
31932          * @param {Roo.LayoutRegion} this
31933          */
31934         "expanded" : true,
31935         /**
31936          * @event slideshow
31937          * Fires when this region is slid into view.
31938          * @param {Roo.LayoutRegion} this
31939          */
31940         "slideshow" : true,
31941         /**
31942          * @event slidehide
31943          * Fires when this region slides out of view. 
31944          * @param {Roo.LayoutRegion} this
31945          */
31946         "slidehide" : true,
31947         /**
31948          * @event panelactivated
31949          * Fires when a panel is activated. 
31950          * @param {Roo.LayoutRegion} this
31951          * @param {Roo.ContentPanel} panel The activated panel
31952          */
31953         "panelactivated" : true,
31954         /**
31955          * @event resized
31956          * Fires when the user resizes this region. 
31957          * @param {Roo.LayoutRegion} this
31958          * @param {Number} newSize The new size (width for east/west, height for north/south)
31959          */
31960         "resized" : true
31961     };
31962     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31963     this.panels = new Roo.util.MixedCollection();
31964     this.panels.getKey = this.getPanelId.createDelegate(this);
31965     this.box = null;
31966     this.activePanel = null;
31967     // ensure listeners are added...
31968     
31969     if (config.listeners || config.events) {
31970         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31971             listeners : config.listeners || {},
31972             events : config.events || {}
31973         });
31974     }
31975     
31976     if(skipConfig !== true){
31977         this.applyConfig(config);
31978     }
31979 };
31980
31981 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31982 {
31983     getPanelId : function(p){
31984         return p.getId();
31985     },
31986     
31987     applyConfig : function(config){
31988         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31989         this.config = config;
31990         
31991     },
31992     
31993     /**
31994      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31995      * the width, for horizontal (north, south) the height.
31996      * @param {Number} newSize The new width or height
31997      */
31998     resizeTo : function(newSize){
31999         var el = this.el ? this.el :
32000                  (this.activePanel ? this.activePanel.getEl() : null);
32001         if(el){
32002             switch(this.position){
32003                 case "east":
32004                 case "west":
32005                     el.setWidth(newSize);
32006                     this.fireEvent("resized", this, newSize);
32007                 break;
32008                 case "north":
32009                 case "south":
32010                     el.setHeight(newSize);
32011                     this.fireEvent("resized", this, newSize);
32012                 break;                
32013             }
32014         }
32015     },
32016     
32017     getBox : function(){
32018         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32019     },
32020     
32021     getMargins : function(){
32022         return this.margins;
32023     },
32024     
32025     updateBox : function(box){
32026         this.box = box;
32027         var el = this.activePanel.getEl();
32028         el.dom.style.left = box.x + "px";
32029         el.dom.style.top = box.y + "px";
32030         this.activePanel.setSize(box.width, box.height);
32031     },
32032     
32033     /**
32034      * Returns the container element for this region.
32035      * @return {Roo.Element}
32036      */
32037     getEl : function(){
32038         return this.activePanel;
32039     },
32040     
32041     /**
32042      * Returns true if this region is currently visible.
32043      * @return {Boolean}
32044      */
32045     isVisible : function(){
32046         return this.activePanel ? true : false;
32047     },
32048     
32049     setActivePanel : function(panel){
32050         panel = this.getPanel(panel);
32051         if(this.activePanel && this.activePanel != panel){
32052             this.activePanel.setActiveState(false);
32053             this.activePanel.getEl().setLeftTop(-10000,-10000);
32054         }
32055         this.activePanel = panel;
32056         panel.setActiveState(true);
32057         if(this.box){
32058             panel.setSize(this.box.width, this.box.height);
32059         }
32060         this.fireEvent("panelactivated", this, panel);
32061         this.fireEvent("invalidated");
32062     },
32063     
32064     /**
32065      * Show the specified panel.
32066      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32067      * @return {Roo.ContentPanel} The shown panel or null
32068      */
32069     showPanel : function(panel){
32070         panel = this.getPanel(panel);
32071         if(panel){
32072             this.setActivePanel(panel);
32073         }
32074         return panel;
32075     },
32076     
32077     /**
32078      * Get the active panel for this region.
32079      * @return {Roo.ContentPanel} The active panel or null
32080      */
32081     getActivePanel : function(){
32082         return this.activePanel;
32083     },
32084     
32085     /**
32086      * Add the passed ContentPanel(s)
32087      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32088      * @return {Roo.ContentPanel} The panel added (if only one was added)
32089      */
32090     add : function(panel){
32091         if(arguments.length > 1){
32092             for(var i = 0, len = arguments.length; i < len; i++) {
32093                 this.add(arguments[i]);
32094             }
32095             return null;
32096         }
32097         if(this.hasPanel(panel)){
32098             this.showPanel(panel);
32099             return panel;
32100         }
32101         var el = panel.getEl();
32102         if(el.dom.parentNode != this.mgr.el.dom){
32103             this.mgr.el.dom.appendChild(el.dom);
32104         }
32105         if(panel.setRegion){
32106             panel.setRegion(this);
32107         }
32108         this.panels.add(panel);
32109         el.setStyle("position", "absolute");
32110         if(!panel.background){
32111             this.setActivePanel(panel);
32112             if(this.config.initialSize && this.panels.getCount()==1){
32113                 this.resizeTo(this.config.initialSize);
32114             }
32115         }
32116         this.fireEvent("paneladded", this, panel);
32117         return panel;
32118     },
32119     
32120     /**
32121      * Returns true if the panel is in this region.
32122      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32123      * @return {Boolean}
32124      */
32125     hasPanel : function(panel){
32126         if(typeof panel == "object"){ // must be panel obj
32127             panel = panel.getId();
32128         }
32129         return this.getPanel(panel) ? true : false;
32130     },
32131     
32132     /**
32133      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32134      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32135      * @param {Boolean} preservePanel Overrides the config preservePanel option
32136      * @return {Roo.ContentPanel} The panel that was removed
32137      */
32138     remove : function(panel, preservePanel){
32139         panel = this.getPanel(panel);
32140         if(!panel){
32141             return null;
32142         }
32143         var e = {};
32144         this.fireEvent("beforeremove", this, panel, e);
32145         if(e.cancel === true){
32146             return null;
32147         }
32148         var panelId = panel.getId();
32149         this.panels.removeKey(panelId);
32150         return panel;
32151     },
32152     
32153     /**
32154      * Returns the panel specified or null if it's not in this region.
32155      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32156      * @return {Roo.ContentPanel}
32157      */
32158     getPanel : function(id){
32159         if(typeof id == "object"){ // must be panel obj
32160             return id;
32161         }
32162         return this.panels.get(id);
32163     },
32164     
32165     /**
32166      * Returns this regions position (north/south/east/west/center).
32167      * @return {String} 
32168      */
32169     getPosition: function(){
32170         return this.position;    
32171     }
32172 });/*
32173  * Based on:
32174  * Ext JS Library 1.1.1
32175  * Copyright(c) 2006-2007, Ext JS, LLC.
32176  *
32177  * Originally Released Under LGPL - original licence link has changed is not relivant.
32178  *
32179  * Fork - LGPL
32180  * <script type="text/javascript">
32181  */
32182  
32183 /**
32184  * @class Roo.bootstrap.layout.Region
32185  * @extends Roo.bootstrap.layout.Basic
32186  * This class represents a region in a layout manager.
32187  
32188  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32189  * @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})
32190  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32191  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32192  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32193  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32194  * @cfg {String}    title           The title for the region (overrides panel titles)
32195  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32196  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32197  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32198  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32199  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32200  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32201  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32202  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32203  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32204  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32205
32206  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32207  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32208  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32209  * @cfg {Number}    width           For East/West panels
32210  * @cfg {Number}    height          For North/South panels
32211  * @cfg {Boolean}   split           To show the splitter
32212  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32213  * 
32214  * @cfg {string}   cls             Extra CSS classes to add to region
32215  * 
32216  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32217  * @cfg {string}   region  the region that it inhabits..
32218  *
32219
32220  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32221  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32222
32223  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32224  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32225  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32226  */
32227 Roo.bootstrap.layout.Region = function(config)
32228 {
32229     this.applyConfig(config);
32230
32231     var mgr = config.mgr;
32232     var pos = config.region;
32233     config.skipConfig = true;
32234     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32235     
32236     if (mgr.el) {
32237         this.onRender(mgr.el);   
32238     }
32239      
32240     this.visible = true;
32241     this.collapsed = false;
32242 };
32243
32244 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32245
32246     position: '', // set by wrapper (eg. north/south etc..)
32247
32248     createBody : function(){
32249         /** This region's body element 
32250         * @type Roo.Element */
32251         this.bodyEl = this.el.createChild({
32252                 tag: "div",
32253                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32254         });
32255     },
32256
32257     onRender: function(ctr, pos)
32258     {
32259         var dh = Roo.DomHelper;
32260         /** This region's container element 
32261         * @type Roo.Element */
32262         this.el = dh.append(ctr.dom, {
32263                 tag: "div",
32264                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32265             }, true);
32266         /** This region's title element 
32267         * @type Roo.Element */
32268     
32269         this.titleEl = dh.append(this.el.dom,
32270             {
32271                     tag: "div",
32272                     unselectable: "on",
32273                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32274                     children:[
32275                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32276                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32277                     ]}, true);
32278         
32279         this.titleEl.enableDisplayMode();
32280         /** This region's title text element 
32281         * @type HTMLElement */
32282         this.titleTextEl = this.titleEl.dom.firstChild;
32283         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32284         /*
32285         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32286         this.closeBtn.enableDisplayMode();
32287         this.closeBtn.on("click", this.closeClicked, this);
32288         this.closeBtn.hide();
32289     */
32290         this.createBody(this.config);
32291         if(this.config.hideWhenEmpty){
32292             this.hide();
32293             this.on("paneladded", this.validateVisibility, this);
32294             this.on("panelremoved", this.validateVisibility, this);
32295         }
32296         if(this.autoScroll){
32297             this.bodyEl.setStyle("overflow", "auto");
32298         }else{
32299             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32300         }
32301         //if(c.titlebar !== false){
32302             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32303                 this.titleEl.hide();
32304             }else{
32305                 this.titleEl.show();
32306                 if(this.config.title){
32307                     this.titleTextEl.innerHTML = this.config.title;
32308                 }
32309             }
32310         //}
32311         if(this.config.collapsed){
32312             this.collapse(true);
32313         }
32314         if(this.config.hidden){
32315             this.hide();
32316         }
32317     },
32318     
32319     applyConfig : function(c)
32320     {
32321         /*
32322          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32323             var dh = Roo.DomHelper;
32324             if(c.titlebar !== false){
32325                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32326                 this.collapseBtn.on("click", this.collapse, this);
32327                 this.collapseBtn.enableDisplayMode();
32328                 /*
32329                 if(c.showPin === true || this.showPin){
32330                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32331                     this.stickBtn.enableDisplayMode();
32332                     this.stickBtn.on("click", this.expand, this);
32333                     this.stickBtn.hide();
32334                 }
32335                 
32336             }
32337             */
32338             /** This region's collapsed element
32339             * @type Roo.Element */
32340             /*
32341              *
32342             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32343                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32344             ]}, true);
32345             
32346             if(c.floatable !== false){
32347                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32348                this.collapsedEl.on("click", this.collapseClick, this);
32349             }
32350
32351             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32352                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32353                    id: "message", unselectable: "on", style:{"float":"left"}});
32354                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32355              }
32356             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32357             this.expandBtn.on("click", this.expand, this);
32358             
32359         }
32360         
32361         if(this.collapseBtn){
32362             this.collapseBtn.setVisible(c.collapsible == true);
32363         }
32364         
32365         this.cmargins = c.cmargins || this.cmargins ||
32366                          (this.position == "west" || this.position == "east" ?
32367                              {top: 0, left: 2, right:2, bottom: 0} :
32368                              {top: 2, left: 0, right:0, bottom: 2});
32369         */
32370         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32371         
32372         
32373         this.bottomTabs = c.tabPosition != "top";
32374         
32375         this.autoScroll = c.autoScroll || false;
32376         
32377         
32378        
32379         
32380         this.duration = c.duration || .30;
32381         this.slideDuration = c.slideDuration || .45;
32382         this.config = c;
32383        
32384     },
32385     /**
32386      * Returns true if this region is currently visible.
32387      * @return {Boolean}
32388      */
32389     isVisible : function(){
32390         return this.visible;
32391     },
32392
32393     /**
32394      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32395      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32396      */
32397     //setCollapsedTitle : function(title){
32398     //    title = title || "&#160;";
32399      //   if(this.collapsedTitleTextEl){
32400       //      this.collapsedTitleTextEl.innerHTML = title;
32401        // }
32402     //},
32403
32404     getBox : function(){
32405         var b;
32406       //  if(!this.collapsed){
32407             b = this.el.getBox(false, true);
32408        // }else{
32409           //  b = this.collapsedEl.getBox(false, true);
32410         //}
32411         return b;
32412     },
32413
32414     getMargins : function(){
32415         return this.margins;
32416         //return this.collapsed ? this.cmargins : this.margins;
32417     },
32418 /*
32419     highlight : function(){
32420         this.el.addClass("x-layout-panel-dragover");
32421     },
32422
32423     unhighlight : function(){
32424         this.el.removeClass("x-layout-panel-dragover");
32425     },
32426 */
32427     updateBox : function(box)
32428     {
32429         this.box = box;
32430         if(!this.collapsed){
32431             this.el.dom.style.left = box.x + "px";
32432             this.el.dom.style.top = box.y + "px";
32433             this.updateBody(box.width, box.height);
32434         }else{
32435             this.collapsedEl.dom.style.left = box.x + "px";
32436             this.collapsedEl.dom.style.top = box.y + "px";
32437             this.collapsedEl.setSize(box.width, box.height);
32438         }
32439         if(this.tabs){
32440             this.tabs.autoSizeTabs();
32441         }
32442     },
32443
32444     updateBody : function(w, h)
32445     {
32446         if(w !== null){
32447             this.el.setWidth(w);
32448             w -= this.el.getBorderWidth("rl");
32449             if(this.config.adjustments){
32450                 w += this.config.adjustments[0];
32451             }
32452         }
32453         if(h !== null){
32454             this.el.setHeight(h);
32455             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32456             h -= this.el.getBorderWidth("tb");
32457             if(this.config.adjustments){
32458                 h += this.config.adjustments[1];
32459             }
32460             this.bodyEl.setHeight(h);
32461             if(this.tabs){
32462                 h = this.tabs.syncHeight(h);
32463             }
32464         }
32465         if(this.panelSize){
32466             w = w !== null ? w : this.panelSize.width;
32467             h = h !== null ? h : this.panelSize.height;
32468         }
32469         if(this.activePanel){
32470             var el = this.activePanel.getEl();
32471             w = w !== null ? w : el.getWidth();
32472             h = h !== null ? h : el.getHeight();
32473             this.panelSize = {width: w, height: h};
32474             this.activePanel.setSize(w, h);
32475         }
32476         if(Roo.isIE && this.tabs){
32477             this.tabs.el.repaint();
32478         }
32479     },
32480
32481     /**
32482      * Returns the container element for this region.
32483      * @return {Roo.Element}
32484      */
32485     getEl : function(){
32486         return this.el;
32487     },
32488
32489     /**
32490      * Hides this region.
32491      */
32492     hide : function(){
32493         //if(!this.collapsed){
32494             this.el.dom.style.left = "-2000px";
32495             this.el.hide();
32496         //}else{
32497          //   this.collapsedEl.dom.style.left = "-2000px";
32498          //   this.collapsedEl.hide();
32499        // }
32500         this.visible = false;
32501         this.fireEvent("visibilitychange", this, false);
32502     },
32503
32504     /**
32505      * Shows this region if it was previously hidden.
32506      */
32507     show : function(){
32508         //if(!this.collapsed){
32509             this.el.show();
32510         //}else{
32511         //    this.collapsedEl.show();
32512        // }
32513         this.visible = true;
32514         this.fireEvent("visibilitychange", this, true);
32515     },
32516 /*
32517     closeClicked : function(){
32518         if(this.activePanel){
32519             this.remove(this.activePanel);
32520         }
32521     },
32522
32523     collapseClick : function(e){
32524         if(this.isSlid){
32525            e.stopPropagation();
32526            this.slideIn();
32527         }else{
32528            e.stopPropagation();
32529            this.slideOut();
32530         }
32531     },
32532 */
32533     /**
32534      * Collapses this region.
32535      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32536      */
32537     /*
32538     collapse : function(skipAnim, skipCheck = false){
32539         if(this.collapsed) {
32540             return;
32541         }
32542         
32543         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32544             
32545             this.collapsed = true;
32546             if(this.split){
32547                 this.split.el.hide();
32548             }
32549             if(this.config.animate && skipAnim !== true){
32550                 this.fireEvent("invalidated", this);
32551                 this.animateCollapse();
32552             }else{
32553                 this.el.setLocation(-20000,-20000);
32554                 this.el.hide();
32555                 this.collapsedEl.show();
32556                 this.fireEvent("collapsed", this);
32557                 this.fireEvent("invalidated", this);
32558             }
32559         }
32560         
32561     },
32562 */
32563     animateCollapse : function(){
32564         // overridden
32565     },
32566
32567     /**
32568      * Expands this region if it was previously collapsed.
32569      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32570      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32571      */
32572     /*
32573     expand : function(e, skipAnim){
32574         if(e) {
32575             e.stopPropagation();
32576         }
32577         if(!this.collapsed || this.el.hasActiveFx()) {
32578             return;
32579         }
32580         if(this.isSlid){
32581             this.afterSlideIn();
32582             skipAnim = true;
32583         }
32584         this.collapsed = false;
32585         if(this.config.animate && skipAnim !== true){
32586             this.animateExpand();
32587         }else{
32588             this.el.show();
32589             if(this.split){
32590                 this.split.el.show();
32591             }
32592             this.collapsedEl.setLocation(-2000,-2000);
32593             this.collapsedEl.hide();
32594             this.fireEvent("invalidated", this);
32595             this.fireEvent("expanded", this);
32596         }
32597     },
32598 */
32599     animateExpand : function(){
32600         // overridden
32601     },
32602
32603     initTabs : function()
32604     {
32605         this.bodyEl.setStyle("overflow", "hidden");
32606         var ts = new Roo.bootstrap.panel.Tabs({
32607                 el: this.bodyEl.dom,
32608                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32609                 disableTooltips: this.config.disableTabTips,
32610                 toolbar : this.config.toolbar
32611             });
32612         
32613         if(this.config.hideTabs){
32614             ts.stripWrap.setDisplayed(false);
32615         }
32616         this.tabs = ts;
32617         ts.resizeTabs = this.config.resizeTabs === true;
32618         ts.minTabWidth = this.config.minTabWidth || 40;
32619         ts.maxTabWidth = this.config.maxTabWidth || 250;
32620         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32621         ts.monitorResize = false;
32622         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32623         ts.bodyEl.addClass('roo-layout-tabs-body');
32624         this.panels.each(this.initPanelAsTab, this);
32625     },
32626
32627     initPanelAsTab : function(panel){
32628         var ti = this.tabs.addTab(
32629                     panel.getEl().id,
32630                     panel.getTitle(), null,
32631                     this.config.closeOnTab && panel.isClosable()
32632             );
32633         if(panel.tabTip !== undefined){
32634             ti.setTooltip(panel.tabTip);
32635         }
32636         ti.on("activate", function(){
32637               this.setActivePanel(panel);
32638         }, this);
32639         
32640         if(this.config.closeOnTab){
32641             ti.on("beforeclose", function(t, e){
32642                 e.cancel = true;
32643                 this.remove(panel);
32644             }, this);
32645         }
32646         return ti;
32647     },
32648
32649     updatePanelTitle : function(panel, title)
32650     {
32651         if(this.activePanel == panel){
32652             this.updateTitle(title);
32653         }
32654         if(this.tabs){
32655             var ti = this.tabs.getTab(panel.getEl().id);
32656             ti.setText(title);
32657             if(panel.tabTip !== undefined){
32658                 ti.setTooltip(panel.tabTip);
32659             }
32660         }
32661     },
32662
32663     updateTitle : function(title){
32664         if(this.titleTextEl && !this.config.title){
32665             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32666         }
32667     },
32668
32669     setActivePanel : function(panel)
32670     {
32671         panel = this.getPanel(panel);
32672         if(this.activePanel && this.activePanel != panel){
32673             this.activePanel.setActiveState(false);
32674         }
32675         this.activePanel = panel;
32676         panel.setActiveState(true);
32677         if(this.panelSize){
32678             panel.setSize(this.panelSize.width, this.panelSize.height);
32679         }
32680         if(this.closeBtn){
32681             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32682         }
32683         this.updateTitle(panel.getTitle());
32684         if(this.tabs){
32685             this.fireEvent("invalidated", this);
32686         }
32687         this.fireEvent("panelactivated", this, panel);
32688     },
32689
32690     /**
32691      * Shows the specified panel.
32692      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32693      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32694      */
32695     showPanel : function(panel)
32696     {
32697         panel = this.getPanel(panel);
32698         if(panel){
32699             if(this.tabs){
32700                 var tab = this.tabs.getTab(panel.getEl().id);
32701                 if(tab.isHidden()){
32702                     this.tabs.unhideTab(tab.id);
32703                 }
32704                 tab.activate();
32705             }else{
32706                 this.setActivePanel(panel);
32707             }
32708         }
32709         return panel;
32710     },
32711
32712     /**
32713      * Get the active panel for this region.
32714      * @return {Roo.ContentPanel} The active panel or null
32715      */
32716     getActivePanel : function(){
32717         return this.activePanel;
32718     },
32719
32720     validateVisibility : function(){
32721         if(this.panels.getCount() < 1){
32722             this.updateTitle("&#160;");
32723             this.closeBtn.hide();
32724             this.hide();
32725         }else{
32726             if(!this.isVisible()){
32727                 this.show();
32728             }
32729         }
32730     },
32731
32732     /**
32733      * Adds the passed ContentPanel(s) to this region.
32734      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32735      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32736      */
32737     add : function(panel){
32738         if(arguments.length > 1){
32739             for(var i = 0, len = arguments.length; i < len; i++) {
32740                 this.add(arguments[i]);
32741             }
32742             return null;
32743         }
32744         if(this.hasPanel(panel)){
32745             this.showPanel(panel);
32746             return panel;
32747         }
32748         panel.setRegion(this);
32749         this.panels.add(panel);
32750         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32751             this.bodyEl.dom.appendChild(panel.getEl().dom);
32752             if(panel.background !== true){
32753                 this.setActivePanel(panel);
32754             }
32755             this.fireEvent("paneladded", this, panel);
32756             return panel;
32757         }
32758         if(!this.tabs){
32759             this.initTabs();
32760         }else{
32761             this.initPanelAsTab(panel);
32762         }
32763         
32764         
32765         if(panel.background !== true){
32766             this.tabs.activate(panel.getEl().id);
32767         }
32768         this.fireEvent("paneladded", this, panel);
32769         return panel;
32770     },
32771
32772     /**
32773      * Hides the tab for the specified panel.
32774      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32775      */
32776     hidePanel : function(panel){
32777         if(this.tabs && (panel = this.getPanel(panel))){
32778             this.tabs.hideTab(panel.getEl().id);
32779         }
32780     },
32781
32782     /**
32783      * Unhides the tab for a previously hidden panel.
32784      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32785      */
32786     unhidePanel : function(panel){
32787         if(this.tabs && (panel = this.getPanel(panel))){
32788             this.tabs.unhideTab(panel.getEl().id);
32789         }
32790     },
32791
32792     clearPanels : function(){
32793         while(this.panels.getCount() > 0){
32794              this.remove(this.panels.first());
32795         }
32796     },
32797
32798     /**
32799      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32800      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32801      * @param {Boolean} preservePanel Overrides the config preservePanel option
32802      * @return {Roo.ContentPanel} The panel that was removed
32803      */
32804     remove : function(panel, preservePanel)
32805     {
32806         panel = this.getPanel(panel);
32807         if(!panel){
32808             return null;
32809         }
32810         var e = {};
32811         this.fireEvent("beforeremove", this, panel, e);
32812         if(e.cancel === true){
32813             return null;
32814         }
32815         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32816         var panelId = panel.getId();
32817         this.panels.removeKey(panelId);
32818         if(preservePanel){
32819             document.body.appendChild(panel.getEl().dom);
32820         }
32821         if(this.tabs){
32822             this.tabs.removeTab(panel.getEl().id);
32823         }else if (!preservePanel){
32824             this.bodyEl.dom.removeChild(panel.getEl().dom);
32825         }
32826         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32827             var p = this.panels.first();
32828             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32829             tempEl.appendChild(p.getEl().dom);
32830             this.bodyEl.update("");
32831             this.bodyEl.dom.appendChild(p.getEl().dom);
32832             tempEl = null;
32833             this.updateTitle(p.getTitle());
32834             this.tabs = null;
32835             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32836             this.setActivePanel(p);
32837         }
32838         panel.setRegion(null);
32839         if(this.activePanel == panel){
32840             this.activePanel = null;
32841         }
32842         if(this.config.autoDestroy !== false && preservePanel !== true){
32843             try{panel.destroy();}catch(e){}
32844         }
32845         this.fireEvent("panelremoved", this, panel);
32846         return panel;
32847     },
32848
32849     /**
32850      * Returns the TabPanel component used by this region
32851      * @return {Roo.TabPanel}
32852      */
32853     getTabs : function(){
32854         return this.tabs;
32855     },
32856
32857     createTool : function(parentEl, className){
32858         var btn = Roo.DomHelper.append(parentEl, {
32859             tag: "div",
32860             cls: "x-layout-tools-button",
32861             children: [ {
32862                 tag: "div",
32863                 cls: "roo-layout-tools-button-inner " + className,
32864                 html: "&#160;"
32865             }]
32866         }, true);
32867         btn.addClassOnOver("roo-layout-tools-button-over");
32868         return btn;
32869     }
32870 });/*
32871  * Based on:
32872  * Ext JS Library 1.1.1
32873  * Copyright(c) 2006-2007, Ext JS, LLC.
32874  *
32875  * Originally Released Under LGPL - original licence link has changed is not relivant.
32876  *
32877  * Fork - LGPL
32878  * <script type="text/javascript">
32879  */
32880  
32881
32882
32883 /**
32884  * @class Roo.SplitLayoutRegion
32885  * @extends Roo.LayoutRegion
32886  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32887  */
32888 Roo.bootstrap.layout.Split = function(config){
32889     this.cursor = config.cursor;
32890     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32891 };
32892
32893 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32894 {
32895     splitTip : "Drag to resize.",
32896     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32897     useSplitTips : false,
32898
32899     applyConfig : function(config){
32900         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32901     },
32902     
32903     onRender : function(ctr,pos) {
32904         
32905         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
32906         if(!this.config.split){
32907             return;
32908         }
32909         if(!this.split){
32910             
32911             var splitEl = Roo.DomHelper.append(ctr.dom,  {
32912                             tag: "div",
32913                             id: this.el.id + "-split",
32914                             cls: "roo-layout-split roo-layout-split-"+this.position,
32915                             html: "&#160;"
32916             });
32917             /** The SplitBar for this region 
32918             * @type Roo.SplitBar */
32919             // does not exist yet...
32920             Roo.log([this.position, this.orientation]);
32921             
32922             this.split = new Roo.bootstrap.SplitBar({
32923                 dragElement : splitEl,
32924                 resizingElement: this.el,
32925                 orientation : this.orientation
32926             });
32927             
32928             this.split.on("moved", this.onSplitMove, this);
32929             this.split.useShim = this.config.useShim === true;
32930             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32931             if(this.useSplitTips){
32932                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32933             }
32934             //if(config.collapsible){
32935             //    this.split.el.on("dblclick", this.collapse,  this);
32936             //}
32937         }
32938         if(typeof this.config.minSize != "undefined"){
32939             this.split.minSize = this.config.minSize;
32940         }
32941         if(typeof this.config.maxSize != "undefined"){
32942             this.split.maxSize = this.config.maxSize;
32943         }
32944         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
32945             this.hideSplitter();
32946         }
32947         
32948     },
32949
32950     getHMaxSize : function(){
32951          var cmax = this.config.maxSize || 10000;
32952          var center = this.mgr.getRegion("center");
32953          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32954     },
32955
32956     getVMaxSize : function(){
32957          var cmax = this.config.maxSize || 10000;
32958          var center = this.mgr.getRegion("center");
32959          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32960     },
32961
32962     onSplitMove : function(split, newSize){
32963         this.fireEvent("resized", this, newSize);
32964     },
32965     
32966     /** 
32967      * Returns the {@link Roo.SplitBar} for this region.
32968      * @return {Roo.SplitBar}
32969      */
32970     getSplitBar : function(){
32971         return this.split;
32972     },
32973     
32974     hide : function(){
32975         this.hideSplitter();
32976         Roo.bootstrap.layout.Split.superclass.hide.call(this);
32977     },
32978
32979     hideSplitter : function(){
32980         if(this.split){
32981             this.split.el.setLocation(-2000,-2000);
32982             this.split.el.hide();
32983         }
32984     },
32985
32986     show : function(){
32987         if(this.split){
32988             this.split.el.show();
32989         }
32990         Roo.bootstrap.layout.Split.superclass.show.call(this);
32991     },
32992     
32993     beforeSlide: function(){
32994         if(Roo.isGecko){// firefox overflow auto bug workaround
32995             this.bodyEl.clip();
32996             if(this.tabs) {
32997                 this.tabs.bodyEl.clip();
32998             }
32999             if(this.activePanel){
33000                 this.activePanel.getEl().clip();
33001                 
33002                 if(this.activePanel.beforeSlide){
33003                     this.activePanel.beforeSlide();
33004                 }
33005             }
33006         }
33007     },
33008     
33009     afterSlide : function(){
33010         if(Roo.isGecko){// firefox overflow auto bug workaround
33011             this.bodyEl.unclip();
33012             if(this.tabs) {
33013                 this.tabs.bodyEl.unclip();
33014             }
33015             if(this.activePanel){
33016                 this.activePanel.getEl().unclip();
33017                 if(this.activePanel.afterSlide){
33018                     this.activePanel.afterSlide();
33019                 }
33020             }
33021         }
33022     },
33023
33024     initAutoHide : function(){
33025         if(this.autoHide !== false){
33026             if(!this.autoHideHd){
33027                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33028                 this.autoHideHd = {
33029                     "mouseout": function(e){
33030                         if(!e.within(this.el, true)){
33031                             st.delay(500);
33032                         }
33033                     },
33034                     "mouseover" : function(e){
33035                         st.cancel();
33036                     },
33037                     scope : this
33038                 };
33039             }
33040             this.el.on(this.autoHideHd);
33041         }
33042     },
33043
33044     clearAutoHide : function(){
33045         if(this.autoHide !== false){
33046             this.el.un("mouseout", this.autoHideHd.mouseout);
33047             this.el.un("mouseover", this.autoHideHd.mouseover);
33048         }
33049     },
33050
33051     clearMonitor : function(){
33052         Roo.get(document).un("click", this.slideInIf, this);
33053     },
33054
33055     // these names are backwards but not changed for compat
33056     slideOut : function(){
33057         if(this.isSlid || this.el.hasActiveFx()){
33058             return;
33059         }
33060         this.isSlid = true;
33061         if(this.collapseBtn){
33062             this.collapseBtn.hide();
33063         }
33064         this.closeBtnState = this.closeBtn.getStyle('display');
33065         this.closeBtn.hide();
33066         if(this.stickBtn){
33067             this.stickBtn.show();
33068         }
33069         this.el.show();
33070         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33071         this.beforeSlide();
33072         this.el.setStyle("z-index", 10001);
33073         this.el.slideIn(this.getSlideAnchor(), {
33074             callback: function(){
33075                 this.afterSlide();
33076                 this.initAutoHide();
33077                 Roo.get(document).on("click", this.slideInIf, this);
33078                 this.fireEvent("slideshow", this);
33079             },
33080             scope: this,
33081             block: true
33082         });
33083     },
33084
33085     afterSlideIn : function(){
33086         this.clearAutoHide();
33087         this.isSlid = false;
33088         this.clearMonitor();
33089         this.el.setStyle("z-index", "");
33090         if(this.collapseBtn){
33091             this.collapseBtn.show();
33092         }
33093         this.closeBtn.setStyle('display', this.closeBtnState);
33094         if(this.stickBtn){
33095             this.stickBtn.hide();
33096         }
33097         this.fireEvent("slidehide", this);
33098     },
33099
33100     slideIn : function(cb){
33101         if(!this.isSlid || this.el.hasActiveFx()){
33102             Roo.callback(cb);
33103             return;
33104         }
33105         this.isSlid = false;
33106         this.beforeSlide();
33107         this.el.slideOut(this.getSlideAnchor(), {
33108             callback: function(){
33109                 this.el.setLeftTop(-10000, -10000);
33110                 this.afterSlide();
33111                 this.afterSlideIn();
33112                 Roo.callback(cb);
33113             },
33114             scope: this,
33115             block: true
33116         });
33117     },
33118     
33119     slideInIf : function(e){
33120         if(!e.within(this.el)){
33121             this.slideIn();
33122         }
33123     },
33124
33125     animateCollapse : function(){
33126         this.beforeSlide();
33127         this.el.setStyle("z-index", 20000);
33128         var anchor = this.getSlideAnchor();
33129         this.el.slideOut(anchor, {
33130             callback : function(){
33131                 this.el.setStyle("z-index", "");
33132                 this.collapsedEl.slideIn(anchor, {duration:.3});
33133                 this.afterSlide();
33134                 this.el.setLocation(-10000,-10000);
33135                 this.el.hide();
33136                 this.fireEvent("collapsed", this);
33137             },
33138             scope: this,
33139             block: true
33140         });
33141     },
33142
33143     animateExpand : function(){
33144         this.beforeSlide();
33145         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33146         this.el.setStyle("z-index", 20000);
33147         this.collapsedEl.hide({
33148             duration:.1
33149         });
33150         this.el.slideIn(this.getSlideAnchor(), {
33151             callback : function(){
33152                 this.el.setStyle("z-index", "");
33153                 this.afterSlide();
33154                 if(this.split){
33155                     this.split.el.show();
33156                 }
33157                 this.fireEvent("invalidated", this);
33158                 this.fireEvent("expanded", this);
33159             },
33160             scope: this,
33161             block: true
33162         });
33163     },
33164
33165     anchors : {
33166         "west" : "left",
33167         "east" : "right",
33168         "north" : "top",
33169         "south" : "bottom"
33170     },
33171
33172     sanchors : {
33173         "west" : "l",
33174         "east" : "r",
33175         "north" : "t",
33176         "south" : "b"
33177     },
33178
33179     canchors : {
33180         "west" : "tl-tr",
33181         "east" : "tr-tl",
33182         "north" : "tl-bl",
33183         "south" : "bl-tl"
33184     },
33185
33186     getAnchor : function(){
33187         return this.anchors[this.position];
33188     },
33189
33190     getCollapseAnchor : function(){
33191         return this.canchors[this.position];
33192     },
33193
33194     getSlideAnchor : function(){
33195         return this.sanchors[this.position];
33196     },
33197
33198     getAlignAdj : function(){
33199         var cm = this.cmargins;
33200         switch(this.position){
33201             case "west":
33202                 return [0, 0];
33203             break;
33204             case "east":
33205                 return [0, 0];
33206             break;
33207             case "north":
33208                 return [0, 0];
33209             break;
33210             case "south":
33211                 return [0, 0];
33212             break;
33213         }
33214     },
33215
33216     getExpandAdj : function(){
33217         var c = this.collapsedEl, cm = this.cmargins;
33218         switch(this.position){
33219             case "west":
33220                 return [-(cm.right+c.getWidth()+cm.left), 0];
33221             break;
33222             case "east":
33223                 return [cm.right+c.getWidth()+cm.left, 0];
33224             break;
33225             case "north":
33226                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33227             break;
33228             case "south":
33229                 return [0, cm.top+cm.bottom+c.getHeight()];
33230             break;
33231         }
33232     }
33233 });/*
33234  * Based on:
33235  * Ext JS Library 1.1.1
33236  * Copyright(c) 2006-2007, Ext JS, LLC.
33237  *
33238  * Originally Released Under LGPL - original licence link has changed is not relivant.
33239  *
33240  * Fork - LGPL
33241  * <script type="text/javascript">
33242  */
33243 /*
33244  * These classes are private internal classes
33245  */
33246 Roo.bootstrap.layout.Center = function(config){
33247     config.region = "center";
33248     Roo.bootstrap.layout.Region.call(this, config);
33249     this.visible = true;
33250     this.minWidth = config.minWidth || 20;
33251     this.minHeight = config.minHeight || 20;
33252 };
33253
33254 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33255     hide : function(){
33256         // center panel can't be hidden
33257     },
33258     
33259     show : function(){
33260         // center panel can't be hidden
33261     },
33262     
33263     getMinWidth: function(){
33264         return this.minWidth;
33265     },
33266     
33267     getMinHeight: function(){
33268         return this.minHeight;
33269     }
33270 });
33271
33272
33273
33274
33275  
33276
33277
33278
33279
33280
33281 Roo.bootstrap.layout.North = function(config)
33282 {
33283     config.region = 'north';
33284     config.cursor = 'n-resize';
33285     
33286     Roo.bootstrap.layout.Split.call(this, config);
33287     
33288     
33289     if(this.split){
33290         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33291         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33292         this.split.el.addClass("roo-layout-split-v");
33293     }
33294     var size = config.initialSize || config.height;
33295     if(typeof size != "undefined"){
33296         this.el.setHeight(size);
33297     }
33298 };
33299 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33300 {
33301     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33302     
33303     
33304     
33305     getBox : function(){
33306         if(this.collapsed){
33307             return this.collapsedEl.getBox();
33308         }
33309         var box = this.el.getBox();
33310         if(this.split){
33311             box.height += this.split.el.getHeight();
33312         }
33313         return box;
33314     },
33315     
33316     updateBox : function(box){
33317         if(this.split && !this.collapsed){
33318             box.height -= this.split.el.getHeight();
33319             this.split.el.setLeft(box.x);
33320             this.split.el.setTop(box.y+box.height);
33321             this.split.el.setWidth(box.width);
33322         }
33323         if(this.collapsed){
33324             this.updateBody(box.width, null);
33325         }
33326         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33327     }
33328 });
33329
33330
33331
33332
33333
33334 Roo.bootstrap.layout.South = function(config){
33335     config.region = 'south';
33336     config.cursor = 's-resize';
33337     Roo.bootstrap.layout.Split.call(this, config);
33338     if(this.split){
33339         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33340         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33341         this.split.el.addClass("roo-layout-split-v");
33342     }
33343     var size = config.initialSize || config.height;
33344     if(typeof size != "undefined"){
33345         this.el.setHeight(size);
33346     }
33347 };
33348
33349 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33350     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33351     getBox : function(){
33352         if(this.collapsed){
33353             return this.collapsedEl.getBox();
33354         }
33355         var box = this.el.getBox();
33356         if(this.split){
33357             var sh = this.split.el.getHeight();
33358             box.height += sh;
33359             box.y -= sh;
33360         }
33361         return box;
33362     },
33363     
33364     updateBox : function(box){
33365         if(this.split && !this.collapsed){
33366             var sh = this.split.el.getHeight();
33367             box.height -= sh;
33368             box.y += sh;
33369             this.split.el.setLeft(box.x);
33370             this.split.el.setTop(box.y-sh);
33371             this.split.el.setWidth(box.width);
33372         }
33373         if(this.collapsed){
33374             this.updateBody(box.width, null);
33375         }
33376         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33377     }
33378 });
33379
33380 Roo.bootstrap.layout.East = function(config){
33381     config.region = "east";
33382     config.cursor = "e-resize";
33383     Roo.bootstrap.layout.Split.call(this, config);
33384     if(this.split){
33385         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33386         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33387         this.split.el.addClass("roo-layout-split-h");
33388     }
33389     var size = config.initialSize || config.width;
33390     if(typeof size != "undefined"){
33391         this.el.setWidth(size);
33392     }
33393 };
33394 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33395     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33396     getBox : function(){
33397         if(this.collapsed){
33398             return this.collapsedEl.getBox();
33399         }
33400         var box = this.el.getBox();
33401         if(this.split){
33402             var sw = this.split.el.getWidth();
33403             box.width += sw;
33404             box.x -= sw;
33405         }
33406         return box;
33407     },
33408
33409     updateBox : function(box){
33410         if(this.split && !this.collapsed){
33411             var sw = this.split.el.getWidth();
33412             box.width -= sw;
33413             this.split.el.setLeft(box.x);
33414             this.split.el.setTop(box.y);
33415             this.split.el.setHeight(box.height);
33416             box.x += sw;
33417         }
33418         if(this.collapsed){
33419             this.updateBody(null, box.height);
33420         }
33421         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33422     }
33423 });
33424
33425 Roo.bootstrap.layout.West = function(config){
33426     config.region = "west";
33427     config.cursor = "w-resize";
33428     
33429     Roo.bootstrap.layout.Split.call(this, config);
33430     if(this.split){
33431         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33432         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33433         this.split.el.addClass("roo-layout-split-h");
33434     }
33435     
33436 };
33437 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33438     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33439     
33440     onRender: function(ctr, pos)
33441     {
33442         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33443         var size = this.config.initialSize || this.config.width;
33444         if(typeof size != "undefined"){
33445             this.el.setWidth(size);
33446         }
33447     },
33448     
33449     getBox : function(){
33450         if(this.collapsed){
33451             return this.collapsedEl.getBox();
33452         }
33453         var box = this.el.getBox();
33454         if(this.split){
33455             box.width += this.split.el.getWidth();
33456         }
33457         return box;
33458     },
33459     
33460     updateBox : function(box){
33461         if(this.split && !this.collapsed){
33462             var sw = this.split.el.getWidth();
33463             box.width -= sw;
33464             this.split.el.setLeft(box.x+box.width);
33465             this.split.el.setTop(box.y);
33466             this.split.el.setHeight(box.height);
33467         }
33468         if(this.collapsed){
33469             this.updateBody(null, box.height);
33470         }
33471         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33472     }
33473 });
33474 Roo.namespace("Roo.bootstrap.panel");/*
33475  * Based on:
33476  * Ext JS Library 1.1.1
33477  * Copyright(c) 2006-2007, Ext JS, LLC.
33478  *
33479  * Originally Released Under LGPL - original licence link has changed is not relivant.
33480  *
33481  * Fork - LGPL
33482  * <script type="text/javascript">
33483  */
33484 /**
33485  * @class Roo.ContentPanel
33486  * @extends Roo.util.Observable
33487  * A basic ContentPanel element.
33488  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33489  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33490  * @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
33491  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33492  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33493  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33494  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33495  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33496  * @cfg {String} title          The title for this panel
33497  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33498  * @cfg {String} url            Calls {@link #setUrl} with this value
33499  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33500  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33501  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33502  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33503
33504  * @constructor
33505  * Create a new ContentPanel.
33506  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33507  * @param {String/Object} config A string to set only the title or a config object
33508  * @param {String} content (optional) Set the HTML content for this panel
33509  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33510  */
33511 Roo.bootstrap.panel.Content = function( config){
33512     
33513     var el = config.el;
33514     var content = config.content;
33515
33516     if(config.autoCreate){ // xtype is available if this is called from factory
33517         el = Roo.id();
33518     }
33519     this.el = Roo.get(el);
33520     if(!this.el && config && config.autoCreate){
33521         if(typeof config.autoCreate == "object"){
33522             if(!config.autoCreate.id){
33523                 config.autoCreate.id = config.id||el;
33524             }
33525             this.el = Roo.DomHelper.append(document.body,
33526                         config.autoCreate, true);
33527         }else{
33528             var elcfg =  {   tag: "div",
33529                             cls: "roo-layout-inactive-content",
33530                             id: config.id||el
33531                             };
33532             if (config.html) {
33533                 elcfg.html = config.html;
33534                 
33535             }
33536                         
33537             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33538         }
33539     } 
33540     this.closable = false;
33541     this.loaded = false;
33542     this.active = false;
33543    
33544       
33545     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33546         
33547         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33548         
33549         this.wrapEl = this.el.wrap();
33550         var ti = [];
33551         if (config.toolbar.items) {
33552             ti = config.toolbar.items ;
33553             delete config.toolbar.items ;
33554         }
33555         
33556         var nitems = [];
33557         this.toolbar.render(this.wrapEl, 'before');
33558         for(var i =0;i < ti.length;i++) {
33559           //  Roo.log(['add child', items[i]]);
33560             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33561         }
33562         this.toolbar.items = nitems;
33563         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33564         delete config.toolbar;
33565         
33566     }
33567     /*
33568     // xtype created footer. - not sure if will work as we normally have to render first..
33569     if (this.footer && !this.footer.el && this.footer.xtype) {
33570         if (!this.wrapEl) {
33571             this.wrapEl = this.el.wrap();
33572         }
33573     
33574         this.footer.container = this.wrapEl.createChild();
33575          
33576         this.footer = Roo.factory(this.footer, Roo);
33577         
33578     }
33579     */
33580     
33581      if(typeof config == "string"){
33582         this.title = config;
33583     }else{
33584         Roo.apply(this, config);
33585     }
33586     
33587     if(this.resizeEl){
33588         this.resizeEl = Roo.get(this.resizeEl, true);
33589     }else{
33590         this.resizeEl = this.el;
33591     }
33592     // handle view.xtype
33593     
33594  
33595     
33596     
33597     this.addEvents({
33598         /**
33599          * @event activate
33600          * Fires when this panel is activated. 
33601          * @param {Roo.ContentPanel} this
33602          */
33603         "activate" : true,
33604         /**
33605          * @event deactivate
33606          * Fires when this panel is activated. 
33607          * @param {Roo.ContentPanel} this
33608          */
33609         "deactivate" : true,
33610
33611         /**
33612          * @event resize
33613          * Fires when this panel is resized if fitToFrame is true.
33614          * @param {Roo.ContentPanel} this
33615          * @param {Number} width The width after any component adjustments
33616          * @param {Number} height The height after any component adjustments
33617          */
33618         "resize" : true,
33619         
33620          /**
33621          * @event render
33622          * Fires when this tab is created
33623          * @param {Roo.ContentPanel} this
33624          */
33625         "render" : true
33626         
33627         
33628         
33629     });
33630     
33631
33632     
33633     
33634     if(this.autoScroll){
33635         this.resizeEl.setStyle("overflow", "auto");
33636     } else {
33637         // fix randome scrolling
33638         //this.el.on('scroll', function() {
33639         //    Roo.log('fix random scolling');
33640         //    this.scrollTo('top',0); 
33641         //});
33642     }
33643     content = content || this.content;
33644     if(content){
33645         this.setContent(content);
33646     }
33647     if(config && config.url){
33648         this.setUrl(this.url, this.params, this.loadOnce);
33649     }
33650     
33651     
33652     
33653     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33654     
33655     if (this.view && typeof(this.view.xtype) != 'undefined') {
33656         this.view.el = this.el.appendChild(document.createElement("div"));
33657         this.view = Roo.factory(this.view); 
33658         this.view.render  &&  this.view.render(false, '');  
33659     }
33660     
33661     
33662     this.fireEvent('render', this);
33663 };
33664
33665 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33666     tabTip:'',
33667     setRegion : function(region){
33668         this.region = region;
33669         if(region){
33670            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33671         }else{
33672            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33673         } 
33674     },
33675     
33676     /**
33677      * Returns the toolbar for this Panel if one was configured. 
33678      * @return {Roo.Toolbar} 
33679      */
33680     getToolbar : function(){
33681         return this.toolbar;
33682     },
33683     
33684     setActiveState : function(active){
33685         this.active = active;
33686         if(!active){
33687             this.fireEvent("deactivate", this);
33688         }else{
33689             this.fireEvent("activate", this);
33690         }
33691     },
33692     /**
33693      * Updates this panel's element
33694      * @param {String} content The new content
33695      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33696     */
33697     setContent : function(content, loadScripts){
33698         this.el.update(content, loadScripts);
33699     },
33700
33701     ignoreResize : function(w, h){
33702         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33703             return true;
33704         }else{
33705             this.lastSize = {width: w, height: h};
33706             return false;
33707         }
33708     },
33709     /**
33710      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33711      * @return {Roo.UpdateManager} The UpdateManager
33712      */
33713     getUpdateManager : function(){
33714         return this.el.getUpdateManager();
33715     },
33716      /**
33717      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33718      * @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:
33719 <pre><code>
33720 panel.load({
33721     url: "your-url.php",
33722     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33723     callback: yourFunction,
33724     scope: yourObject, //(optional scope)
33725     discardUrl: false,
33726     nocache: false,
33727     text: "Loading...",
33728     timeout: 30,
33729     scripts: false
33730 });
33731 </code></pre>
33732      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33733      * 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.
33734      * @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}
33735      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33736      * @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.
33737      * @return {Roo.ContentPanel} this
33738      */
33739     load : function(){
33740         var um = this.el.getUpdateManager();
33741         um.update.apply(um, arguments);
33742         return this;
33743     },
33744
33745
33746     /**
33747      * 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.
33748      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33749      * @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)
33750      * @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)
33751      * @return {Roo.UpdateManager} The UpdateManager
33752      */
33753     setUrl : function(url, params, loadOnce){
33754         if(this.refreshDelegate){
33755             this.removeListener("activate", this.refreshDelegate);
33756         }
33757         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33758         this.on("activate", this.refreshDelegate);
33759         return this.el.getUpdateManager();
33760     },
33761     
33762     _handleRefresh : function(url, params, loadOnce){
33763         if(!loadOnce || !this.loaded){
33764             var updater = this.el.getUpdateManager();
33765             updater.update(url, params, this._setLoaded.createDelegate(this));
33766         }
33767     },
33768     
33769     _setLoaded : function(){
33770         this.loaded = true;
33771     }, 
33772     
33773     /**
33774      * Returns this panel's id
33775      * @return {String} 
33776      */
33777     getId : function(){
33778         return this.el.id;
33779     },
33780     
33781     /** 
33782      * Returns this panel's element - used by regiosn to add.
33783      * @return {Roo.Element} 
33784      */
33785     getEl : function(){
33786         return this.wrapEl || this.el;
33787     },
33788     
33789    
33790     
33791     adjustForComponents : function(width, height)
33792     {
33793         //Roo.log('adjustForComponents ');
33794         if(this.resizeEl != this.el){
33795             width -= this.el.getFrameWidth('lr');
33796             height -= this.el.getFrameWidth('tb');
33797         }
33798         if(this.toolbar){
33799             var te = this.toolbar.getEl();
33800             height -= te.getHeight();
33801             te.setWidth(width);
33802         }
33803         if(this.footer){
33804             var te = this.footer.getEl();
33805             Roo.log("footer:" + te.getHeight());
33806             
33807             height -= te.getHeight();
33808             te.setWidth(width);
33809         }
33810         
33811         
33812         if(this.adjustments){
33813             width += this.adjustments[0];
33814             height += this.adjustments[1];
33815         }
33816         return {"width": width, "height": height};
33817     },
33818     
33819     setSize : function(width, height){
33820         if(this.fitToFrame && !this.ignoreResize(width, height)){
33821             if(this.fitContainer && this.resizeEl != this.el){
33822                 this.el.setSize(width, height);
33823             }
33824             var size = this.adjustForComponents(width, height);
33825             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33826             this.fireEvent('resize', this, size.width, size.height);
33827         }
33828     },
33829     
33830     /**
33831      * Returns this panel's title
33832      * @return {String} 
33833      */
33834     getTitle : function(){
33835         return this.title;
33836     },
33837     
33838     /**
33839      * Set this panel's title
33840      * @param {String} title
33841      */
33842     setTitle : function(title){
33843         this.title = title;
33844         if(this.region){
33845             this.region.updatePanelTitle(this, title);
33846         }
33847     },
33848     
33849     /**
33850      * Returns true is this panel was configured to be closable
33851      * @return {Boolean} 
33852      */
33853     isClosable : function(){
33854         return this.closable;
33855     },
33856     
33857     beforeSlide : function(){
33858         this.el.clip();
33859         this.resizeEl.clip();
33860     },
33861     
33862     afterSlide : function(){
33863         this.el.unclip();
33864         this.resizeEl.unclip();
33865     },
33866     
33867     /**
33868      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33869      *   Will fail silently if the {@link #setUrl} method has not been called.
33870      *   This does not activate the panel, just updates its content.
33871      */
33872     refresh : function(){
33873         if(this.refreshDelegate){
33874            this.loaded = false;
33875            this.refreshDelegate();
33876         }
33877     },
33878     
33879     /**
33880      * Destroys this panel
33881      */
33882     destroy : function(){
33883         this.el.removeAllListeners();
33884         var tempEl = document.createElement("span");
33885         tempEl.appendChild(this.el.dom);
33886         tempEl.innerHTML = "";
33887         this.el.remove();
33888         this.el = null;
33889     },
33890     
33891     /**
33892      * form - if the content panel contains a form - this is a reference to it.
33893      * @type {Roo.form.Form}
33894      */
33895     form : false,
33896     /**
33897      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33898      *    This contains a reference to it.
33899      * @type {Roo.View}
33900      */
33901     view : false,
33902     
33903       /**
33904      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33905      * <pre><code>
33906
33907 layout.addxtype({
33908        xtype : 'Form',
33909        items: [ .... ]
33910    }
33911 );
33912
33913 </code></pre>
33914      * @param {Object} cfg Xtype definition of item to add.
33915      */
33916     
33917     
33918     getChildContainer: function () {
33919         return this.getEl();
33920     }
33921     
33922     
33923     /*
33924         var  ret = new Roo.factory(cfg);
33925         return ret;
33926         
33927         
33928         // add form..
33929         if (cfg.xtype.match(/^Form$/)) {
33930             
33931             var el;
33932             //if (this.footer) {
33933             //    el = this.footer.container.insertSibling(false, 'before');
33934             //} else {
33935                 el = this.el.createChild();
33936             //}
33937
33938             this.form = new  Roo.form.Form(cfg);
33939             
33940             
33941             if ( this.form.allItems.length) {
33942                 this.form.render(el.dom);
33943             }
33944             return this.form;
33945         }
33946         // should only have one of theses..
33947         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33948             // views.. should not be just added - used named prop 'view''
33949             
33950             cfg.el = this.el.appendChild(document.createElement("div"));
33951             // factory?
33952             
33953             var ret = new Roo.factory(cfg);
33954              
33955              ret.render && ret.render(false, ''); // render blank..
33956             this.view = ret;
33957             return ret;
33958         }
33959         return false;
33960     }
33961     \*/
33962 });
33963  
33964 /**
33965  * @class Roo.bootstrap.panel.Grid
33966  * @extends Roo.bootstrap.panel.Content
33967  * @constructor
33968  * Create a new GridPanel.
33969  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33970  * @param {Object} config A the config object
33971   
33972  */
33973
33974
33975
33976 Roo.bootstrap.panel.Grid = function(config)
33977 {
33978     
33979       
33980     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33981         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
33982
33983     config.el = this.wrapper;
33984     //this.el = this.wrapper;
33985     
33986       if (config.container) {
33987         // ctor'ed from a Border/panel.grid
33988         
33989         
33990         this.wrapper.setStyle("overflow", "hidden");
33991         this.wrapper.addClass('roo-grid-container');
33992
33993     }
33994     
33995     
33996     if(config.toolbar){
33997         var tool_el = this.wrapper.createChild();    
33998         this.toolbar = Roo.factory(config.toolbar);
33999         var ti = [];
34000         if (config.toolbar.items) {
34001             ti = config.toolbar.items ;
34002             delete config.toolbar.items ;
34003         }
34004         
34005         var nitems = [];
34006         this.toolbar.render(tool_el);
34007         for(var i =0;i < ti.length;i++) {
34008           //  Roo.log(['add child', items[i]]);
34009             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34010         }
34011         this.toolbar.items = nitems;
34012         
34013         delete config.toolbar;
34014     }
34015     
34016     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34017     config.grid.scrollBody = true;;
34018     config.grid.monitorWindowResize = false; // turn off autosizing
34019     config.grid.autoHeight = false;
34020     config.grid.autoWidth = false;
34021     
34022     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34023     
34024     if (config.background) {
34025         // render grid on panel activation (if panel background)
34026         this.on('activate', function(gp) {
34027             if (!gp.grid.rendered) {
34028                 gp.grid.render(el);
34029                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34030
34031             }
34032         });
34033             
34034     } else {
34035         this.grid.render(this.wrapper);
34036         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34037
34038     }
34039     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34040     // ??? needed ??? config.el = this.wrapper;
34041     
34042     
34043     
34044   
34045     // xtype created footer. - not sure if will work as we normally have to render first..
34046     if (this.footer && !this.footer.el && this.footer.xtype) {
34047         
34048         var ctr = this.grid.getView().getFooterPanel(true);
34049         this.footer.dataSource = this.grid.dataSource;
34050         this.footer = Roo.factory(this.footer, Roo);
34051         this.footer.render(ctr);
34052         
34053     }
34054     
34055     
34056     
34057     
34058      
34059 };
34060
34061 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34062     getId : function(){
34063         return this.grid.id;
34064     },
34065     
34066     /**
34067      * Returns the grid for this panel
34068      * @return {Roo.bootstrap.Table} 
34069      */
34070     getGrid : function(){
34071         return this.grid;    
34072     },
34073     
34074     setSize : function(width, height){
34075         if(!this.ignoreResize(width, height)){
34076             var grid = this.grid;
34077             var size = this.adjustForComponents(width, height);
34078             var gridel = grid.getGridEl();
34079             gridel.setSize(size.width, size.height);
34080             /*
34081             var thd = grid.getGridEl().select('thead',true).first();
34082             var tbd = grid.getGridEl().select('tbody', true).first();
34083             if (tbd) {
34084                 tbd.setSize(width, height - thd.getHeight());
34085             }
34086             */
34087             grid.autoSize();
34088         }
34089     },
34090      
34091     
34092     
34093     beforeSlide : function(){
34094         this.grid.getView().scroller.clip();
34095     },
34096     
34097     afterSlide : function(){
34098         this.grid.getView().scroller.unclip();
34099     },
34100     
34101     destroy : function(){
34102         this.grid.destroy();
34103         delete this.grid;
34104         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34105     }
34106 });
34107
34108 /**
34109  * @class Roo.bootstrap.panel.Nest
34110  * @extends Roo.bootstrap.panel.Content
34111  * @constructor
34112  * Create a new Panel, that can contain a layout.Border.
34113  * 
34114  * 
34115  * @param {Roo.BorderLayout} layout The layout for this panel
34116  * @param {String/Object} config A string to set only the title or a config object
34117  */
34118 Roo.bootstrap.panel.Nest = function(config)
34119 {
34120     // construct with only one argument..
34121     /* FIXME - implement nicer consturctors
34122     if (layout.layout) {
34123         config = layout;
34124         layout = config.layout;
34125         delete config.layout;
34126     }
34127     if (layout.xtype && !layout.getEl) {
34128         // then layout needs constructing..
34129         layout = Roo.factory(layout, Roo);
34130     }
34131     */
34132     
34133     config.el =  config.layout.getEl();
34134     
34135     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34136     
34137     config.layout.monitorWindowResize = false; // turn off autosizing
34138     this.layout = config.layout;
34139     this.layout.getEl().addClass("roo-layout-nested-layout");
34140     
34141     
34142     
34143     
34144 };
34145
34146 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34147
34148     setSize : function(width, height){
34149         if(!this.ignoreResize(width, height)){
34150             var size = this.adjustForComponents(width, height);
34151             var el = this.layout.getEl();
34152             el.setSize(size.width, size.height);
34153             var touch = el.dom.offsetWidth;
34154             this.layout.layout();
34155             // ie requires a double layout on the first pass
34156             if(Roo.isIE && !this.initialized){
34157                 this.initialized = true;
34158                 this.layout.layout();
34159             }
34160         }
34161     },
34162     
34163     // activate all subpanels if not currently active..
34164     
34165     setActiveState : function(active){
34166         this.active = active;
34167         if(!active){
34168             this.fireEvent("deactivate", this);
34169             return;
34170         }
34171         
34172         this.fireEvent("activate", this);
34173         // not sure if this should happen before or after..
34174         if (!this.layout) {
34175             return; // should not happen..
34176         }
34177         var reg = false;
34178         for (var r in this.layout.regions) {
34179             reg = this.layout.getRegion(r);
34180             if (reg.getActivePanel()) {
34181                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34182                 reg.setActivePanel(reg.getActivePanel());
34183                 continue;
34184             }
34185             if (!reg.panels.length) {
34186                 continue;
34187             }
34188             reg.showPanel(reg.getPanel(0));
34189         }
34190         
34191         
34192         
34193         
34194     },
34195     
34196     /**
34197      * Returns the nested BorderLayout for this panel
34198      * @return {Roo.BorderLayout} 
34199      */
34200     getLayout : function(){
34201         return this.layout;
34202     },
34203     
34204      /**
34205      * Adds a xtype elements to the layout of the nested panel
34206      * <pre><code>
34207
34208 panel.addxtype({
34209        xtype : 'ContentPanel',
34210        region: 'west',
34211        items: [ .... ]
34212    }
34213 );
34214
34215 panel.addxtype({
34216         xtype : 'NestedLayoutPanel',
34217         region: 'west',
34218         layout: {
34219            center: { },
34220            west: { }   
34221         },
34222         items : [ ... list of content panels or nested layout panels.. ]
34223    }
34224 );
34225 </code></pre>
34226      * @param {Object} cfg Xtype definition of item to add.
34227      */
34228     addxtype : function(cfg) {
34229         return this.layout.addxtype(cfg);
34230     
34231     }
34232 });        /*
34233  * Based on:
34234  * Ext JS Library 1.1.1
34235  * Copyright(c) 2006-2007, Ext JS, LLC.
34236  *
34237  * Originally Released Under LGPL - original licence link has changed is not relivant.
34238  *
34239  * Fork - LGPL
34240  * <script type="text/javascript">
34241  */
34242 /**
34243  * @class Roo.TabPanel
34244  * @extends Roo.util.Observable
34245  * A lightweight tab container.
34246  * <br><br>
34247  * Usage:
34248  * <pre><code>
34249 // basic tabs 1, built from existing content
34250 var tabs = new Roo.TabPanel("tabs1");
34251 tabs.addTab("script", "View Script");
34252 tabs.addTab("markup", "View Markup");
34253 tabs.activate("script");
34254
34255 // more advanced tabs, built from javascript
34256 var jtabs = new Roo.TabPanel("jtabs");
34257 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34258
34259 // set up the UpdateManager
34260 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34261 var updater = tab2.getUpdateManager();
34262 updater.setDefaultUrl("ajax1.htm");
34263 tab2.on('activate', updater.refresh, updater, true);
34264
34265 // Use setUrl for Ajax loading
34266 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34267 tab3.setUrl("ajax2.htm", null, true);
34268
34269 // Disabled tab
34270 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34271 tab4.disable();
34272
34273 jtabs.activate("jtabs-1");
34274  * </code></pre>
34275  * @constructor
34276  * Create a new TabPanel.
34277  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34278  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34279  */
34280 Roo.bootstrap.panel.Tabs = function(config){
34281     /**
34282     * The container element for this TabPanel.
34283     * @type Roo.Element
34284     */
34285     this.el = Roo.get(config.el);
34286     delete config.el;
34287     if(config){
34288         if(typeof config == "boolean"){
34289             this.tabPosition = config ? "bottom" : "top";
34290         }else{
34291             Roo.apply(this, config);
34292         }
34293     }
34294     
34295     if(this.tabPosition == "bottom"){
34296         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34297         this.el.addClass("roo-tabs-bottom");
34298     }
34299     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34300     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34301     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34302     if(Roo.isIE){
34303         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34304     }
34305     if(this.tabPosition != "bottom"){
34306         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34307          * @type Roo.Element
34308          */
34309         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34310         this.el.addClass("roo-tabs-top");
34311     }
34312     this.items = [];
34313
34314     this.bodyEl.setStyle("position", "relative");
34315
34316     this.active = null;
34317     this.activateDelegate = this.activate.createDelegate(this);
34318
34319     this.addEvents({
34320         /**
34321          * @event tabchange
34322          * Fires when the active tab changes
34323          * @param {Roo.TabPanel} this
34324          * @param {Roo.TabPanelItem} activePanel The new active tab
34325          */
34326         "tabchange": true,
34327         /**
34328          * @event beforetabchange
34329          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34330          * @param {Roo.TabPanel} this
34331          * @param {Object} e Set cancel to true on this object to cancel the tab change
34332          * @param {Roo.TabPanelItem} tab The tab being changed to
34333          */
34334         "beforetabchange" : true
34335     });
34336
34337     Roo.EventManager.onWindowResize(this.onResize, this);
34338     this.cpad = this.el.getPadding("lr");
34339     this.hiddenCount = 0;
34340
34341
34342     // toolbar on the tabbar support...
34343     if (this.toolbar) {
34344         alert("no toolbar support yet");
34345         this.toolbar  = false;
34346         /*
34347         var tcfg = this.toolbar;
34348         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34349         this.toolbar = new Roo.Toolbar(tcfg);
34350         if (Roo.isSafari) {
34351             var tbl = tcfg.container.child('table', true);
34352             tbl.setAttribute('width', '100%');
34353         }
34354         */
34355         
34356     }
34357    
34358
34359
34360     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34361 };
34362
34363 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34364     /*
34365      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34366      */
34367     tabPosition : "top",
34368     /*
34369      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34370      */
34371     currentTabWidth : 0,
34372     /*
34373      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34374      */
34375     minTabWidth : 40,
34376     /*
34377      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34378      */
34379     maxTabWidth : 250,
34380     /*
34381      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34382      */
34383     preferredTabWidth : 175,
34384     /*
34385      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34386      */
34387     resizeTabs : false,
34388     /*
34389      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34390      */
34391     monitorResize : true,
34392     /*
34393      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34394      */
34395     toolbar : false,
34396
34397     /**
34398      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34399      * @param {String} id The id of the div to use <b>or create</b>
34400      * @param {String} text The text for the tab
34401      * @param {String} content (optional) Content to put in the TabPanelItem body
34402      * @param {Boolean} closable (optional) True to create a close icon on the tab
34403      * @return {Roo.TabPanelItem} The created TabPanelItem
34404      */
34405     addTab : function(id, text, content, closable)
34406     {
34407         var item = new Roo.bootstrap.panel.TabItem({
34408             panel: this,
34409             id : id,
34410             text : text,
34411             closable : closable
34412         });
34413         this.addTabItem(item);
34414         if(content){
34415             item.setContent(content);
34416         }
34417         return item;
34418     },
34419
34420     /**
34421      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34422      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34423      * @return {Roo.TabPanelItem}
34424      */
34425     getTab : function(id){
34426         return this.items[id];
34427     },
34428
34429     /**
34430      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34431      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34432      */
34433     hideTab : function(id){
34434         var t = this.items[id];
34435         if(!t.isHidden()){
34436            t.setHidden(true);
34437            this.hiddenCount++;
34438            this.autoSizeTabs();
34439         }
34440     },
34441
34442     /**
34443      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34444      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34445      */
34446     unhideTab : function(id){
34447         var t = this.items[id];
34448         if(t.isHidden()){
34449            t.setHidden(false);
34450            this.hiddenCount--;
34451            this.autoSizeTabs();
34452         }
34453     },
34454
34455     /**
34456      * Adds an existing {@link Roo.TabPanelItem}.
34457      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34458      */
34459     addTabItem : function(item){
34460         this.items[item.id] = item;
34461         this.items.push(item);
34462       //  if(this.resizeTabs){
34463     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34464   //         this.autoSizeTabs();
34465 //        }else{
34466 //            item.autoSize();
34467        // }
34468     },
34469
34470     /**
34471      * Removes a {@link Roo.TabPanelItem}.
34472      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34473      */
34474     removeTab : function(id){
34475         var items = this.items;
34476         var tab = items[id];
34477         if(!tab) { return; }
34478         var index = items.indexOf(tab);
34479         if(this.active == tab && items.length > 1){
34480             var newTab = this.getNextAvailable(index);
34481             if(newTab) {
34482                 newTab.activate();
34483             }
34484         }
34485         this.stripEl.dom.removeChild(tab.pnode.dom);
34486         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34487             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34488         }
34489         items.splice(index, 1);
34490         delete this.items[tab.id];
34491         tab.fireEvent("close", tab);
34492         tab.purgeListeners();
34493         this.autoSizeTabs();
34494     },
34495
34496     getNextAvailable : function(start){
34497         var items = this.items;
34498         var index = start;
34499         // look for a next tab that will slide over to
34500         // replace the one being removed
34501         while(index < items.length){
34502             var item = items[++index];
34503             if(item && !item.isHidden()){
34504                 return item;
34505             }
34506         }
34507         // if one isn't found select the previous tab (on the left)
34508         index = start;
34509         while(index >= 0){
34510             var item = items[--index];
34511             if(item && !item.isHidden()){
34512                 return item;
34513             }
34514         }
34515         return null;
34516     },
34517
34518     /**
34519      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34520      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34521      */
34522     disableTab : function(id){
34523         var tab = this.items[id];
34524         if(tab && this.active != tab){
34525             tab.disable();
34526         }
34527     },
34528
34529     /**
34530      * Enables a {@link Roo.TabPanelItem} that is disabled.
34531      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34532      */
34533     enableTab : function(id){
34534         var tab = this.items[id];
34535         tab.enable();
34536     },
34537
34538     /**
34539      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34540      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34541      * @return {Roo.TabPanelItem} The TabPanelItem.
34542      */
34543     activate : function(id){
34544         var tab = this.items[id];
34545         if(!tab){
34546             return null;
34547         }
34548         if(tab == this.active || tab.disabled){
34549             return tab;
34550         }
34551         var e = {};
34552         this.fireEvent("beforetabchange", this, e, tab);
34553         if(e.cancel !== true && !tab.disabled){
34554             if(this.active){
34555                 this.active.hide();
34556             }
34557             this.active = this.items[id];
34558             this.active.show();
34559             this.fireEvent("tabchange", this, this.active);
34560         }
34561         return tab;
34562     },
34563
34564     /**
34565      * Gets the active {@link Roo.TabPanelItem}.
34566      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34567      */
34568     getActiveTab : function(){
34569         return this.active;
34570     },
34571
34572     /**
34573      * Updates the tab body element to fit the height of the container element
34574      * for overflow scrolling
34575      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34576      */
34577     syncHeight : function(targetHeight){
34578         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34579         var bm = this.bodyEl.getMargins();
34580         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34581         this.bodyEl.setHeight(newHeight);
34582         return newHeight;
34583     },
34584
34585     onResize : function(){
34586         if(this.monitorResize){
34587             this.autoSizeTabs();
34588         }
34589     },
34590
34591     /**
34592      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34593      */
34594     beginUpdate : function(){
34595         this.updating = true;
34596     },
34597
34598     /**
34599      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34600      */
34601     endUpdate : function(){
34602         this.updating = false;
34603         this.autoSizeTabs();
34604     },
34605
34606     /**
34607      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34608      */
34609     autoSizeTabs : function(){
34610         var count = this.items.length;
34611         var vcount = count - this.hiddenCount;
34612         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34613             return;
34614         }
34615         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34616         var availWidth = Math.floor(w / vcount);
34617         var b = this.stripBody;
34618         if(b.getWidth() > w){
34619             var tabs = this.items;
34620             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34621             if(availWidth < this.minTabWidth){
34622                 /*if(!this.sleft){    // incomplete scrolling code
34623                     this.createScrollButtons();
34624                 }
34625                 this.showScroll();
34626                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34627             }
34628         }else{
34629             if(this.currentTabWidth < this.preferredTabWidth){
34630                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34631             }
34632         }
34633     },
34634
34635     /**
34636      * Returns the number of tabs in this TabPanel.
34637      * @return {Number}
34638      */
34639      getCount : function(){
34640          return this.items.length;
34641      },
34642
34643     /**
34644      * Resizes all the tabs to the passed width
34645      * @param {Number} The new width
34646      */
34647     setTabWidth : function(width){
34648         this.currentTabWidth = width;
34649         for(var i = 0, len = this.items.length; i < len; i++) {
34650                 if(!this.items[i].isHidden()) {
34651                 this.items[i].setWidth(width);
34652             }
34653         }
34654     },
34655
34656     /**
34657      * Destroys this TabPanel
34658      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34659      */
34660     destroy : function(removeEl){
34661         Roo.EventManager.removeResizeListener(this.onResize, this);
34662         for(var i = 0, len = this.items.length; i < len; i++){
34663             this.items[i].purgeListeners();
34664         }
34665         if(removeEl === true){
34666             this.el.update("");
34667             this.el.remove();
34668         }
34669     },
34670     
34671     createStrip : function(container)
34672     {
34673         var strip = document.createElement("nav");
34674         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34675         container.appendChild(strip);
34676         return strip;
34677     },
34678     
34679     createStripList : function(strip)
34680     {
34681         // div wrapper for retard IE
34682         // returns the "tr" element.
34683         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34684         //'<div class="x-tabs-strip-wrap">'+
34685           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34686           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34687         return strip.firstChild; //.firstChild.firstChild.firstChild;
34688     },
34689     createBody : function(container)
34690     {
34691         var body = document.createElement("div");
34692         Roo.id(body, "tab-body");
34693         //Roo.fly(body).addClass("x-tabs-body");
34694         Roo.fly(body).addClass("tab-content");
34695         container.appendChild(body);
34696         return body;
34697     },
34698     createItemBody :function(bodyEl, id){
34699         var body = Roo.getDom(id);
34700         if(!body){
34701             body = document.createElement("div");
34702             body.id = id;
34703         }
34704         //Roo.fly(body).addClass("x-tabs-item-body");
34705         Roo.fly(body).addClass("tab-pane");
34706          bodyEl.insertBefore(body, bodyEl.firstChild);
34707         return body;
34708     },
34709     /** @private */
34710     createStripElements :  function(stripEl, text, closable)
34711     {
34712         var td = document.createElement("li"); // was td..
34713         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34714         //stripEl.appendChild(td);
34715         /*if(closable){
34716             td.className = "x-tabs-closable";
34717             if(!this.closeTpl){
34718                 this.closeTpl = new Roo.Template(
34719                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34720                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34721                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34722                 );
34723             }
34724             var el = this.closeTpl.overwrite(td, {"text": text});
34725             var close = el.getElementsByTagName("div")[0];
34726             var inner = el.getElementsByTagName("em")[0];
34727             return {"el": el, "close": close, "inner": inner};
34728         } else {
34729         */
34730         // not sure what this is..
34731             if(!this.tabTpl){
34732                 //this.tabTpl = new Roo.Template(
34733                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34734                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34735                 //);
34736                 this.tabTpl = new Roo.Template(
34737                    '<a href="#">' +
34738                    '<span unselectable="on"' +
34739                             (this.disableTooltips ? '' : ' title="{text}"') +
34740                             ' >{text}</span></span></a>'
34741                 );
34742                 
34743             }
34744             var el = this.tabTpl.overwrite(td, {"text": text});
34745             var inner = el.getElementsByTagName("span")[0];
34746             return {"el": el, "inner": inner};
34747         //}
34748     }
34749         
34750     
34751 });
34752
34753 /**
34754  * @class Roo.TabPanelItem
34755  * @extends Roo.util.Observable
34756  * Represents an individual item (tab plus body) in a TabPanel.
34757  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34758  * @param {String} id The id of this TabPanelItem
34759  * @param {String} text The text for the tab of this TabPanelItem
34760  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34761  */
34762 Roo.bootstrap.panel.TabItem = function(config){
34763     /**
34764      * The {@link Roo.TabPanel} this TabPanelItem belongs to
34765      * @type Roo.TabPanel
34766      */
34767     this.tabPanel = config.panel;
34768     /**
34769      * The id for this TabPanelItem
34770      * @type String
34771      */
34772     this.id = config.id;
34773     /** @private */
34774     this.disabled = false;
34775     /** @private */
34776     this.text = config.text;
34777     /** @private */
34778     this.loaded = false;
34779     this.closable = config.closable;
34780
34781     /**
34782      * The body element for this TabPanelItem.
34783      * @type Roo.Element
34784      */
34785     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34786     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34787     this.bodyEl.setStyle("display", "block");
34788     this.bodyEl.setStyle("zoom", "1");
34789     //this.hideAction();
34790
34791     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34792     /** @private */
34793     this.el = Roo.get(els.el);
34794     this.inner = Roo.get(els.inner, true);
34795     this.textEl = Roo.get(this.el.dom.firstChild, true);
34796     this.pnode = Roo.get(els.el.parentNode, true);
34797     this.el.on("mousedown", this.onTabMouseDown, this);
34798     this.el.on("click", this.onTabClick, this);
34799     /** @private */
34800     if(config.closable){
34801         var c = Roo.get(els.close, true);
34802         c.dom.title = this.closeText;
34803         c.addClassOnOver("close-over");
34804         c.on("click", this.closeClick, this);
34805      }
34806
34807     this.addEvents({
34808          /**
34809          * @event activate
34810          * Fires when this tab becomes the active tab.
34811          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34812          * @param {Roo.TabPanelItem} this
34813          */
34814         "activate": true,
34815         /**
34816          * @event beforeclose
34817          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34818          * @param {Roo.TabPanelItem} this
34819          * @param {Object} e Set cancel to true on this object to cancel the close.
34820          */
34821         "beforeclose": true,
34822         /**
34823          * @event close
34824          * Fires when this tab is closed.
34825          * @param {Roo.TabPanelItem} this
34826          */
34827          "close": true,
34828         /**
34829          * @event deactivate
34830          * Fires when this tab is no longer the active tab.
34831          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34832          * @param {Roo.TabPanelItem} this
34833          */
34834          "deactivate" : true
34835     });
34836     this.hidden = false;
34837
34838     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34839 };
34840
34841 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34842            {
34843     purgeListeners : function(){
34844        Roo.util.Observable.prototype.purgeListeners.call(this);
34845        this.el.removeAllListeners();
34846     },
34847     /**
34848      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34849      */
34850     show : function(){
34851         this.pnode.addClass("active");
34852         this.showAction();
34853         if(Roo.isOpera){
34854             this.tabPanel.stripWrap.repaint();
34855         }
34856         this.fireEvent("activate", this.tabPanel, this);
34857     },
34858
34859     /**
34860      * Returns true if this tab is the active tab.
34861      * @return {Boolean}
34862      */
34863     isActive : function(){
34864         return this.tabPanel.getActiveTab() == this;
34865     },
34866
34867     /**
34868      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34869      */
34870     hide : function(){
34871         this.pnode.removeClass("active");
34872         this.hideAction();
34873         this.fireEvent("deactivate", this.tabPanel, this);
34874     },
34875
34876     hideAction : function(){
34877         this.bodyEl.hide();
34878         this.bodyEl.setStyle("position", "absolute");
34879         this.bodyEl.setLeft("-20000px");
34880         this.bodyEl.setTop("-20000px");
34881     },
34882
34883     showAction : function(){
34884         this.bodyEl.setStyle("position", "relative");
34885         this.bodyEl.setTop("");
34886         this.bodyEl.setLeft("");
34887         this.bodyEl.show();
34888     },
34889
34890     /**
34891      * Set the tooltip for the tab.
34892      * @param {String} tooltip The tab's tooltip
34893      */
34894     setTooltip : function(text){
34895         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34896             this.textEl.dom.qtip = text;
34897             this.textEl.dom.removeAttribute('title');
34898         }else{
34899             this.textEl.dom.title = text;
34900         }
34901     },
34902
34903     onTabClick : function(e){
34904         e.preventDefault();
34905         this.tabPanel.activate(this.id);
34906     },
34907
34908     onTabMouseDown : function(e){
34909         e.preventDefault();
34910         this.tabPanel.activate(this.id);
34911     },
34912 /*
34913     getWidth : function(){
34914         return this.inner.getWidth();
34915     },
34916
34917     setWidth : function(width){
34918         var iwidth = width - this.pnode.getPadding("lr");
34919         this.inner.setWidth(iwidth);
34920         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34921         this.pnode.setWidth(width);
34922     },
34923 */
34924     /**
34925      * Show or hide the tab
34926      * @param {Boolean} hidden True to hide or false to show.
34927      */
34928     setHidden : function(hidden){
34929         this.hidden = hidden;
34930         this.pnode.setStyle("display", hidden ? "none" : "");
34931     },
34932
34933     /**
34934      * Returns true if this tab is "hidden"
34935      * @return {Boolean}
34936      */
34937     isHidden : function(){
34938         return this.hidden;
34939     },
34940
34941     /**
34942      * Returns the text for this tab
34943      * @return {String}
34944      */
34945     getText : function(){
34946         return this.text;
34947     },
34948     /*
34949     autoSize : function(){
34950         //this.el.beginMeasure();
34951         this.textEl.setWidth(1);
34952         /*
34953          *  #2804 [new] Tabs in Roojs
34954          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34955          */
34956         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34957         //this.el.endMeasure();
34958     //},
34959
34960     /**
34961      * Sets the text for the tab (Note: this also sets the tooltip text)
34962      * @param {String} text The tab's text and tooltip
34963      */
34964     setText : function(text){
34965         this.text = text;
34966         this.textEl.update(text);
34967         this.setTooltip(text);
34968         //if(!this.tabPanel.resizeTabs){
34969         //    this.autoSize();
34970         //}
34971     },
34972     /**
34973      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34974      */
34975     activate : function(){
34976         this.tabPanel.activate(this.id);
34977     },
34978
34979     /**
34980      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34981      */
34982     disable : function(){
34983         if(this.tabPanel.active != this){
34984             this.disabled = true;
34985             this.pnode.addClass("disabled");
34986         }
34987     },
34988
34989     /**
34990      * Enables this TabPanelItem if it was previously disabled.
34991      */
34992     enable : function(){
34993         this.disabled = false;
34994         this.pnode.removeClass("disabled");
34995     },
34996
34997     /**
34998      * Sets the content for this TabPanelItem.
34999      * @param {String} content The content
35000      * @param {Boolean} loadScripts true to look for and load scripts
35001      */
35002     setContent : function(content, loadScripts){
35003         this.bodyEl.update(content, loadScripts);
35004     },
35005
35006     /**
35007      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35008      * @return {Roo.UpdateManager} The UpdateManager
35009      */
35010     getUpdateManager : function(){
35011         return this.bodyEl.getUpdateManager();
35012     },
35013
35014     /**
35015      * Set a URL to be used to load the content for this TabPanelItem.
35016      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35017      * @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)
35018      * @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)
35019      * @return {Roo.UpdateManager} The UpdateManager
35020      */
35021     setUrl : function(url, params, loadOnce){
35022         if(this.refreshDelegate){
35023             this.un('activate', this.refreshDelegate);
35024         }
35025         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35026         this.on("activate", this.refreshDelegate);
35027         return this.bodyEl.getUpdateManager();
35028     },
35029
35030     /** @private */
35031     _handleRefresh : function(url, params, loadOnce){
35032         if(!loadOnce || !this.loaded){
35033             var updater = this.bodyEl.getUpdateManager();
35034             updater.update(url, params, this._setLoaded.createDelegate(this));
35035         }
35036     },
35037
35038     /**
35039      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35040      *   Will fail silently if the setUrl method has not been called.
35041      *   This does not activate the panel, just updates its content.
35042      */
35043     refresh : function(){
35044         if(this.refreshDelegate){
35045            this.loaded = false;
35046            this.refreshDelegate();
35047         }
35048     },
35049
35050     /** @private */
35051     _setLoaded : function(){
35052         this.loaded = true;
35053     },
35054
35055     /** @private */
35056     closeClick : function(e){
35057         var o = {};
35058         e.stopEvent();
35059         this.fireEvent("beforeclose", this, o);
35060         if(o.cancel !== true){
35061             this.tabPanel.removeTab(this.id);
35062         }
35063     },
35064     /**
35065      * The text displayed in the tooltip for the close icon.
35066      * @type String
35067      */
35068     closeText : "Close this tab"
35069 });