roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  * 
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  * 
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394     Roo.bootstrap.Body.superclass.constructor.call(this, config);
395     this.el = Roo.get(document.body);
396     if (this.cls && this.cls.length) {
397         Roo.get(document.body).addClass(this.cls);
398     }
399 };
400
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
402     
403     is_body : true,// just to make sure it's constructed?
404     
405         autoCreate : {
406         cls: 'container'
407     },
408     onRender : function(ct, position)
409     {
410        /* Roo.log("Roo.bootstrap.Body - onRender");
411         if (this.cls && this.cls.length) {
412             Roo.get(document.body).addClass(this.cls);
413         }
414         // style??? xttr???
415         */
416     }
417     
418     
419  
420    
421 });
422
423  /*
424  * - LGPL
425  *
426  * button group
427  * 
428  */
429
430
431 /**
432  * @class Roo.bootstrap.ButtonGroup
433  * @extends Roo.bootstrap.Component
434  * Bootstrap ButtonGroup class
435  * @cfg {String} size lg | sm | xs (default empty normal)
436  * @cfg {String} align vertical | justified  (default none)
437  * @cfg {String} direction up | down (default down)
438  * @cfg {Boolean} toolbar false | true
439  * @cfg {Boolean} btn true | false
440  * 
441  * 
442  * @constructor
443  * Create a new Input
444  * @param {Object} config The config object
445  */
446
447 Roo.bootstrap.ButtonGroup = function(config){
448     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
449 };
450
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
452     
453     size: '',
454     align: '',
455     direction: '',
456     toolbar: false,
457     btn: true,
458
459     getAutoCreate : function(){
460         var cfg = {
461             cls: 'btn-group',
462             html : null
463         };
464         
465         cfg.html = this.html || cfg.html;
466         
467         if (this.toolbar) {
468             cfg = {
469                 cls: 'btn-toolbar',
470                 html: null
471             };
472             
473             return cfg;
474         }
475         
476         if (['vertical','justified'].indexOf(this.align)!==-1) {
477             cfg.cls = 'btn-group-' + this.align;
478             
479             if (this.align == 'justified') {
480                 console.log(this.items);
481             }
482         }
483         
484         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485             cfg.cls += ' btn-group-' + this.size;
486         }
487         
488         if (this.direction == 'up') {
489             cfg.cls += ' dropup' ;
490         }
491         
492         return cfg;
493     }
494    
495 });
496
497  /*
498  * - LGPL
499  *
500  * button
501  * 
502  */
503
504 /**
505  * @class Roo.bootstrap.Button
506  * @extends Roo.bootstrap.Component
507  * Bootstrap Button class
508  * @cfg {String} html The button content
509  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
510  * @cfg {String} size ( lg | sm | xs)
511  * @cfg {String} tag ( a | input | submit)
512  * @cfg {String} href empty or href
513  * @cfg {Boolean} disabled default false;
514  * @cfg {Boolean} isClose default false;
515  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
516  * @cfg {String} badge text for badge
517  * @cfg {String} theme default 
518  * @cfg {Boolean} inverse 
519  * @cfg {Boolean} toggle 
520  * @cfg {String} ontext text for on toggle state
521  * @cfg {String} offtext text for off toggle state
522  * @cfg {Boolean} defaulton 
523  * @cfg {Boolean} preventDefault  default true
524  * @cfg {Boolean} removeClass remove the standard class..
525  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
526  * 
527  * @constructor
528  * Create a new button
529  * @param {Object} config The config object
530  */
531
532
533 Roo.bootstrap.Button = function(config){
534     Roo.bootstrap.Button.superclass.constructor.call(this, config);
535     this.addEvents({
536         // raw events
537         /**
538          * @event click
539          * When a butotn is pressed
540          * @param {Roo.bootstrap.Button} this
541          * @param {Roo.EventObject} e
542          */
543         "click" : true,
544          /**
545          * @event toggle
546          * After the button has been toggles
547          * @param {Roo.EventObject} e
548          * @param {boolean} pressed (also available as button.pressed)
549          */
550         "toggle" : true
551     });
552 };
553
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
555     html: false,
556     active: false,
557     weight: '',
558     size: '',
559     tag: 'button',
560     href: '',
561     disabled: false,
562     isClose: false,
563     glyphicon: '',
564     badge: '',
565     theme: 'default',
566     inverse: false,
567     
568     toggle: false,
569     ontext: 'ON',
570     offtext: 'OFF',
571     defaulton: true,
572     preventDefault: true,
573     removeClass: false,
574     name: false,
575     target: false,
576     
577     
578     pressed : null,
579      
580     
581     getAutoCreate : function(){
582         
583         var cfg = {
584             tag : 'button',
585             cls : 'roo-button',
586             html: ''
587         };
588         
589         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
591             this.tag = 'button';
592         } else {
593             cfg.tag = this.tag;
594         }
595         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
596         
597         if (this.toggle == true) {
598             cfg={
599                 tag: 'div',
600                 cls: 'slider-frame roo-button',
601                 cn: [
602                     {
603                         tag: 'span',
604                         'data-on-text':'ON',
605                         'data-off-text':'OFF',
606                         cls: 'slider-button',
607                         html: this.offtext
608                     }
609                 ]
610             };
611             
612             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613                 cfg.cls += ' '+this.weight;
614             }
615             
616             return cfg;
617         }
618         
619         if (this.isClose) {
620             cfg.cls += ' close';
621             
622             cfg["aria-hidden"] = true;
623             
624             cfg.html = "&times;";
625             
626             return cfg;
627         }
628         
629          
630         if (this.theme==='default') {
631             cfg.cls = 'btn roo-button';
632             
633             //if (this.parentType != 'Navbar') {
634             this.weight = this.weight.length ?  this.weight : 'default';
635             //}
636             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
637                 
638                 cfg.cls += ' btn-' + this.weight;
639             }
640         } else if (this.theme==='glow') {
641             
642             cfg.tag = 'a';
643             cfg.cls = 'btn-glow roo-button';
644             
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' ' + this.weight;
648             }
649         }
650    
651         
652         if (this.inverse) {
653             this.cls += ' inverse';
654         }
655         
656         
657         if (this.active) {
658             cfg.cls += ' active';
659         }
660         
661         if (this.disabled) {
662             cfg.disabled = 'disabled';
663         }
664         
665         if (this.items) {
666             Roo.log('changing to ul' );
667             cfg.tag = 'ul';
668             this.glyphicon = 'caret';
669         }
670         
671         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
672          
673         //gsRoo.log(this.parentType);
674         if (this.parentType === 'Navbar' && !this.parent().bar) {
675             Roo.log('changing to li?');
676             
677             cfg.tag = 'li';
678             
679             cfg.cls = '';
680             cfg.cn =  [{
681                 tag : 'a',
682                 cls : 'roo-button',
683                 html : this.html,
684                 href : this.href || '#'
685             }];
686             if (this.menu) {
687                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
688                 cfg.cls += ' dropdown';
689             }   
690             
691             delete cfg.html;
692             
693         }
694         
695        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
696         
697         if (this.glyphicon) {
698             cfg.html = ' ' + cfg.html;
699             
700             cfg.cn = [
701                 {
702                     tag: 'span',
703                     cls: 'glyphicon glyphicon-' + this.glyphicon
704                 }
705             ];
706         }
707         
708         if (this.badge) {
709             cfg.html += ' ';
710             
711             cfg.tag = 'a';
712             
713 //            cfg.cls='btn roo-button';
714             
715             cfg.href=this.href;
716             
717             var value = cfg.html;
718             
719             if(this.glyphicon){
720                 value = {
721                             tag: 'span',
722                             cls: 'glyphicon glyphicon-' + this.glyphicon,
723                             html: this.html
724                         };
725                 
726             }
727             
728             cfg.cn = [
729                 value,
730                 {
731                     tag: 'span',
732                     cls: 'badge',
733                     html: this.badge
734                 }
735             ];
736             
737             cfg.html='';
738         }
739         
740         if (this.menu) {
741             cfg.cls += ' dropdown';
742             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
743         }
744         
745         if (cfg.tag !== 'a' && this.href !== '') {
746             throw "Tag must be a to set href.";
747         } else if (this.href.length > 0) {
748             cfg.href = this.href;
749         }
750         
751         if(this.removeClass){
752             cfg.cls = '';
753         }
754         
755         if(this.target){
756             cfg.target = this.target;
757         }
758         
759         return cfg;
760     },
761     initEvents: function() {
762        // Roo.log('init events?');
763 //        Roo.log(this.el.dom);
764         // add the menu...
765         
766         if (typeof (this.menu) != 'undefined') {
767             this.menu.parentType = this.xtype;
768             this.menu.triggerEl = this.el;
769             this.addxtype(Roo.apply({}, this.menu));
770         }
771
772
773        if (this.el.hasClass('roo-button')) {
774             this.el.on('click', this.onClick, this);
775        } else {
776             this.el.select('.roo-button').on('click', this.onClick, this);
777        }
778        
779        if(this.removeClass){
780            this.el.on('click', this.onClick, this);
781        }
782        
783        this.el.enableDisplayMode();
784         
785     },
786     onClick : function(e)
787     {
788         if (this.disabled) {
789             return;
790         }
791         
792         
793         Roo.log('button on click ');
794         if(this.preventDefault){
795             e.preventDefault();
796         }
797         if (this.pressed === true || this.pressed === false) {
798             this.pressed = !this.pressed;
799             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800             this.fireEvent('toggle', this, e, this.pressed);
801         }
802         
803         
804         this.fireEvent('click', this, e);
805     },
806     
807     /**
808      * Enables this button
809      */
810     enable : function()
811     {
812         this.disabled = false;
813         this.el.removeClass('disabled');
814     },
815     
816     /**
817      * Disable this button
818      */
819     disable : function()
820     {
821         this.disabled = true;
822         this.el.addClass('disabled');
823     },
824      /**
825      * sets the active state on/off, 
826      * @param {Boolean} state (optional) Force a particular state
827      */
828     setActive : function(v) {
829         
830         this.el[v ? 'addClass' : 'removeClass']('active');
831     },
832      /**
833      * toggles the current active state 
834      */
835     toggleActive : function()
836     {
837        var active = this.el.hasClass('active');
838        this.setActive(!active);
839        
840         
841     },
842     setText : function(str)
843     {
844         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
845     },
846     getText : function()
847     {
848         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
849     },
850     hide: function() {
851        
852      
853         this.el.hide();   
854     },
855     show: function() {
856        
857         this.el.show();   
858     }
859     
860     
861 });
862
863  /*
864  * - LGPL
865  *
866  * column
867  * 
868  */
869
870 /**
871  * @class Roo.bootstrap.Column
872  * @extends Roo.bootstrap.Component
873  * Bootstrap Column class
874  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
882  *
883  * 
884  * @cfg {Boolean} hidden (true|false) hide the element
885  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886  * @cfg {String} fa (ban|check|...) font awesome icon
887  * @cfg {Number} fasize (1|2|....) font awsome size
888
889  * @cfg {String} icon (info-sign|check|...) glyphicon name
890
891  * @cfg {String} html content of column.
892  * 
893  * @constructor
894  * Create a new Column
895  * @param {Object} config The config object
896  */
897
898 Roo.bootstrap.Column = function(config){
899     Roo.bootstrap.Column.superclass.constructor.call(this, config);
900 };
901
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
903     
904     xs: false,
905     sm: false,
906     md: false,
907     lg: false,
908     xsoff: false,
909     smoff: false,
910     mdoff: false,
911     lgoff: false,
912     html: '',
913     offset: 0,
914     alert: false,
915     fa: false,
916     icon : false,
917     hidden : false,
918     fasize : 1,
919     
920     getAutoCreate : function(){
921         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
922         
923         cfg = {
924             tag: 'div',
925             cls: 'column'
926         };
927         
928         var settings=this;
929         ['xs','sm','md','lg'].map(function(size){
930             //Roo.log( size + ':' + settings[size]);
931             
932             if (settings[size+'off'] !== false) {
933                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
934             }
935             
936             if (settings[size] === false) {
937                 return;
938             }
939             
940             if (!settings[size]) { // 0 = hidden
941                 cfg.cls += ' hidden-' + size;
942                 return;
943             }
944             cfg.cls += ' col-' + size + '-' + settings[size];
945             
946         });
947         
948         if (this.hidden) {
949             cfg.cls += ' hidden';
950         }
951         
952         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953             cfg.cls +=' alert alert-' + this.alert;
954         }
955         
956         
957         if (this.html.length) {
958             cfg.html = this.html;
959         }
960         if (this.fa) {
961             var fasize = '';
962             if (this.fasize > 1) {
963                 fasize = ' fa-' + this.fasize + 'x';
964             }
965             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
966             
967             
968         }
969         if (this.icon) {
970             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
971         }
972         
973         return cfg;
974     }
975    
976 });
977
978  
979
980  /*
981  * - LGPL
982  *
983  * page container.
984  * 
985  */
986
987
988 /**
989  * @class Roo.bootstrap.Container
990  * @extends Roo.bootstrap.Component
991  * Bootstrap Container class
992  * @cfg {Boolean} jumbotron is it a jumbotron element
993  * @cfg {String} html content of element
994  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
996  * @cfg {String} header content of header (for panel)
997  * @cfg {String} footer content of footer (for panel)
998  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999  * @cfg {String} tag (header|aside|section) type of HTML tag.
1000  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001  * @cfg {String} fa font awesome icon
1002  * @cfg {String} icon (info-sign|check|...) glyphicon name
1003  * @cfg {Boolean} hidden (true|false) hide the element
1004  * @cfg {Boolean} expandable (true|false) default false
1005  * @cfg {Boolean} expanded (true|false) default true
1006  * @cfg {String} rheader contet on the right of header
1007  * @cfg {Boolean} clickable (true|false) default false
1008
1009  *     
1010  * @constructor
1011  * Create a new Container
1012  * @param {Object} config The config object
1013  */
1014
1015 Roo.bootstrap.Container = function(config){
1016     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020          /**
1021          * @event expand
1022          * After the panel has been expand
1023          * 
1024          * @param {Roo.bootstrap.Container} this
1025          */
1026         "expand" : true,
1027         /**
1028          * @event collapse
1029          * After the panel has been collapsed
1030          * 
1031          * @param {Roo.bootstrap.Container} this
1032          */
1033         "collapse" : true,
1034         /**
1035          * @event click
1036          * When a element is chick
1037          * @param {Roo.bootstrap.Container} this
1038          * @param {Roo.EventObject} e
1039          */
1040         "click" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1045     
1046     jumbotron : false,
1047     well: '',
1048     panel : '',
1049     header: '',
1050     footer : '',
1051     sticky: '',
1052     tag : false,
1053     alert : false,
1054     fa: false,
1055     icon : false,
1056     expandable : false,
1057     rheader : '',
1058     expanded : true,
1059     clickable: false,
1060   
1061      
1062     getChildContainer : function() {
1063         
1064         if(!this.el){
1065             return false;
1066         }
1067         
1068         if (this.panel.length) {
1069             return this.el.select('.panel-body',true).first();
1070         }
1071         
1072         return this.el;
1073     },
1074     
1075     
1076     getAutoCreate : function(){
1077         
1078         var cfg = {
1079             tag : this.tag || 'div',
1080             html : '',
1081             cls : ''
1082         };
1083         if (this.jumbotron) {
1084             cfg.cls = 'jumbotron';
1085         }
1086         
1087         
1088         
1089         // - this is applied by the parent..
1090         //if (this.cls) {
1091         //    cfg.cls = this.cls + '';
1092         //}
1093         
1094         if (this.sticky.length) {
1095             
1096             var bd = Roo.get(document.body);
1097             if (!bd.hasClass('bootstrap-sticky')) {
1098                 bd.addClass('bootstrap-sticky');
1099                 Roo.select('html',true).setStyle('height', '100%');
1100             }
1101              
1102             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1103         }
1104         
1105         
1106         if (this.well.length) {
1107             switch (this.well) {
1108                 case 'lg':
1109                 case 'sm':
1110                     cfg.cls +=' well well-' +this.well;
1111                     break;
1112                 default:
1113                     cfg.cls +=' well';
1114                     break;
1115             }
1116         }
1117         
1118         if (this.hidden) {
1119             cfg.cls += ' hidden';
1120         }
1121         
1122         
1123         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124             cfg.cls +=' alert alert-' + this.alert;
1125         }
1126         
1127         var body = cfg;
1128         
1129         if (this.panel.length) {
1130             cfg.cls += ' panel panel-' + this.panel;
1131             cfg.cn = [];
1132             if (this.header.length) {
1133                 
1134                 var h = [];
1135                 
1136                 if(this.expandable){
1137                     
1138                     cfg.cls = cfg.cls + ' expandable';
1139                     
1140                     h.push({
1141                         tag: 'i',
1142                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1143                     });
1144                     
1145                 }
1146                 
1147                 h.push(
1148                     {
1149                         tag: 'span',
1150                         cls : 'panel-title',
1151                         html : (this.expandable ? '&nbsp;' : '') + this.header
1152                     },
1153                     {
1154                         tag: 'span',
1155                         cls: 'panel-header-right',
1156                         html: this.rheader
1157                     }
1158                 );
1159                 
1160                 cfg.cn.push({
1161                     cls : 'panel-heading',
1162                     style : this.expandable ? 'cursor: pointer' : '',
1163                     cn : h
1164                 });
1165                 
1166             }
1167             
1168             body = false;
1169             cfg.cn.push({
1170                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1171                 html : this.html
1172             });
1173             
1174             
1175             if (this.footer.length) {
1176                 cfg.cn.push({
1177                     cls : 'panel-footer',
1178                     html : this.footer
1179                     
1180                 });
1181             }
1182             
1183         }
1184         
1185         if (body) {
1186             body.html = this.html || cfg.html;
1187             // prefix with the icons..
1188             if (this.fa) {
1189                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1190             }
1191             if (this.icon) {
1192                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1193             }
1194             
1195             
1196         }
1197         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198             cfg.cls =  'container';
1199         }
1200         
1201         return cfg;
1202     },
1203     
1204     initEvents: function() 
1205     {
1206         if(this.expandable){
1207             var headerEl = this.headerEl();
1208         
1209             if(headerEl){
1210                 headerEl.on('click', this.onToggleClick, this);
1211             }
1212         }
1213         
1214         if(this.clickable){
1215             this.el.on('click', this.onClick, this);
1216         }
1217         
1218     },
1219     
1220     onToggleClick : function()
1221     {
1222         var headerEl = this.headerEl();
1223         
1224         if(!headerEl){
1225             return;
1226         }
1227         
1228         if(this.expanded){
1229             this.collapse();
1230             return;
1231         }
1232         
1233         this.expand();
1234     },
1235     
1236     expand : function()
1237     {
1238         if(this.fireEvent('expand', this)) {
1239             
1240             this.expanded = true;
1241             
1242             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1243             
1244             this.el.select('.panel-body',true).first().removeClass('hide');
1245             
1246             var toggleEl = this.toggleEl();
1247
1248             if(!toggleEl){
1249                 return;
1250             }
1251
1252             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1253         }
1254         
1255     },
1256     
1257     collapse : function()
1258     {
1259         if(this.fireEvent('collapse', this)) {
1260             
1261             this.expanded = false;
1262             
1263             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264             this.el.select('.panel-body',true).first().addClass('hide');
1265         
1266             var toggleEl = this.toggleEl();
1267
1268             if(!toggleEl){
1269                 return;
1270             }
1271
1272             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1273         }
1274     },
1275     
1276     toggleEl : function()
1277     {
1278         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1279             return;
1280         }
1281         
1282         return this.el.select('.panel-heading .fa',true).first();
1283     },
1284     
1285     headerEl : function()
1286     {
1287         if(!this.el || !this.panel.length || !this.header.length){
1288             return;
1289         }
1290         
1291         return this.el.select('.panel-heading',true).first()
1292     },
1293     
1294     titleEl : function()
1295     {
1296         if(!this.el || !this.panel.length || !this.header.length){
1297             return;
1298         }
1299         
1300         return this.el.select('.panel-title',true).first();
1301     },
1302     
1303     setTitle : function(v)
1304     {
1305         var titleEl = this.titleEl();
1306         
1307         if(!titleEl){
1308             return;
1309         }
1310         
1311         titleEl.dom.innerHTML = v;
1312     },
1313     
1314     getTitle : function()
1315     {
1316         
1317         var titleEl = this.titleEl();
1318         
1319         if(!titleEl){
1320             return '';
1321         }
1322         
1323         return titleEl.dom.innerHTML;
1324     },
1325     
1326     setRightTitle : function(v)
1327     {
1328         var t = this.el.select('.panel-header-right',true).first();
1329         
1330         if(!t){
1331             return;
1332         }
1333         
1334         t.dom.innerHTML = v;
1335     },
1336     
1337     onClick : function(e)
1338     {
1339         e.preventDefault();
1340         
1341         this.fireEvent('click', this, e);
1342     }
1343    
1344 });
1345
1346  /*
1347  * - LGPL
1348  *
1349  * image
1350  * 
1351  */
1352
1353
1354 /**
1355  * @class Roo.bootstrap.Img
1356  * @extends Roo.bootstrap.Component
1357  * Bootstrap Img class
1358  * @cfg {Boolean} imgResponsive false | true
1359  * @cfg {String} border rounded | circle | thumbnail
1360  * @cfg {String} src image source
1361  * @cfg {String} alt image alternative text
1362  * @cfg {String} href a tag href
1363  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364  * @cfg {String} xsUrl xs image source
1365  * @cfg {String} smUrl sm image source
1366  * @cfg {String} mdUrl md image source
1367  * @cfg {String} lgUrl lg image source
1368  * 
1369  * @constructor
1370  * Create a new Input
1371  * @param {Object} config The config object
1372  */
1373
1374 Roo.bootstrap.Img = function(config){
1375     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1376     
1377     this.addEvents({
1378         // img events
1379         /**
1380          * @event click
1381          * The img click event for the img.
1382          * @param {Roo.EventObject} e
1383          */
1384         "click" : true
1385     });
1386 };
1387
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1389     
1390     imgResponsive: true,
1391     border: '',
1392     src: 'about:blank',
1393     href: false,
1394     target: false,
1395     xsUrl: '',
1396     smUrl: '',
1397     mdUrl: '',
1398     lgUrl: '',
1399
1400     getAutoCreate : function()
1401     {   
1402         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403             return this.createSingleImg();
1404         }
1405         
1406         var cfg = {
1407             tag: 'div',
1408             cls: 'roo-image-responsive-group',
1409             cn: []
1410         };
1411         var _this = this;
1412         
1413         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1414             
1415             if(!_this[size + 'Url']){
1416                 return;
1417             }
1418             
1419             var img = {
1420                 tag: 'img',
1421                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422                 html: _this.html || cfg.html,
1423                 src: _this[size + 'Url']
1424             };
1425             
1426             img.cls += ' roo-image-responsive-' + size;
1427             
1428             var s = ['xs', 'sm', 'md', 'lg'];
1429             
1430             s.splice(s.indexOf(size), 1);
1431             
1432             Roo.each(s, function(ss){
1433                 img.cls += ' hidden-' + ss;
1434             });
1435             
1436             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437                 cfg.cls += ' img-' + _this.border;
1438             }
1439             
1440             if(_this.alt){
1441                 cfg.alt = _this.alt;
1442             }
1443             
1444             if(_this.href){
1445                 var a = {
1446                     tag: 'a',
1447                     href: _this.href,
1448                     cn: [
1449                         img
1450                     ]
1451                 };
1452
1453                 if(this.target){
1454                     a.target = _this.target;
1455                 }
1456             }
1457             
1458             cfg.cn.push((_this.href) ? a : img);
1459             
1460         });
1461         
1462         return cfg;
1463     },
1464     
1465     createSingleImg : function()
1466     {
1467         var cfg = {
1468             tag: 'img',
1469             cls: (this.imgResponsive) ? 'img-responsive' : '',
1470             html : null,
1471             src : 'about:blank'  // just incase src get's set to undefined?!?
1472         };
1473         
1474         cfg.html = this.html || cfg.html;
1475         
1476         cfg.src = this.src || cfg.src;
1477         
1478         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479             cfg.cls += ' img-' + this.border;
1480         }
1481         
1482         if(this.alt){
1483             cfg.alt = this.alt;
1484         }
1485         
1486         if(this.href){
1487             var a = {
1488                 tag: 'a',
1489                 href: this.href,
1490                 cn: [
1491                     cfg
1492                 ]
1493             };
1494             
1495             if(this.target){
1496                 a.target = this.target;
1497             }
1498             
1499         }
1500         
1501         return (this.href) ? a : cfg;
1502     },
1503     
1504     initEvents: function() 
1505     {
1506         if(!this.href){
1507             this.el.on('click', this.onClick, this);
1508         }
1509         
1510     },
1511     
1512     onClick : function(e)
1513     {
1514         Roo.log('img onclick');
1515         this.fireEvent('click', this, e);
1516     }
1517    
1518 });
1519
1520  /*
1521  * - LGPL
1522  *
1523  * image
1524  * 
1525  */
1526
1527
1528 /**
1529  * @class Roo.bootstrap.Link
1530  * @extends Roo.bootstrap.Component
1531  * Bootstrap Link Class
1532  * @cfg {String} alt image alternative text
1533  * @cfg {String} href a tag href
1534  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1535  * @cfg {String} html the content of the link.
1536  * @cfg {String} anchor name for the anchor link
1537  * @cfg {String} fa - favicon
1538
1539  * @cfg {Boolean} preventDefault (true | false) default false
1540
1541  * 
1542  * @constructor
1543  * Create a new Input
1544  * @param {Object} config The config object
1545  */
1546
1547 Roo.bootstrap.Link = function(config){
1548     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1549     
1550     this.addEvents({
1551         // img events
1552         /**
1553          * @event click
1554          * The img click event for the img.
1555          * @param {Roo.EventObject} e
1556          */
1557         "click" : true
1558     });
1559 };
1560
1561 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1562     
1563     href: false,
1564     target: false,
1565     preventDefault: false,
1566     anchor : false,
1567     alt : false,
1568     fa: false,
1569
1570
1571     getAutoCreate : function()
1572     {
1573         var html = this.html || '';
1574         
1575         if (this.fa !== false) {
1576             html = '<i class="fa fa-' + this.fa + '"></i>';
1577         }
1578         var cfg = {
1579             tag: 'a'
1580         };
1581         // anchor's do not require html/href...
1582         if (this.anchor === false) {
1583             cfg.html = html;
1584             cfg.href = this.href || '#';
1585         } else {
1586             cfg.name = this.anchor;
1587             if (this.html !== false || this.fa !== false) {
1588                 cfg.html = html;
1589             }
1590             if (this.href !== false) {
1591                 cfg.href = this.href;
1592             }
1593         }
1594         
1595         if(this.alt !== false){
1596             cfg.alt = this.alt;
1597         }
1598         
1599         
1600         if(this.target !== false) {
1601             cfg.target = this.target;
1602         }
1603         
1604         return cfg;
1605     },
1606     
1607     initEvents: function() {
1608         
1609         if(!this.href || this.preventDefault){
1610             this.el.on('click', this.onClick, this);
1611         }
1612     },
1613     
1614     onClick : function(e)
1615     {
1616         if(this.preventDefault){
1617             e.preventDefault();
1618         }
1619         //Roo.log('img onclick');
1620         this.fireEvent('click', this, e);
1621     }
1622    
1623 });
1624
1625  /*
1626  * - LGPL
1627  *
1628  * header
1629  * 
1630  */
1631
1632 /**
1633  * @class Roo.bootstrap.Header
1634  * @extends Roo.bootstrap.Component
1635  * Bootstrap Header class
1636  * @cfg {String} html content of header
1637  * @cfg {Number} level (1|2|3|4|5|6) default 1
1638  * 
1639  * @constructor
1640  * Create a new Header
1641  * @param {Object} config The config object
1642  */
1643
1644
1645 Roo.bootstrap.Header  = function(config){
1646     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1650     
1651     //href : false,
1652     html : false,
1653     level : 1,
1654     
1655     
1656     
1657     getAutoCreate : function(){
1658         
1659         
1660         
1661         var cfg = {
1662             tag: 'h' + (1 *this.level),
1663             html: this.html || ''
1664         } ;
1665         
1666         return cfg;
1667     }
1668    
1669 });
1670
1671  
1672
1673  /*
1674  * Based on:
1675  * Ext JS Library 1.1.1
1676  * Copyright(c) 2006-2007, Ext JS, LLC.
1677  *
1678  * Originally Released Under LGPL - original licence link has changed is not relivant.
1679  *
1680  * Fork - LGPL
1681  * <script type="text/javascript">
1682  */
1683  
1684 /**
1685  * @class Roo.bootstrap.MenuMgr
1686  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1687  * @singleton
1688  */
1689 Roo.bootstrap.MenuMgr = function(){
1690    var menus, active, groups = {}, attached = false, lastShow = new Date();
1691
1692    // private - called when first menu is created
1693    function init(){
1694        menus = {};
1695        active = new Roo.util.MixedCollection();
1696        Roo.get(document).addKeyListener(27, function(){
1697            if(active.length > 0){
1698                hideAll();
1699            }
1700        });
1701    }
1702
1703    // private
1704    function hideAll(){
1705        if(active && active.length > 0){
1706            var c = active.clone();
1707            c.each(function(m){
1708                m.hide();
1709            });
1710        }
1711    }
1712
1713    // private
1714    function onHide(m){
1715        active.remove(m);
1716        if(active.length < 1){
1717            Roo.get(document).un("mouseup", onMouseDown);
1718             
1719            attached = false;
1720        }
1721    }
1722
1723    // private
1724    function onShow(m){
1725        var last = active.last();
1726        lastShow = new Date();
1727        active.add(m);
1728        if(!attached){
1729           Roo.get(document).on("mouseup", onMouseDown);
1730            
1731            attached = true;
1732        }
1733        if(m.parentMenu){
1734           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1735           m.parentMenu.activeChild = m;
1736        }else if(last && last.isVisible()){
1737           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1738        }
1739    }
1740
1741    // private
1742    function onBeforeHide(m){
1743        if(m.activeChild){
1744            m.activeChild.hide();
1745        }
1746        if(m.autoHideTimer){
1747            clearTimeout(m.autoHideTimer);
1748            delete m.autoHideTimer;
1749        }
1750    }
1751
1752    // private
1753    function onBeforeShow(m){
1754        var pm = m.parentMenu;
1755        if(!pm && !m.allowOtherMenus){
1756            hideAll();
1757        }else if(pm && pm.activeChild && active != m){
1758            pm.activeChild.hide();
1759        }
1760    }
1761
1762    // private this should really trigger on mouseup..
1763    function onMouseDown(e){
1764         Roo.log("on Mouse Up");
1765         
1766         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1767             Roo.log("MenuManager hideAll");
1768             hideAll();
1769             e.stopEvent();
1770         }
1771         
1772         
1773    }
1774
1775    // private
1776    function onBeforeCheck(mi, state){
1777        if(state){
1778            var g = groups[mi.group];
1779            for(var i = 0, l = g.length; i < l; i++){
1780                if(g[i] != mi){
1781                    g[i].setChecked(false);
1782                }
1783            }
1784        }
1785    }
1786
1787    return {
1788
1789        /**
1790         * Hides all menus that are currently visible
1791         */
1792        hideAll : function(){
1793             hideAll();  
1794        },
1795
1796        // private
1797        register : function(menu){
1798            if(!menus){
1799                init();
1800            }
1801            menus[menu.id] = menu;
1802            menu.on("beforehide", onBeforeHide);
1803            menu.on("hide", onHide);
1804            menu.on("beforeshow", onBeforeShow);
1805            menu.on("show", onShow);
1806            var g = menu.group;
1807            if(g && menu.events["checkchange"]){
1808                if(!groups[g]){
1809                    groups[g] = [];
1810                }
1811                groups[g].push(menu);
1812                menu.on("checkchange", onCheck);
1813            }
1814        },
1815
1816         /**
1817          * Returns a {@link Roo.menu.Menu} object
1818          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1819          * be used to generate and return a new Menu instance.
1820          */
1821        get : function(menu){
1822            if(typeof menu == "string"){ // menu id
1823                return menus[menu];
1824            }else if(menu.events){  // menu instance
1825                return menu;
1826            }
1827            /*else if(typeof menu.length == 'number'){ // array of menu items?
1828                return new Roo.bootstrap.Menu({items:menu});
1829            }else{ // otherwise, must be a config
1830                return new Roo.bootstrap.Menu(menu);
1831            }
1832            */
1833            return false;
1834        },
1835
1836        // private
1837        unregister : function(menu){
1838            delete menus[menu.id];
1839            menu.un("beforehide", onBeforeHide);
1840            menu.un("hide", onHide);
1841            menu.un("beforeshow", onBeforeShow);
1842            menu.un("show", onShow);
1843            var g = menu.group;
1844            if(g && menu.events["checkchange"]){
1845                groups[g].remove(menu);
1846                menu.un("checkchange", onCheck);
1847            }
1848        },
1849
1850        // private
1851        registerCheckable : function(menuItem){
1852            var g = menuItem.group;
1853            if(g){
1854                if(!groups[g]){
1855                    groups[g] = [];
1856                }
1857                groups[g].push(menuItem);
1858                menuItem.on("beforecheckchange", onBeforeCheck);
1859            }
1860        },
1861
1862        // private
1863        unregisterCheckable : function(menuItem){
1864            var g = menuItem.group;
1865            if(g){
1866                groups[g].remove(menuItem);
1867                menuItem.un("beforecheckchange", onBeforeCheck);
1868            }
1869        }
1870    };
1871 }();/*
1872  * - LGPL
1873  *
1874  * menu
1875  * 
1876  */
1877
1878 /**
1879  * @class Roo.bootstrap.Menu
1880  * @extends Roo.bootstrap.Component
1881  * Bootstrap Menu class - container for MenuItems
1882  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1883  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1884  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1885  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1886  * 
1887  * @constructor
1888  * Create a new Menu
1889  * @param {Object} config The config object
1890  */
1891
1892
1893 Roo.bootstrap.Menu = function(config){
1894     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1895     if (this.registerMenu && this.type != 'treeview')  {
1896         Roo.bootstrap.MenuMgr.register(this);
1897     }
1898     this.addEvents({
1899         /**
1900          * @event beforeshow
1901          * Fires before this menu is displayed
1902          * @param {Roo.menu.Menu} this
1903          */
1904         beforeshow : true,
1905         /**
1906          * @event beforehide
1907          * Fires before this menu is hidden
1908          * @param {Roo.menu.Menu} this
1909          */
1910         beforehide : true,
1911         /**
1912          * @event show
1913          * Fires after this menu is displayed
1914          * @param {Roo.menu.Menu} this
1915          */
1916         show : true,
1917         /**
1918          * @event hide
1919          * Fires after this menu is hidden
1920          * @param {Roo.menu.Menu} this
1921          */
1922         hide : true,
1923         /**
1924          * @event click
1925          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1926          * @param {Roo.menu.Menu} this
1927          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928          * @param {Roo.EventObject} e
1929          */
1930         click : true,
1931         /**
1932          * @event mouseover
1933          * Fires when the mouse is hovering over this menu
1934          * @param {Roo.menu.Menu} this
1935          * @param {Roo.EventObject} e
1936          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1937          */
1938         mouseover : true,
1939         /**
1940          * @event mouseout
1941          * Fires when the mouse exits this menu
1942          * @param {Roo.menu.Menu} this
1943          * @param {Roo.EventObject} e
1944          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1945          */
1946         mouseout : true,
1947         /**
1948          * @event itemclick
1949          * Fires when a menu item contained in this menu is clicked
1950          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1951          * @param {Roo.EventObject} e
1952          */
1953         itemclick: true
1954     });
1955     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1956 };
1957
1958 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1959     
1960    /// html : false,
1961     //align : '',
1962     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1963     type: false,
1964     /**
1965      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1966      */
1967     registerMenu : true,
1968     
1969     menuItems :false, // stores the menu items..
1970     
1971     hidden:true,
1972         
1973     parentMenu : false,
1974     
1975     stopEvent : true,
1976     
1977     isLink : false,
1978     
1979     getChildContainer : function() {
1980         return this.el;  
1981     },
1982     
1983     getAutoCreate : function(){
1984          
1985         //if (['right'].indexOf(this.align)!==-1) {
1986         //    cfg.cn[1].cls += ' pull-right'
1987         //}
1988         
1989         
1990         var cfg = {
1991             tag : 'ul',
1992             cls : 'dropdown-menu' ,
1993             style : 'z-index:1000'
1994             
1995         };
1996         
1997         if (this.type === 'submenu') {
1998             cfg.cls = 'submenu active';
1999         }
2000         if (this.type === 'treeview') {
2001             cfg.cls = 'treeview-menu';
2002         }
2003         
2004         return cfg;
2005     },
2006     initEvents : function() {
2007         
2008        // Roo.log("ADD event");
2009        // Roo.log(this.triggerEl.dom);
2010         
2011         this.triggerEl.on('click', this.onTriggerClick, this);
2012         
2013         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2014         
2015         this.triggerEl.addClass('dropdown-toggle');
2016         
2017         if (Roo.isTouch) {
2018             this.el.on('touchstart'  , this.onTouch, this);
2019         }
2020         this.el.on('click' , this.onClick, this);
2021
2022         this.el.on("mouseover", this.onMouseOver, this);
2023         this.el.on("mouseout", this.onMouseOut, this);
2024         
2025     },
2026     
2027     findTargetItem : function(e)
2028     {
2029         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2030         if(!t){
2031             return false;
2032         }
2033         //Roo.log(t);         Roo.log(t.id);
2034         if(t && t.id){
2035             //Roo.log(this.menuitems);
2036             return this.menuitems.get(t.id);
2037             
2038             //return this.items.get(t.menuItemId);
2039         }
2040         
2041         return false;
2042     },
2043     
2044     onTouch : function(e) 
2045     {
2046         Roo.log("menu.onTouch");
2047         //e.stopEvent(); this make the user popdown broken
2048         this.onClick(e);
2049     },
2050     
2051     onClick : function(e)
2052     {
2053         Roo.log("menu.onClick");
2054         
2055         var t = this.findTargetItem(e);
2056         if(!t || t.isContainer){
2057             return;
2058         }
2059         Roo.log(e);
2060         /*
2061         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2062             if(t == this.activeItem && t.shouldDeactivate(e)){
2063                 this.activeItem.deactivate();
2064                 delete this.activeItem;
2065                 return;
2066             }
2067             if(t.canActivate){
2068                 this.setActiveItem(t, true);
2069             }
2070             return;
2071             
2072             
2073         }
2074         */
2075        
2076         Roo.log('pass click event');
2077         
2078         t.onClick(e);
2079         
2080         this.fireEvent("click", this, t, e);
2081         
2082         var _this = this;
2083         
2084         (function() { _this.hide(); }).defer(500);
2085     },
2086     
2087     onMouseOver : function(e){
2088         var t  = this.findTargetItem(e);
2089         //Roo.log(t);
2090         //if(t){
2091         //    if(t.canActivate && !t.disabled){
2092         //        this.setActiveItem(t, true);
2093         //    }
2094         //}
2095         
2096         this.fireEvent("mouseover", this, e, t);
2097     },
2098     isVisible : function(){
2099         return !this.hidden;
2100     },
2101      onMouseOut : function(e){
2102         var t  = this.findTargetItem(e);
2103         
2104         //if(t ){
2105         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2106         //        this.activeItem.deactivate();
2107         //        delete this.activeItem;
2108         //    }
2109         //}
2110         this.fireEvent("mouseout", this, e, t);
2111     },
2112     
2113     
2114     /**
2115      * Displays this menu relative to another element
2116      * @param {String/HTMLElement/Roo.Element} element The element to align to
2117      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2118      * the element (defaults to this.defaultAlign)
2119      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2120      */
2121     show : function(el, pos, parentMenu){
2122         this.parentMenu = parentMenu;
2123         if(!this.el){
2124             this.render();
2125         }
2126         this.fireEvent("beforeshow", this);
2127         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2128     },
2129      /**
2130      * Displays this menu at a specific xy position
2131      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2132      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2133      */
2134     showAt : function(xy, parentMenu, /* private: */_e){
2135         this.parentMenu = parentMenu;
2136         if(!this.el){
2137             this.render();
2138         }
2139         if(_e !== false){
2140             this.fireEvent("beforeshow", this);
2141             //xy = this.el.adjustForConstraints(xy);
2142         }
2143         
2144         //this.el.show();
2145         this.hideMenuItems();
2146         this.hidden = false;
2147         this.triggerEl.addClass('open');
2148         
2149         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2150             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2151         }
2152         
2153         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2154             this.el.setXY(xy);
2155         }
2156         
2157         this.focus();
2158         this.fireEvent("show", this);
2159     },
2160     
2161     focus : function(){
2162         return;
2163         if(!this.hidden){
2164             this.doFocus.defer(50, this);
2165         }
2166     },
2167
2168     doFocus : function(){
2169         if(!this.hidden){
2170             this.focusEl.focus();
2171         }
2172     },
2173
2174     /**
2175      * Hides this menu and optionally all parent menus
2176      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2177      */
2178     hide : function(deep)
2179     {
2180         
2181         this.hideMenuItems();
2182         if(this.el && this.isVisible()){
2183             this.fireEvent("beforehide", this);
2184             if(this.activeItem){
2185                 this.activeItem.deactivate();
2186                 this.activeItem = null;
2187             }
2188             this.triggerEl.removeClass('open');;
2189             this.hidden = true;
2190             this.fireEvent("hide", this);
2191         }
2192         if(deep === true && this.parentMenu){
2193             this.parentMenu.hide(true);
2194         }
2195     },
2196     
2197     onTriggerClick : function(e)
2198     {
2199         Roo.log('trigger click');
2200         
2201         var target = e.getTarget();
2202         
2203         Roo.log(target.nodeName.toLowerCase());
2204         
2205         if(target.nodeName.toLowerCase() === 'i'){
2206             e.preventDefault();
2207         }
2208         
2209     },
2210     
2211     onTriggerPress  : function(e)
2212     {
2213         Roo.log('trigger press');
2214         //Roo.log(e.getTarget());
2215        // Roo.log(this.triggerEl.dom);
2216        
2217         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2218         var pel = Roo.get(e.getTarget());
2219         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2220             Roo.log('is treeview or dropdown?');
2221             return;
2222         }
2223         
2224         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2225             return;
2226         }
2227         
2228         if (this.isVisible()) {
2229             Roo.log('hide');
2230             this.hide();
2231         } else {
2232             Roo.log('show');
2233             this.show(this.triggerEl, false, false);
2234         }
2235         
2236         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2237             e.stopEvent();
2238         }
2239         
2240     },
2241        
2242     
2243     hideMenuItems : function()
2244     {
2245         Roo.log("hide Menu Items");
2246         if (!this.el) { 
2247             return;
2248         }
2249         //$(backdrop).remove()
2250         this.el.select('.open',true).each(function(aa) {
2251             
2252             aa.removeClass('open');
2253           //var parent = getParent($(this))
2254           //var relatedTarget = { relatedTarget: this }
2255           
2256            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2257           //if (e.isDefaultPrevented()) return
2258            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2259         });
2260     },
2261     addxtypeChild : function (tree, cntr) {
2262         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2263           
2264         this.menuitems.add(comp);
2265         return comp;
2266
2267     },
2268     getEl : function()
2269     {
2270         Roo.log(this.el);
2271         return this.el;
2272     }
2273 });
2274
2275  
2276  /*
2277  * - LGPL
2278  *
2279  * menu item
2280  * 
2281  */
2282
2283
2284 /**
2285  * @class Roo.bootstrap.MenuItem
2286  * @extends Roo.bootstrap.Component
2287  * Bootstrap MenuItem class
2288  * @cfg {String} html the menu label
2289  * @cfg {String} href the link
2290  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2291  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2292  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2293  * @cfg {String} fa favicon to show on left of menu item.
2294  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2295  * 
2296  * 
2297  * @constructor
2298  * Create a new MenuItem
2299  * @param {Object} config The config object
2300  */
2301
2302
2303 Roo.bootstrap.MenuItem = function(config){
2304     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2305     this.addEvents({
2306         // raw events
2307         /**
2308          * @event click
2309          * The raw click event for the entire grid.
2310          * @param {Roo.bootstrap.MenuItem} this
2311          * @param {Roo.EventObject} e
2312          */
2313         "click" : true
2314     });
2315 };
2316
2317 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2318     
2319     href : false,
2320     html : false,
2321     preventDefault: true,
2322     isContainer : false,
2323     active : false,
2324     fa: false,
2325     
2326     getAutoCreate : function(){
2327         
2328         if(this.isContainer){
2329             return {
2330                 tag: 'li',
2331                 cls: 'dropdown-menu-item'
2332             };
2333         }
2334         var ctag = {
2335             tag: 'span',
2336             html: 'Link'
2337         };
2338         
2339         var anc = {
2340             tag : 'a',
2341             href : '#',
2342             cn : [  ]
2343         };
2344         
2345         if (this.fa !== false) {
2346             anc.cn.push({
2347                 tag : 'i',
2348                 cls : 'fa fa-' + this.fa
2349             });
2350         }
2351         
2352         anc.cn.push(ctag);
2353         
2354         
2355         var cfg= {
2356             tag: 'li',
2357             cls: 'dropdown-menu-item',
2358             cn: [ anc ]
2359         };
2360         if (this.parent().type == 'treeview') {
2361             cfg.cls = 'treeview-menu';
2362         }
2363         if (this.active) {
2364             cfg.cls += ' active';
2365         }
2366         
2367         
2368         
2369         anc.href = this.href || cfg.cn[0].href ;
2370         ctag.html = this.html || cfg.cn[0].html ;
2371         return cfg;
2372     },
2373     
2374     initEvents: function()
2375     {
2376         if (this.parent().type == 'treeview') {
2377             this.el.select('a').on('click', this.onClick, this);
2378         }
2379         if (this.menu) {
2380             this.menu.parentType = this.xtype;
2381             this.menu.triggerEl = this.el;
2382             this.menu = this.addxtype(Roo.apply({}, this.menu));
2383         }
2384         
2385     },
2386     onClick : function(e)
2387     {
2388         Roo.log('item on click ');
2389         //if(this.preventDefault){
2390         //    e.preventDefault();
2391         //}
2392         //this.parent().hideMenuItems();
2393         
2394         this.fireEvent('click', this, e);
2395     },
2396     getEl : function()
2397     {
2398         return this.el;
2399     } 
2400 });
2401
2402  
2403
2404  /*
2405  * - LGPL
2406  *
2407  * menu separator
2408  * 
2409  */
2410
2411
2412 /**
2413  * @class Roo.bootstrap.MenuSeparator
2414  * @extends Roo.bootstrap.Component
2415  * Bootstrap MenuSeparator class
2416  * 
2417  * @constructor
2418  * Create a new MenuItem
2419  * @param {Object} config The config object
2420  */
2421
2422
2423 Roo.bootstrap.MenuSeparator = function(config){
2424     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2428     
2429     getAutoCreate : function(){
2430         var cfg = {
2431             cls: 'divider',
2432             tag : 'li'
2433         };
2434         
2435         return cfg;
2436     }
2437    
2438 });
2439
2440  
2441
2442  
2443 /*
2444 * Licence: LGPL
2445 */
2446
2447 /**
2448  * @class Roo.bootstrap.Modal
2449  * @extends Roo.bootstrap.Component
2450  * Bootstrap Modal class
2451  * @cfg {String} title Title of dialog
2452  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2453  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2454  * @cfg {Boolean} specificTitle default false
2455  * @cfg {Array} buttons Array of buttons or standard button set..
2456  * @cfg {String} buttonPosition (left|right|center) default right
2457  * @cfg {Boolean} animate default true
2458  * @cfg {Boolean} allow_close default true
2459  * @cfg {Boolean} fitwindow default true
2460  * @cfg {String} size (sm|lg) default empty
2461  * 
2462  * 
2463  * @constructor
2464  * Create a new Modal Dialog
2465  * @param {Object} config The config object
2466  */
2467
2468 Roo.bootstrap.Modal = function(config){
2469     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2470     this.addEvents({
2471         // raw events
2472         /**
2473          * @event btnclick
2474          * The raw btnclick event for the button
2475          * @param {Roo.EventObject} e
2476          */
2477         "btnclick" : true
2478     });
2479     this.buttons = this.buttons || [];
2480      
2481     if (this.tmpl) {
2482         this.tmpl = Roo.factory(this.tmpl);
2483     }
2484     
2485 };
2486
2487 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2488     
2489     title : 'test dialog',
2490    
2491     buttons : false,
2492     
2493     // set on load...
2494      
2495     html: false,
2496     
2497     tmp: false,
2498     
2499     specificTitle: false,
2500     
2501     buttonPosition: 'right',
2502     
2503     allow_close : true,
2504     
2505     animate : true,
2506     
2507     fitwindow: false,
2508     
2509     
2510      // private
2511     dialogEl: false,
2512     bodyEl:  false,
2513     footerEl:  false,
2514     titleEl:  false,
2515     closeEl:  false,
2516     
2517     size: '',
2518     
2519     
2520     onRender : function(ct, position)
2521     {
2522         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2523      
2524         if(!this.el){
2525             var cfg = Roo.apply({},  this.getAutoCreate());
2526             cfg.id = Roo.id();
2527             //if(!cfg.name){
2528             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2529             //}
2530             //if (!cfg.name.length) {
2531             //    delete cfg.name;
2532            // }
2533             if (this.cls) {
2534                 cfg.cls += ' ' + this.cls;
2535             }
2536             if (this.style) {
2537                 cfg.style = this.style;
2538             }
2539             this.el = Roo.get(document.body).createChild(cfg, position);
2540         }
2541         //var type = this.el.dom.type;
2542         
2543         
2544         if(this.tabIndex !== undefined){
2545             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2546         }
2547         
2548         this.dialogEl = this.el.select('.modal-dialog',true).first();
2549         this.bodyEl = this.el.select('.modal-body',true).first();
2550         this.closeEl = this.el.select('.modal-header .close', true).first();
2551         this.footerEl = this.el.select('.modal-footer',true).first();
2552         this.titleEl = this.el.select('.modal-title',true).first();
2553         
2554         
2555          
2556         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2557         this.maskEl.enableDisplayMode("block");
2558         this.maskEl.hide();
2559         //this.el.addClass("x-dlg-modal");
2560     
2561         if (this.buttons.length) {
2562             Roo.each(this.buttons, function(bb) {
2563                 var b = Roo.apply({}, bb);
2564                 b.xns = b.xns || Roo.bootstrap;
2565                 b.xtype = b.xtype || 'Button';
2566                 if (typeof(b.listeners) == 'undefined') {
2567                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2568                 }
2569                 
2570                 var btn = Roo.factory(b);
2571                 
2572                 btn.render(this.el.select('.modal-footer div').first());
2573                 
2574             },this);
2575         }
2576         // render the children.
2577         var nitems = [];
2578         
2579         if(typeof(this.items) != 'undefined'){
2580             var items = this.items;
2581             delete this.items;
2582
2583             for(var i =0;i < items.length;i++) {
2584                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2585             }
2586         }
2587         
2588         this.items = nitems;
2589         
2590         // where are these used - they used to be body/close/footer
2591         
2592        
2593         this.initEvents();
2594         //this.el.addClass([this.fieldClass, this.cls]);
2595         
2596     },
2597     
2598     getAutoCreate : function(){
2599         
2600         
2601         var bdy = {
2602                 cls : 'modal-body',
2603                 html : this.html || ''
2604         };
2605         
2606         var title = {
2607             tag: 'h4',
2608             cls : 'modal-title',
2609             html : this.title
2610         };
2611         
2612         if(this.specificTitle){
2613             title = this.title;
2614             
2615         };
2616         
2617         var header = [];
2618         if (this.allow_close) {
2619             header.push({
2620                 tag: 'button',
2621                 cls : 'close',
2622                 html : '&times'
2623             });
2624         }
2625         
2626         header.push(title);
2627         
2628         var size = '';
2629         
2630         if(this.size.length){
2631             size = 'modal-' + this.size;
2632         }
2633         
2634         var modal = {
2635             cls: "modal",
2636             style : 'display: none',
2637             cn : [
2638                 {
2639                     cls: "modal-dialog " + size,
2640                     cn : [
2641                         {
2642                             cls : "modal-content",
2643                             cn : [
2644                                 {
2645                                     cls : 'modal-header',
2646                                     cn : header
2647                                 },
2648                                 bdy,
2649                                 {
2650                                     cls : 'modal-footer',
2651                                     cn : [
2652                                         {
2653                                             tag: 'div',
2654                                             cls: 'btn-' + this.buttonPosition
2655                                         }
2656                                     ]
2657                                     
2658                                 }
2659                                 
2660                                 
2661                             ]
2662                             
2663                         }
2664                     ]
2665                         
2666                 }
2667             ]
2668         };
2669         
2670         if(this.animate){
2671             modal.cls += ' fade';
2672         }
2673         
2674         return modal;
2675           
2676     },
2677     getChildContainer : function() {
2678          
2679          return this.bodyEl;
2680         
2681     },
2682     getButtonContainer : function() {
2683          return this.el.select('.modal-footer div',true).first();
2684         
2685     },
2686     initEvents : function()
2687     {
2688         if (this.allow_close) {
2689             this.closeEl.on('click', this.hide, this);
2690         }
2691         Roo.EventManager.onWindowResize(this.resize, this, true);
2692         
2693  
2694     },
2695     
2696     resize : function()
2697     {
2698         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2699         if (this.fitwindow) {
2700             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2701             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 30;
2702             this.setSize(w,h)
2703         }
2704     },
2705     
2706     setSize : function(w,h)
2707     {
2708         if (!w && !h) {
2709             return;
2710         }
2711         this.resizeTo(w,h);
2712     },
2713     
2714     show : function() {
2715         
2716         if (!this.rendered) {
2717             this.render();
2718         }
2719         
2720         this.el.setStyle('display', 'block');
2721         
2722         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2723             var _this = this;
2724             (function(){
2725                 this.el.addClass('in');
2726             }).defer(50, this);
2727         }else{
2728             this.el.addClass('in');
2729             
2730         }
2731         
2732         // not sure how we can show data in here.. 
2733         //if (this.tmpl) {
2734         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2735         //}
2736         
2737         Roo.get(document.body).addClass("x-body-masked");
2738         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2739         this.maskEl.show();
2740         this.el.setStyle('zIndex', '10001');
2741        
2742         this.fireEvent('show', this);
2743         this.items.forEach(function(e) {
2744             e.layout ? e.layout() : false;
2745                 
2746         });
2747         this.resize();
2748         
2749         
2750         
2751     },
2752     hide : function()
2753     {
2754         this.maskEl.hide();
2755         Roo.get(document.body).removeClass("x-body-masked");
2756         this.el.removeClass('in');
2757         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2758         
2759         if(this.animate){ // why
2760             var _this = this;
2761             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2762         }else{
2763             this.el.setStyle('display', 'none');
2764         }
2765         
2766         this.fireEvent('hide', this);
2767     },
2768     
2769     addButton : function(str, cb)
2770     {
2771          
2772         
2773         var b = Roo.apply({}, { html : str } );
2774         b.xns = b.xns || Roo.bootstrap;
2775         b.xtype = b.xtype || 'Button';
2776         if (typeof(b.listeners) == 'undefined') {
2777             b.listeners = { click : cb.createDelegate(this)  };
2778         }
2779         
2780         var btn = Roo.factory(b);
2781            
2782         btn.render(this.el.select('.modal-footer div').first());
2783         
2784         return btn;   
2785        
2786     },
2787     
2788     setDefaultButton : function(btn)
2789     {
2790         //this.el.select('.modal-footer').()
2791     },
2792     diff : false,
2793     
2794     resizeTo: function(w,h)
2795     {
2796         // skip.. ?? why??
2797         
2798         this.dialogEl.setWidth(w);
2799         if (this.diff === false) {
2800             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2801         }
2802         
2803         this.bodyEl.setHeight(h-this.diff);
2804         
2805         
2806     },
2807     setContentSize  : function(w, h)
2808     {
2809         
2810     },
2811     onButtonClick: function(btn,e)
2812     {
2813         //Roo.log([a,b,c]);
2814         this.fireEvent('btnclick', btn.name, e);
2815     },
2816      /**
2817      * Set the title of the Dialog
2818      * @param {String} str new Title
2819      */
2820     setTitle: function(str) {
2821         this.titleEl.dom.innerHTML = str;    
2822     },
2823     /**
2824      * Set the body of the Dialog
2825      * @param {String} str new Title
2826      */
2827     setBody: function(str) {
2828         this.bodyEl.dom.innerHTML = str;    
2829     },
2830     /**
2831      * Set the body of the Dialog using the template
2832      * @param {Obj} data - apply this data to the template and replace the body contents.
2833      */
2834     applyBody: function(obj)
2835     {
2836         if (!this.tmpl) {
2837             Roo.log("Error - using apply Body without a template");
2838             //code
2839         }
2840         this.tmpl.overwrite(this.bodyEl, obj);
2841     }
2842     
2843 });
2844
2845
2846 Roo.apply(Roo.bootstrap.Modal,  {
2847     /**
2848          * Button config that displays a single OK button
2849          * @type Object
2850          */
2851         OK :  [{
2852             name : 'ok',
2853             weight : 'primary',
2854             html : 'OK'
2855         }], 
2856         /**
2857          * Button config that displays Yes and No buttons
2858          * @type Object
2859          */
2860         YESNO : [
2861             {
2862                 name  : 'no',
2863                 html : 'No'
2864             },
2865             {
2866                 name  :'yes',
2867                 weight : 'primary',
2868                 html : 'Yes'
2869             }
2870         ],
2871         
2872         /**
2873          * Button config that displays OK and Cancel buttons
2874          * @type Object
2875          */
2876         OKCANCEL : [
2877             {
2878                name : 'cancel',
2879                 html : 'Cancel'
2880             },
2881             {
2882                 name : 'ok',
2883                 weight : 'primary',
2884                 html : 'OK'
2885             }
2886         ],
2887         /**
2888          * Button config that displays Yes, No and Cancel buttons
2889          * @type Object
2890          */
2891         YESNOCANCEL : [
2892             {
2893                 name : 'yes',
2894                 weight : 'primary',
2895                 html : 'Yes'
2896             },
2897             {
2898                 name : 'no',
2899                 html : 'No'
2900             },
2901             {
2902                 name : 'cancel',
2903                 html : 'Cancel'
2904             }
2905         ]
2906 });
2907  
2908  /*
2909  * - LGPL
2910  *
2911  * messagebox - can be used as a replace
2912  * 
2913  */
2914 /**
2915  * @class Roo.MessageBox
2916  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2917  * Example usage:
2918  *<pre><code>
2919 // Basic alert:
2920 Roo.Msg.alert('Status', 'Changes saved successfully.');
2921
2922 // Prompt for user data:
2923 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2924     if (btn == 'ok'){
2925         // process text value...
2926     }
2927 });
2928
2929 // Show a dialog using config options:
2930 Roo.Msg.show({
2931    title:'Save Changes?',
2932    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2933    buttons: Roo.Msg.YESNOCANCEL,
2934    fn: processResult,
2935    animEl: 'elId'
2936 });
2937 </code></pre>
2938  * @singleton
2939  */
2940 Roo.bootstrap.MessageBox = function(){
2941     var dlg, opt, mask, waitTimer;
2942     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2943     var buttons, activeTextEl, bwidth;
2944
2945     
2946     // private
2947     var handleButton = function(button){
2948         dlg.hide();
2949         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2950     };
2951
2952     // private
2953     var handleHide = function(){
2954         if(opt && opt.cls){
2955             dlg.el.removeClass(opt.cls);
2956         }
2957         //if(waitTimer){
2958         //    Roo.TaskMgr.stop(waitTimer);
2959         //    waitTimer = null;
2960         //}
2961     };
2962
2963     // private
2964     var updateButtons = function(b){
2965         var width = 0;
2966         if(!b){
2967             buttons["ok"].hide();
2968             buttons["cancel"].hide();
2969             buttons["yes"].hide();
2970             buttons["no"].hide();
2971             //dlg.footer.dom.style.display = 'none';
2972             return width;
2973         }
2974         dlg.footerEl.dom.style.display = '';
2975         for(var k in buttons){
2976             if(typeof buttons[k] != "function"){
2977                 if(b[k]){
2978                     buttons[k].show();
2979                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2980                     width += buttons[k].el.getWidth()+15;
2981                 }else{
2982                     buttons[k].hide();
2983                 }
2984             }
2985         }
2986         return width;
2987     };
2988
2989     // private
2990     var handleEsc = function(d, k, e){
2991         if(opt && opt.closable !== false){
2992             dlg.hide();
2993         }
2994         if(e){
2995             e.stopEvent();
2996         }
2997     };
2998
2999     return {
3000         /**
3001          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3002          * @return {Roo.BasicDialog} The BasicDialog element
3003          */
3004         getDialog : function(){
3005            if(!dlg){
3006                 dlg = new Roo.bootstrap.Modal( {
3007                     //draggable: true,
3008                     //resizable:false,
3009                     //constraintoviewport:false,
3010                     //fixedcenter:true,
3011                     //collapsible : false,
3012                     //shim:true,
3013                     //modal: true,
3014                   //  width:400,
3015                   //  height:100,
3016                     //buttonAlign:"center",
3017                     closeClick : function(){
3018                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3019                             handleButton("no");
3020                         }else{
3021                             handleButton("cancel");
3022                         }
3023                     }
3024                 });
3025                 dlg.render();
3026                 dlg.on("hide", handleHide);
3027                 mask = dlg.mask;
3028                 //dlg.addKeyListener(27, handleEsc);
3029                 buttons = {};
3030                 this.buttons = buttons;
3031                 var bt = this.buttonText;
3032                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3033                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3034                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3035                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3036                 //Roo.log(buttons);
3037                 bodyEl = dlg.bodyEl.createChild({
3038
3039                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3040                         '<textarea class="roo-mb-textarea"></textarea>' +
3041                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3042                 });
3043                 msgEl = bodyEl.dom.firstChild;
3044                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3045                 textboxEl.enableDisplayMode();
3046                 textboxEl.addKeyListener([10,13], function(){
3047                     if(dlg.isVisible() && opt && opt.buttons){
3048                         if(opt.buttons.ok){
3049                             handleButton("ok");
3050                         }else if(opt.buttons.yes){
3051                             handleButton("yes");
3052                         }
3053                     }
3054                 });
3055                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3056                 textareaEl.enableDisplayMode();
3057                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3058                 progressEl.enableDisplayMode();
3059                 var pf = progressEl.dom.firstChild;
3060                 if (pf) {
3061                     pp = Roo.get(pf.firstChild);
3062                     pp.setHeight(pf.offsetHeight);
3063                 }
3064                 
3065             }
3066             return dlg;
3067         },
3068
3069         /**
3070          * Updates the message box body text
3071          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3072          * the XHTML-compliant non-breaking space character '&amp;#160;')
3073          * @return {Roo.MessageBox} This message box
3074          */
3075         updateText : function(text){
3076             if(!dlg.isVisible() && !opt.width){
3077                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3078             }
3079             msgEl.innerHTML = text || '&#160;';
3080       
3081             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3082             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3083             var w = Math.max(
3084                     Math.min(opt.width || cw , this.maxWidth), 
3085                     Math.max(opt.minWidth || this.minWidth, bwidth)
3086             );
3087             if(opt.prompt){
3088                 activeTextEl.setWidth(w);
3089             }
3090             if(dlg.isVisible()){
3091                 dlg.fixedcenter = false;
3092             }
3093             // to big, make it scroll. = But as usual stupid IE does not support
3094             // !important..
3095             
3096             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3097                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3098                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3099             } else {
3100                 bodyEl.dom.style.height = '';
3101                 bodyEl.dom.style.overflowY = '';
3102             }
3103             if (cw > w) {
3104                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3105             } else {
3106                 bodyEl.dom.style.overflowX = '';
3107             }
3108             
3109             dlg.setContentSize(w, bodyEl.getHeight());
3110             if(dlg.isVisible()){
3111                 dlg.fixedcenter = true;
3112             }
3113             return this;
3114         },
3115
3116         /**
3117          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3118          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3119          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3120          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3121          * @return {Roo.MessageBox} This message box
3122          */
3123         updateProgress : function(value, text){
3124             if(text){
3125                 this.updateText(text);
3126             }
3127             if (pp) { // weird bug on my firefox - for some reason this is not defined
3128                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3129             }
3130             return this;
3131         },        
3132
3133         /**
3134          * Returns true if the message box is currently displayed
3135          * @return {Boolean} True if the message box is visible, else false
3136          */
3137         isVisible : function(){
3138             return dlg && dlg.isVisible();  
3139         },
3140
3141         /**
3142          * Hides the message box if it is displayed
3143          */
3144         hide : function(){
3145             if(this.isVisible()){
3146                 dlg.hide();
3147             }  
3148         },
3149
3150         /**
3151          * Displays a new message box, or reinitializes an existing message box, based on the config options
3152          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3153          * The following config object properties are supported:
3154          * <pre>
3155 Property    Type             Description
3156 ----------  ---------------  ------------------------------------------------------------------------------------
3157 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3158                                    closes (defaults to undefined)
3159 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3160                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3161 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3162                                    progress and wait dialogs will ignore this property and always hide the
3163                                    close button as they can only be closed programmatically.
3164 cls               String           A custom CSS class to apply to the message box element
3165 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3166                                    displayed (defaults to 75)
3167 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3168                                    function will be btn (the name of the button that was clicked, if applicable,
3169                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3170                                    Progress and wait dialogs will ignore this option since they do not respond to
3171                                    user actions and can only be closed programmatically, so any required function
3172                                    should be called by the same code after it closes the dialog.
3173 icon              String           A CSS class that provides a background image to be used as an icon for
3174                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3175 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3176 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3177 modal             Boolean          False to allow user interaction with the page while the message box is
3178                                    displayed (defaults to true)
3179 msg               String           A string that will replace the existing message box body text (defaults
3180                                    to the XHTML-compliant non-breaking space character '&#160;')
3181 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3182 progress          Boolean          True to display a progress bar (defaults to false)
3183 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3184 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3185 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3186 title             String           The title text
3187 value             String           The string value to set into the active textbox element if displayed
3188 wait              Boolean          True to display a progress bar (defaults to false)
3189 width             Number           The width of the dialog in pixels
3190 </pre>
3191          *
3192          * Example usage:
3193          * <pre><code>
3194 Roo.Msg.show({
3195    title: 'Address',
3196    msg: 'Please enter your address:',
3197    width: 300,
3198    buttons: Roo.MessageBox.OKCANCEL,
3199    multiline: true,
3200    fn: saveAddress,
3201    animEl: 'addAddressBtn'
3202 });
3203 </code></pre>
3204          * @param {Object} config Configuration options
3205          * @return {Roo.MessageBox} This message box
3206          */
3207         show : function(options)
3208         {
3209             
3210             // this causes nightmares if you show one dialog after another
3211             // especially on callbacks..
3212              
3213             if(this.isVisible()){
3214                 
3215                 this.hide();
3216                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3217                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3218                 Roo.log("New Dialog Message:" +  options.msg )
3219                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3220                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3221                 
3222             }
3223             var d = this.getDialog();
3224             opt = options;
3225             d.setTitle(opt.title || "&#160;");
3226             d.closeEl.setDisplayed(opt.closable !== false);
3227             activeTextEl = textboxEl;
3228             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3229             if(opt.prompt){
3230                 if(opt.multiline){
3231                     textboxEl.hide();
3232                     textareaEl.show();
3233                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3234                         opt.multiline : this.defaultTextHeight);
3235                     activeTextEl = textareaEl;
3236                 }else{
3237                     textboxEl.show();
3238                     textareaEl.hide();
3239                 }
3240             }else{
3241                 textboxEl.hide();
3242                 textareaEl.hide();
3243             }
3244             progressEl.setDisplayed(opt.progress === true);
3245             this.updateProgress(0);
3246             activeTextEl.dom.value = opt.value || "";
3247             if(opt.prompt){
3248                 dlg.setDefaultButton(activeTextEl);
3249             }else{
3250                 var bs = opt.buttons;
3251                 var db = null;
3252                 if(bs && bs.ok){
3253                     db = buttons["ok"];
3254                 }else if(bs && bs.yes){
3255                     db = buttons["yes"];
3256                 }
3257                 dlg.setDefaultButton(db);
3258             }
3259             bwidth = updateButtons(opt.buttons);
3260             this.updateText(opt.msg);
3261             if(opt.cls){
3262                 d.el.addClass(opt.cls);
3263             }
3264             d.proxyDrag = opt.proxyDrag === true;
3265             d.modal = opt.modal !== false;
3266             d.mask = opt.modal !== false ? mask : false;
3267             if(!d.isVisible()){
3268                 // force it to the end of the z-index stack so it gets a cursor in FF
3269                 document.body.appendChild(dlg.el.dom);
3270                 d.animateTarget = null;
3271                 d.show(options.animEl);
3272             }
3273             return this;
3274         },
3275
3276         /**
3277          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3278          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3279          * and closing the message box when the process is complete.
3280          * @param {String} title The title bar text
3281          * @param {String} msg The message box body text
3282          * @return {Roo.MessageBox} This message box
3283          */
3284         progress : function(title, msg){
3285             this.show({
3286                 title : title,
3287                 msg : msg,
3288                 buttons: false,
3289                 progress:true,
3290                 closable:false,
3291                 minWidth: this.minProgressWidth,
3292                 modal : true
3293             });
3294             return this;
3295         },
3296
3297         /**
3298          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3299          * If a callback function is passed it will be called after the user clicks the button, and the
3300          * id of the button that was clicked will be passed as the only parameter to the callback
3301          * (could also be the top-right close button).
3302          * @param {String} title The title bar text
3303          * @param {String} msg The message box body text
3304          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3305          * @param {Object} scope (optional) The scope of the callback function
3306          * @return {Roo.MessageBox} This message box
3307          */
3308         alert : function(title, msg, fn, scope){
3309             this.show({
3310                 title : title,
3311                 msg : msg,
3312                 buttons: this.OK,
3313                 fn: fn,
3314                 scope : scope,
3315                 modal : true
3316             });
3317             return this;
3318         },
3319
3320         /**
3321          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3322          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3323          * You are responsible for closing the message box when the process is complete.
3324          * @param {String} msg The message box body text
3325          * @param {String} title (optional) The title bar text
3326          * @return {Roo.MessageBox} This message box
3327          */
3328         wait : function(msg, title){
3329             this.show({
3330                 title : title,
3331                 msg : msg,
3332                 buttons: false,
3333                 closable:false,
3334                 progress:true,
3335                 modal:true,
3336                 width:300,
3337                 wait:true
3338             });
3339             waitTimer = Roo.TaskMgr.start({
3340                 run: function(i){
3341                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3342                 },
3343                 interval: 1000
3344             });
3345             return this;
3346         },
3347
3348         /**
3349          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3350          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3351          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3352          * @param {String} title The title bar text
3353          * @param {String} msg The message box body text
3354          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3355          * @param {Object} scope (optional) The scope of the callback function
3356          * @return {Roo.MessageBox} This message box
3357          */
3358         confirm : function(title, msg, fn, scope){
3359             this.show({
3360                 title : title,
3361                 msg : msg,
3362                 buttons: this.YESNO,
3363                 fn: fn,
3364                 scope : scope,
3365                 modal : true
3366             });
3367             return this;
3368         },
3369
3370         /**
3371          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3372          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3373          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3374          * (could also be the top-right close button) and the text that was entered will be passed as the two
3375          * parameters to the callback.
3376          * @param {String} title The title bar text
3377          * @param {String} msg The message box body text
3378          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3379          * @param {Object} scope (optional) The scope of the callback function
3380          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3381          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3382          * @return {Roo.MessageBox} This message box
3383          */
3384         prompt : function(title, msg, fn, scope, multiline){
3385             this.show({
3386                 title : title,
3387                 msg : msg,
3388                 buttons: this.OKCANCEL,
3389                 fn: fn,
3390                 minWidth:250,
3391                 scope : scope,
3392                 prompt:true,
3393                 multiline: multiline,
3394                 modal : true
3395             });
3396             return this;
3397         },
3398
3399         /**
3400          * Button config that displays a single OK button
3401          * @type Object
3402          */
3403         OK : {ok:true},
3404         /**
3405          * Button config that displays Yes and No buttons
3406          * @type Object
3407          */
3408         YESNO : {yes:true, no:true},
3409         /**
3410          * Button config that displays OK and Cancel buttons
3411          * @type Object
3412          */
3413         OKCANCEL : {ok:true, cancel:true},
3414         /**
3415          * Button config that displays Yes, No and Cancel buttons
3416          * @type Object
3417          */
3418         YESNOCANCEL : {yes:true, no:true, cancel:true},
3419
3420         /**
3421          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3422          * @type Number
3423          */
3424         defaultTextHeight : 75,
3425         /**
3426          * The maximum width in pixels of the message box (defaults to 600)
3427          * @type Number
3428          */
3429         maxWidth : 600,
3430         /**
3431          * The minimum width in pixels of the message box (defaults to 100)
3432          * @type Number
3433          */
3434         minWidth : 100,
3435         /**
3436          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3437          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3438          * @type Number
3439          */
3440         minProgressWidth : 250,
3441         /**
3442          * An object containing the default button text strings that can be overriden for localized language support.
3443          * Supported properties are: ok, cancel, yes and no.
3444          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3445          * @type Object
3446          */
3447         buttonText : {
3448             ok : "OK",
3449             cancel : "Cancel",
3450             yes : "Yes",
3451             no : "No"
3452         }
3453     };
3454 }();
3455
3456 /**
3457  * Shorthand for {@link Roo.MessageBox}
3458  */
3459 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3460 Roo.Msg = Roo.Msg || Roo.MessageBox;
3461 /*
3462  * - LGPL
3463  *
3464  * navbar
3465  * 
3466  */
3467
3468 /**
3469  * @class Roo.bootstrap.Navbar
3470  * @extends Roo.bootstrap.Component
3471  * Bootstrap Navbar class
3472
3473  * @constructor
3474  * Create a new Navbar
3475  * @param {Object} config The config object
3476  */
3477
3478
3479 Roo.bootstrap.Navbar = function(config){
3480     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3481     this.addEvents({
3482         // raw events
3483         /**
3484          * @event beforetoggle
3485          * Fire before toggle the menu
3486          * @param {Roo.EventObject} e
3487          */
3488         "beforetoggle" : true
3489     });
3490 };
3491
3492 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3493     
3494     
3495    
3496     // private
3497     navItems : false,
3498     loadMask : false,
3499     
3500     
3501     getAutoCreate : function(){
3502         
3503         
3504         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3505         
3506     },
3507     
3508     initEvents :function ()
3509     {
3510         //Roo.log(this.el.select('.navbar-toggle',true));
3511         this.el.select('.navbar-toggle',true).on('click', function() {
3512             if(this.fireEvent('beforetoggle', this) !== false){
3513                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3514             }
3515             
3516         }, this);
3517         
3518         var mark = {
3519             tag: "div",
3520             cls:"x-dlg-mask"
3521         };
3522         
3523         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3524         
3525         var size = this.el.getSize();
3526         this.maskEl.setSize(size.width, size.height);
3527         this.maskEl.enableDisplayMode("block");
3528         this.maskEl.hide();
3529         
3530         if(this.loadMask){
3531             this.maskEl.show();
3532         }
3533     },
3534     
3535     
3536     getChildContainer : function()
3537     {
3538         if (this.el.select('.collapse').getCount()) {
3539             return this.el.select('.collapse',true).first();
3540         }
3541         
3542         return this.el;
3543     },
3544     
3545     mask : function()
3546     {
3547         this.maskEl.show();
3548     },
3549     
3550     unmask : function()
3551     {
3552         this.maskEl.hide();
3553     } 
3554     
3555     
3556     
3557     
3558 });
3559
3560
3561
3562  
3563
3564  /*
3565  * - LGPL
3566  *
3567  * navbar
3568  * 
3569  */
3570
3571 /**
3572  * @class Roo.bootstrap.NavSimplebar
3573  * @extends Roo.bootstrap.Navbar
3574  * Bootstrap Sidebar class
3575  *
3576  * @cfg {Boolean} inverse is inverted color
3577  * 
3578  * @cfg {String} type (nav | pills | tabs)
3579  * @cfg {Boolean} arrangement stacked | justified
3580  * @cfg {String} align (left | right) alignment
3581  * 
3582  * @cfg {Boolean} main (true|false) main nav bar? default false
3583  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3584  * 
3585  * @cfg {String} tag (header|footer|nav|div) default is nav 
3586
3587  * 
3588  * 
3589  * 
3590  * @constructor
3591  * Create a new Sidebar
3592  * @param {Object} config The config object
3593  */
3594
3595
3596 Roo.bootstrap.NavSimplebar = function(config){
3597     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3598 };
3599
3600 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3601     
3602     inverse: false,
3603     
3604     type: false,
3605     arrangement: '',
3606     align : false,
3607     
3608     
3609     
3610     main : false,
3611     
3612     
3613     tag : false,
3614     
3615     
3616     getAutoCreate : function(){
3617         
3618         
3619         var cfg = {
3620             tag : this.tag || 'div',
3621             cls : 'navbar'
3622         };
3623           
3624         
3625         cfg.cn = [
3626             {
3627                 cls: 'nav',
3628                 tag : 'ul'
3629             }
3630         ];
3631         
3632          
3633         this.type = this.type || 'nav';
3634         if (['tabs','pills'].indexOf(this.type)!==-1) {
3635             cfg.cn[0].cls += ' nav-' + this.type
3636         
3637         
3638         } else {
3639             if (this.type!=='nav') {
3640                 Roo.log('nav type must be nav/tabs/pills')
3641             }
3642             cfg.cn[0].cls += ' navbar-nav'
3643         }
3644         
3645         
3646         
3647         
3648         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3649             cfg.cn[0].cls += ' nav-' + this.arrangement;
3650         }
3651         
3652         
3653         if (this.align === 'right') {
3654             cfg.cn[0].cls += ' navbar-right';
3655         }
3656         
3657         if (this.inverse) {
3658             cfg.cls += ' navbar-inverse';
3659             
3660         }
3661         
3662         
3663         return cfg;
3664     
3665         
3666     }
3667     
3668     
3669     
3670 });
3671
3672
3673
3674  
3675
3676  
3677        /*
3678  * - LGPL
3679  *
3680  * navbar
3681  * 
3682  */
3683
3684 /**
3685  * @class Roo.bootstrap.NavHeaderbar
3686  * @extends Roo.bootstrap.NavSimplebar
3687  * Bootstrap Sidebar class
3688  *
3689  * @cfg {String} brand what is brand
3690  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3691  * @cfg {String} brand_href href of the brand
3692  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3693  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3694  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3695  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3696  * 
3697  * @constructor
3698  * Create a new Sidebar
3699  * @param {Object} config The config object
3700  */
3701
3702
3703 Roo.bootstrap.NavHeaderbar = function(config){
3704     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3705       
3706 };
3707
3708 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3709     
3710     position: '',
3711     brand: '',
3712     brand_href: false,
3713     srButton : true,
3714     autohide : false,
3715     desktopCenter : false,
3716    
3717     
3718     getAutoCreate : function(){
3719         
3720         var   cfg = {
3721             tag: this.nav || 'nav',
3722             cls: 'navbar',
3723             role: 'navigation',
3724             cn: []
3725         };
3726         
3727         var cn = cfg.cn;
3728         if (this.desktopCenter) {
3729             cn.push({cls : 'container', cn : []});
3730             cn = cn[0].cn;
3731         }
3732         
3733         if(this.srButton){
3734             cn.push({
3735                 tag: 'div',
3736                 cls: 'navbar-header',
3737                 cn: [
3738                     {
3739                         tag: 'button',
3740                         type: 'button',
3741                         cls: 'navbar-toggle',
3742                         'data-toggle': 'collapse',
3743                         cn: [
3744                             {
3745                                 tag: 'span',
3746                                 cls: 'sr-only',
3747                                 html: 'Toggle navigation'
3748                             },
3749                             {
3750                                 tag: 'span',
3751                                 cls: 'icon-bar'
3752                             },
3753                             {
3754                                 tag: 'span',
3755                                 cls: 'icon-bar'
3756                             },
3757                             {
3758                                 tag: 'span',
3759                                 cls: 'icon-bar'
3760                             }
3761                         ]
3762                     }
3763                 ]
3764             });
3765         }
3766         
3767         cn.push({
3768             tag: 'div',
3769             cls: 'collapse navbar-collapse',
3770             cn : []
3771         });
3772         
3773         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3774         
3775         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3776             cfg.cls += ' navbar-' + this.position;
3777             
3778             // tag can override this..
3779             
3780             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3781         }
3782         
3783         if (this.brand !== '') {
3784             cn[0].cn.push({
3785                 tag: 'a',
3786                 href: this.brand_href ? this.brand_href : '#',
3787                 cls: 'navbar-brand',
3788                 cn: [
3789                 this.brand
3790                 ]
3791             });
3792         }
3793         
3794         if(this.main){
3795             cfg.cls += ' main-nav';
3796         }
3797         
3798         
3799         return cfg;
3800
3801         
3802     },
3803     getHeaderChildContainer : function()
3804     {
3805         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3806             return this.el.select('.navbar-header',true).first();
3807         }
3808         
3809         return this.getChildContainer();
3810     },
3811     
3812     
3813     initEvents : function()
3814     {
3815         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3816         
3817         if (this.autohide) {
3818             
3819             var prevScroll = 0;
3820             var ft = this.el;
3821             
3822             Roo.get(document).on('scroll',function(e) {
3823                 var ns = Roo.get(document).getScroll().top;
3824                 var os = prevScroll;
3825                 prevScroll = ns;
3826                 
3827                 if(ns > os){
3828                     ft.removeClass('slideDown');
3829                     ft.addClass('slideUp');
3830                     return;
3831                 }
3832                 ft.removeClass('slideUp');
3833                 ft.addClass('slideDown');
3834                  
3835               
3836           },this);
3837         }
3838     }    
3839     
3840 });
3841
3842
3843
3844  
3845
3846  /*
3847  * - LGPL
3848  *
3849  * navbar
3850  * 
3851  */
3852
3853 /**
3854  * @class Roo.bootstrap.NavSidebar
3855  * @extends Roo.bootstrap.Navbar
3856  * Bootstrap Sidebar class
3857  * 
3858  * @constructor
3859  * Create a new Sidebar
3860  * @param {Object} config The config object
3861  */
3862
3863
3864 Roo.bootstrap.NavSidebar = function(config){
3865     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3866 };
3867
3868 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3869     
3870     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3871     
3872     getAutoCreate : function(){
3873         
3874         
3875         return  {
3876             tag: 'div',
3877             cls: 'sidebar sidebar-nav'
3878         };
3879     
3880         
3881     }
3882     
3883     
3884     
3885 });
3886
3887
3888
3889  
3890
3891  /*
3892  * - LGPL
3893  *
3894  * nav group
3895  * 
3896  */
3897
3898 /**
3899  * @class Roo.bootstrap.NavGroup
3900  * @extends Roo.bootstrap.Component
3901  * Bootstrap NavGroup class
3902  * @cfg {String} align (left|right)
3903  * @cfg {Boolean} inverse
3904  * @cfg {String} type (nav|pills|tab) default nav
3905  * @cfg {String} navId - reference Id for navbar.
3906
3907  * 
3908  * @constructor
3909  * Create a new nav group
3910  * @param {Object} config The config object
3911  */
3912
3913 Roo.bootstrap.NavGroup = function(config){
3914     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3915     this.navItems = [];
3916    
3917     Roo.bootstrap.NavGroup.register(this);
3918      this.addEvents({
3919         /**
3920              * @event changed
3921              * Fires when the active item changes
3922              * @param {Roo.bootstrap.NavGroup} this
3923              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3924              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3925          */
3926         'changed': true
3927      });
3928     
3929 };
3930
3931 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3932     
3933     align: '',
3934     inverse: false,
3935     form: false,
3936     type: 'nav',
3937     navId : '',
3938     // private
3939     
3940     navItems : false, 
3941     
3942     getAutoCreate : function()
3943     {
3944         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3945         
3946         cfg = {
3947             tag : 'ul',
3948             cls: 'nav' 
3949         };
3950         
3951         if (['tabs','pills'].indexOf(this.type)!==-1) {
3952             cfg.cls += ' nav-' + this.type
3953         } else {
3954             if (this.type!=='nav') {
3955                 Roo.log('nav type must be nav/tabs/pills')
3956             }
3957             cfg.cls += ' navbar-nav'
3958         }
3959         
3960         if (this.parent().sidebar) {
3961             cfg = {
3962                 tag: 'ul',
3963                 cls: 'dashboard-menu sidebar-menu'
3964             };
3965             
3966             return cfg;
3967         }
3968         
3969         if (this.form === true) {
3970             cfg = {
3971                 tag: 'form',
3972                 cls: 'navbar-form'
3973             };
3974             
3975             if (this.align === 'right') {
3976                 cfg.cls += ' navbar-right';
3977             } else {
3978                 cfg.cls += ' navbar-left';
3979             }
3980         }
3981         
3982         if (this.align === 'right') {
3983             cfg.cls += ' navbar-right';
3984         }
3985         
3986         if (this.inverse) {
3987             cfg.cls += ' navbar-inverse';
3988             
3989         }
3990         
3991         
3992         return cfg;
3993     },
3994     /**
3995     * sets the active Navigation item
3996     * @param {Roo.bootstrap.NavItem} the new current navitem
3997     */
3998     setActiveItem : function(item)
3999     {
4000         var prev = false;
4001         Roo.each(this.navItems, function(v){
4002             if (v == item) {
4003                 return ;
4004             }
4005             if (v.isActive()) {
4006                 v.setActive(false, true);
4007                 prev = v;
4008                 
4009             }
4010             
4011         });
4012
4013         item.setActive(true, true);
4014         this.fireEvent('changed', this, item, prev);
4015         
4016         
4017     },
4018     /**
4019     * gets the active Navigation item
4020     * @return {Roo.bootstrap.NavItem} the current navitem
4021     */
4022     getActive : function()
4023     {
4024         
4025         var prev = false;
4026         Roo.each(this.navItems, function(v){
4027             
4028             if (v.isActive()) {
4029                 prev = v;
4030                 
4031             }
4032             
4033         });
4034         return prev;
4035     },
4036     
4037     indexOfNav : function()
4038     {
4039         
4040         var prev = false;
4041         Roo.each(this.navItems, function(v,i){
4042             
4043             if (v.isActive()) {
4044                 prev = i;
4045                 
4046             }
4047             
4048         });
4049         return prev;
4050     },
4051     /**
4052     * adds a Navigation item
4053     * @param {Roo.bootstrap.NavItem} the navitem to add
4054     */
4055     addItem : function(cfg)
4056     {
4057         var cn = new Roo.bootstrap.NavItem(cfg);
4058         this.register(cn);
4059         cn.parentId = this.id;
4060         cn.onRender(this.el, null);
4061         return cn;
4062     },
4063     /**
4064     * register a Navigation item
4065     * @param {Roo.bootstrap.NavItem} the navitem to add
4066     */
4067     register : function(item)
4068     {
4069         this.navItems.push( item);
4070         item.navId = this.navId;
4071     
4072     },
4073     
4074     /**
4075     * clear all the Navigation item
4076     */
4077    
4078     clearAll : function()
4079     {
4080         this.navItems = [];
4081         this.el.dom.innerHTML = '';
4082     },
4083     
4084     getNavItem: function(tabId)
4085     {
4086         var ret = false;
4087         Roo.each(this.navItems, function(e) {
4088             if (e.tabId == tabId) {
4089                ret =  e;
4090                return false;
4091             }
4092             return true;
4093             
4094         });
4095         return ret;
4096     },
4097     
4098     setActiveNext : function()
4099     {
4100         var i = this.indexOfNav(this.getActive());
4101         if (i > this.navItems.length) {
4102             return;
4103         }
4104         this.setActiveItem(this.navItems[i+1]);
4105     },
4106     setActivePrev : function()
4107     {
4108         var i = this.indexOfNav(this.getActive());
4109         if (i  < 1) {
4110             return;
4111         }
4112         this.setActiveItem(this.navItems[i-1]);
4113     },
4114     clearWasActive : function(except) {
4115         Roo.each(this.navItems, function(e) {
4116             if (e.tabId != except.tabId && e.was_active) {
4117                e.was_active = false;
4118                return false;
4119             }
4120             return true;
4121             
4122         });
4123     },
4124     getWasActive : function ()
4125     {
4126         var r = false;
4127         Roo.each(this.navItems, function(e) {
4128             if (e.was_active) {
4129                r = e;
4130                return false;
4131             }
4132             return true;
4133             
4134         });
4135         return r;
4136     }
4137     
4138     
4139 });
4140
4141  
4142 Roo.apply(Roo.bootstrap.NavGroup, {
4143     
4144     groups: {},
4145      /**
4146     * register a Navigation Group
4147     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4148     */
4149     register : function(navgrp)
4150     {
4151         this.groups[navgrp.navId] = navgrp;
4152         
4153     },
4154     /**
4155     * fetch a Navigation Group based on the navigation ID
4156     * @param {string} the navgroup to add
4157     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4158     */
4159     get: function(navId) {
4160         if (typeof(this.groups[navId]) == 'undefined') {
4161             return false;
4162             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4163         }
4164         return this.groups[navId] ;
4165     }
4166     
4167     
4168     
4169 });
4170
4171  /*
4172  * - LGPL
4173  *
4174  * row
4175  * 
4176  */
4177
4178 /**
4179  * @class Roo.bootstrap.NavItem
4180  * @extends Roo.bootstrap.Component
4181  * Bootstrap Navbar.NavItem class
4182  * @cfg {String} href  link to
4183  * @cfg {String} html content of button
4184  * @cfg {String} badge text inside badge
4185  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4186  * @cfg {String} glyphicon name of glyphicon
4187  * @cfg {String} icon name of font awesome icon
4188  * @cfg {Boolean} active Is item active
4189  * @cfg {Boolean} disabled Is item disabled
4190  
4191  * @cfg {Boolean} preventDefault (true | false) default false
4192  * @cfg {String} tabId the tab that this item activates.
4193  * @cfg {String} tagtype (a|span) render as a href or span?
4194  * @cfg {Boolean} animateRef (true|false) link to element default false  
4195   
4196  * @constructor
4197  * Create a new Navbar Item
4198  * @param {Object} config The config object
4199  */
4200 Roo.bootstrap.NavItem = function(config){
4201     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4202     this.addEvents({
4203         // raw events
4204         /**
4205          * @event click
4206          * The raw click event for the entire grid.
4207          * @param {Roo.EventObject} e
4208          */
4209         "click" : true,
4210          /**
4211             * @event changed
4212             * Fires when the active item active state changes
4213             * @param {Roo.bootstrap.NavItem} this
4214             * @param {boolean} state the new state
4215              
4216          */
4217         'changed': true,
4218         /**
4219             * @event scrollto
4220             * Fires when scroll to element
4221             * @param {Roo.bootstrap.NavItem} this
4222             * @param {Object} options
4223             * @param {Roo.EventObject} e
4224              
4225          */
4226         'scrollto': true
4227     });
4228    
4229 };
4230
4231 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4232     
4233     href: false,
4234     html: '',
4235     badge: '',
4236     icon: false,
4237     glyphicon: false,
4238     active: false,
4239     preventDefault : false,
4240     tabId : false,
4241     tagtype : 'a',
4242     disabled : false,
4243     animateRef : false,
4244     was_active : false,
4245     
4246     getAutoCreate : function(){
4247          
4248         var cfg = {
4249             tag: 'li',
4250             cls: 'nav-item'
4251             
4252         };
4253         
4254         if (this.active) {
4255             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4256         }
4257         if (this.disabled) {
4258             cfg.cls += ' disabled';
4259         }
4260         
4261         if (this.href || this.html || this.glyphicon || this.icon) {
4262             cfg.cn = [
4263                 {
4264                     tag: this.tagtype,
4265                     href : this.href || "#",
4266                     html: this.html || ''
4267                 }
4268             ];
4269             
4270             if (this.icon) {
4271                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4272             }
4273
4274             if(this.glyphicon) {
4275                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4276             }
4277             
4278             if (this.menu) {
4279                 
4280                 cfg.cn[0].html += " <span class='caret'></span>";
4281              
4282             }
4283             
4284             if (this.badge !== '') {
4285                  
4286                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4287             }
4288         }
4289         
4290         
4291         
4292         return cfg;
4293     },
4294     initEvents: function() 
4295     {
4296         if (typeof (this.menu) != 'undefined') {
4297             this.menu.parentType = this.xtype;
4298             this.menu.triggerEl = this.el;
4299             this.menu = this.addxtype(Roo.apply({}, this.menu));
4300         }
4301         
4302         this.el.select('a',true).on('click', this.onClick, this);
4303         
4304         if(this.tagtype == 'span'){
4305             this.el.select('span',true).on('click', this.onClick, this);
4306         }
4307        
4308         // at this point parent should be available..
4309         this.parent().register(this);
4310     },
4311     
4312     onClick : function(e)
4313     {
4314         if (e.getTarget('.dropdown-menu-item')) {
4315             // did you click on a menu itemm.... - then don't trigger onclick..
4316             return;
4317         }
4318         
4319         if(
4320                 this.preventDefault || 
4321                 this.href == '#' 
4322         ){
4323             Roo.log("NavItem - prevent Default?");
4324             e.preventDefault();
4325         }
4326         
4327         if (this.disabled) {
4328             return;
4329         }
4330         
4331         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4332         if (tg && tg.transition) {
4333             Roo.log("waiting for the transitionend");
4334             return;
4335         }
4336         
4337         
4338         
4339         //Roo.log("fire event clicked");
4340         if(this.fireEvent('click', this, e) === false){
4341             return;
4342         };
4343         
4344         if(this.tagtype == 'span'){
4345             return;
4346         }
4347         
4348         //Roo.log(this.href);
4349         var ael = this.el.select('a',true).first();
4350         //Roo.log(ael);
4351         
4352         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4353             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4354             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4355                 return; // ignore... - it's a 'hash' to another page.
4356             }
4357             Roo.log("NavItem - prevent Default?");
4358             e.preventDefault();
4359             this.scrollToElement(e);
4360         }
4361         
4362         
4363         var p =  this.parent();
4364    
4365         if (['tabs','pills'].indexOf(p.type)!==-1) {
4366             if (typeof(p.setActiveItem) !== 'undefined') {
4367                 p.setActiveItem(this);
4368             }
4369         }
4370         
4371         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4372         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4373             // remove the collapsed menu expand...
4374             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4375         }
4376     },
4377     
4378     isActive: function () {
4379         return this.active
4380     },
4381     setActive : function(state, fire, is_was_active)
4382     {
4383         if (this.active && !state && this.navId) {
4384             this.was_active = true;
4385             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4386             if (nv) {
4387                 nv.clearWasActive(this);
4388             }
4389             
4390         }
4391         this.active = state;
4392         
4393         if (!state ) {
4394             this.el.removeClass('active');
4395         } else if (!this.el.hasClass('active')) {
4396             this.el.addClass('active');
4397         }
4398         if (fire) {
4399             this.fireEvent('changed', this, state);
4400         }
4401         
4402         // show a panel if it's registered and related..
4403         
4404         if (!this.navId || !this.tabId || !state || is_was_active) {
4405             return;
4406         }
4407         
4408         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4409         if (!tg) {
4410             return;
4411         }
4412         var pan = tg.getPanelByName(this.tabId);
4413         if (!pan) {
4414             return;
4415         }
4416         // if we can not flip to new panel - go back to old nav highlight..
4417         if (false == tg.showPanel(pan)) {
4418             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4419             if (nv) {
4420                 var onav = nv.getWasActive();
4421                 if (onav) {
4422                     onav.setActive(true, false, true);
4423                 }
4424             }
4425             
4426         }
4427         
4428         
4429         
4430     },
4431      // this should not be here...
4432     setDisabled : function(state)
4433     {
4434         this.disabled = state;
4435         if (!state ) {
4436             this.el.removeClass('disabled');
4437         } else if (!this.el.hasClass('disabled')) {
4438             this.el.addClass('disabled');
4439         }
4440         
4441     },
4442     
4443     /**
4444      * Fetch the element to display the tooltip on.
4445      * @return {Roo.Element} defaults to this.el
4446      */
4447     tooltipEl : function()
4448     {
4449         return this.el.select('' + this.tagtype + '', true).first();
4450     },
4451     
4452     scrollToElement : function(e)
4453     {
4454         var c = document.body;
4455         
4456         /*
4457          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4458          */
4459         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4460             c = document.documentElement;
4461         }
4462         
4463         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4464         
4465         if(!target){
4466             return;
4467         }
4468
4469         var o = target.calcOffsetsTo(c);
4470         
4471         var options = {
4472             target : target,
4473             value : o[1]
4474         };
4475         
4476         this.fireEvent('scrollto', this, options, e);
4477         
4478         Roo.get(c).scrollTo('top', options.value, true);
4479         
4480         return;
4481     }
4482 });
4483  
4484
4485  /*
4486  * - LGPL
4487  *
4488  * sidebar item
4489  *
4490  *  li
4491  *    <span> icon </span>
4492  *    <span> text </span>
4493  *    <span>badge </span>
4494  */
4495
4496 /**
4497  * @class Roo.bootstrap.NavSidebarItem
4498  * @extends Roo.bootstrap.NavItem
4499  * Bootstrap Navbar.NavSidebarItem class
4500  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4501  * {bool} open is the menu open
4502  * @constructor
4503  * Create a new Navbar Button
4504  * @param {Object} config The config object
4505  */
4506 Roo.bootstrap.NavSidebarItem = function(config){
4507     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4508     this.addEvents({
4509         // raw events
4510         /**
4511          * @event click
4512          * The raw click event for the entire grid.
4513          * @param {Roo.EventObject} e
4514          */
4515         "click" : true,
4516          /**
4517             * @event changed
4518             * Fires when the active item active state changes
4519             * @param {Roo.bootstrap.NavSidebarItem} this
4520             * @param {boolean} state the new state
4521              
4522          */
4523         'changed': true
4524     });
4525    
4526 };
4527
4528 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4529     
4530     badgeWeight : 'default',
4531     
4532     open: false,
4533     
4534     getAutoCreate : function(){
4535         
4536         
4537         var a = {
4538                 tag: 'a',
4539                 href : this.href || '#',
4540                 cls: '',
4541                 html : '',
4542                 cn : []
4543         };
4544         var cfg = {
4545             tag: 'li',
4546             cls: '',
4547             cn: [ a ]
4548         };
4549         var span = {
4550             tag: 'span',
4551             html : this.html || ''
4552         };
4553         
4554         
4555         if (this.active) {
4556             cfg.cls += ' active';
4557         }
4558         
4559         if (this.disabled) {
4560             cfg.cls += ' disabled';
4561         }
4562         if (this.open) {
4563             cfg.cls += ' open x-open';
4564         }
4565         // left icon..
4566         if (this.glyphicon || this.icon) {
4567             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4568             a.cn.push({ tag : 'i', cls : c }) ;
4569         }
4570         // html..
4571         a.cn.push(span);
4572         // then badge..
4573         if (this.badge !== '') {
4574             
4575             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4576         }
4577         // fi
4578         if (this.menu) {
4579             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4580             a.cls += 'dropdown-toggle treeview' ;
4581         }
4582         
4583         return cfg;
4584          
4585            
4586     },
4587     
4588     initEvents : function()
4589     { 
4590         if (typeof (this.menu) != 'undefined') {
4591             this.menu.parentType = this.xtype;
4592             this.menu.triggerEl = this.el;
4593             this.menu = this.addxtype(Roo.apply({}, this.menu));
4594         }
4595         
4596         this.el.on('click', this.onClick, this);
4597        
4598     
4599         if(this.badge !== ''){
4600  
4601             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4602         }
4603         
4604     },
4605     
4606     onClick : function(e)
4607     {
4608         if(this.disabled){
4609             e.preventDefault();
4610             return;
4611         }
4612         
4613         if(this.preventDefault){
4614             e.preventDefault();
4615         }
4616         
4617         this.fireEvent('click', this);
4618     },
4619     
4620     disable : function()
4621     {
4622         this.setDisabled(true);
4623     },
4624     
4625     enable : function()
4626     {
4627         this.setDisabled(false);
4628     },
4629     
4630     setDisabled : function(state)
4631     {
4632         if(this.disabled == state){
4633             return;
4634         }
4635         
4636         this.disabled = state;
4637         
4638         if (state) {
4639             this.el.addClass('disabled');
4640             return;
4641         }
4642         
4643         this.el.removeClass('disabled');
4644         
4645         return;
4646     },
4647     
4648     setActive : function(state)
4649     {
4650         if(this.active == state){
4651             return;
4652         }
4653         
4654         this.active = state;
4655         
4656         if (state) {
4657             this.el.addClass('active');
4658             return;
4659         }
4660         
4661         this.el.removeClass('active');
4662         
4663         return;
4664     },
4665     
4666     isActive: function () 
4667     {
4668         return this.active;
4669     },
4670     
4671     setBadge : function(str)
4672     {
4673         if(!this.badgeEl){
4674             return;
4675         }
4676         
4677         this.badgeEl.dom.innerHTML = str;
4678     }
4679     
4680    
4681      
4682  
4683 });
4684  
4685
4686  /*
4687  * - LGPL
4688  *
4689  * row
4690  * 
4691  */
4692
4693 /**
4694  * @class Roo.bootstrap.Row
4695  * @extends Roo.bootstrap.Component
4696  * Bootstrap Row class (contains columns...)
4697  * 
4698  * @constructor
4699  * Create a new Row
4700  * @param {Object} config The config object
4701  */
4702
4703 Roo.bootstrap.Row = function(config){
4704     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4705 };
4706
4707 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4708     
4709     getAutoCreate : function(){
4710        return {
4711             cls: 'row clearfix'
4712        };
4713     }
4714     
4715     
4716 });
4717
4718  
4719
4720  /*
4721  * - LGPL
4722  *
4723  * element
4724  * 
4725  */
4726
4727 /**
4728  * @class Roo.bootstrap.Element
4729  * @extends Roo.bootstrap.Component
4730  * Bootstrap Element class
4731  * @cfg {String} html contents of the element
4732  * @cfg {String} tag tag of the element
4733  * @cfg {String} cls class of the element
4734  * @cfg {Boolean} preventDefault (true|false) default false
4735  * @cfg {Boolean} clickable (true|false) default false
4736  * 
4737  * @constructor
4738  * Create a new Element
4739  * @param {Object} config The config object
4740  */
4741
4742 Roo.bootstrap.Element = function(config){
4743     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4744     
4745     this.addEvents({
4746         // raw events
4747         /**
4748          * @event click
4749          * When a element is chick
4750          * @param {Roo.bootstrap.Element} this
4751          * @param {Roo.EventObject} e
4752          */
4753         "click" : true
4754     });
4755 };
4756
4757 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4758     
4759     tag: 'div',
4760     cls: '',
4761     html: '',
4762     preventDefault: false, 
4763     clickable: false,
4764     
4765     getAutoCreate : function(){
4766         
4767         var cfg = {
4768             tag: this.tag,
4769             cls: this.cls,
4770             html: this.html
4771         };
4772         
4773         return cfg;
4774     },
4775     
4776     initEvents: function() 
4777     {
4778         Roo.bootstrap.Element.superclass.initEvents.call(this);
4779         
4780         if(this.clickable){
4781             this.el.on('click', this.onClick, this);
4782         }
4783         
4784     },
4785     
4786     onClick : function(e)
4787     {
4788         if(this.preventDefault){
4789             e.preventDefault();
4790         }
4791         
4792         this.fireEvent('click', this, e);
4793     },
4794     
4795     getValue : function()
4796     {
4797         return this.el.dom.innerHTML;
4798     },
4799     
4800     setValue : function(value)
4801     {
4802         this.el.dom.innerHTML = value;
4803     }
4804    
4805 });
4806
4807  
4808
4809  /*
4810  * - LGPL
4811  *
4812  * pagination
4813  * 
4814  */
4815
4816 /**
4817  * @class Roo.bootstrap.Pagination
4818  * @extends Roo.bootstrap.Component
4819  * Bootstrap Pagination class
4820  * @cfg {String} size xs | sm | md | lg
4821  * @cfg {Boolean} inverse false | true
4822  * 
4823  * @constructor
4824  * Create a new Pagination
4825  * @param {Object} config The config object
4826  */
4827
4828 Roo.bootstrap.Pagination = function(config){
4829     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4830 };
4831
4832 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4833     
4834     cls: false,
4835     size: false,
4836     inverse: false,
4837     
4838     getAutoCreate : function(){
4839         var cfg = {
4840             tag: 'ul',
4841                 cls: 'pagination'
4842         };
4843         if (this.inverse) {
4844             cfg.cls += ' inverse';
4845         }
4846         if (this.html) {
4847             cfg.html=this.html;
4848         }
4849         if (this.cls) {
4850             cfg.cls += " " + this.cls;
4851         }
4852         return cfg;
4853     }
4854    
4855 });
4856
4857  
4858
4859  /*
4860  * - LGPL
4861  *
4862  * Pagination item
4863  * 
4864  */
4865
4866
4867 /**
4868  * @class Roo.bootstrap.PaginationItem
4869  * @extends Roo.bootstrap.Component
4870  * Bootstrap PaginationItem class
4871  * @cfg {String} html text
4872  * @cfg {String} href the link
4873  * @cfg {Boolean} preventDefault (true | false) default true
4874  * @cfg {Boolean} active (true | false) default false
4875  * @cfg {Boolean} disabled default false
4876  * 
4877  * 
4878  * @constructor
4879  * Create a new PaginationItem
4880  * @param {Object} config The config object
4881  */
4882
4883
4884 Roo.bootstrap.PaginationItem = function(config){
4885     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4886     this.addEvents({
4887         // raw events
4888         /**
4889          * @event click
4890          * The raw click event for the entire grid.
4891          * @param {Roo.EventObject} e
4892          */
4893         "click" : true
4894     });
4895 };
4896
4897 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4898     
4899     href : false,
4900     html : false,
4901     preventDefault: true,
4902     active : false,
4903     cls : false,
4904     disabled: false,
4905     
4906     getAutoCreate : function(){
4907         var cfg= {
4908             tag: 'li',
4909             cn: [
4910                 {
4911                     tag : 'a',
4912                     href : this.href ? this.href : '#',
4913                     html : this.html ? this.html : ''
4914                 }
4915             ]
4916         };
4917         
4918         if(this.cls){
4919             cfg.cls = this.cls;
4920         }
4921         
4922         if(this.disabled){
4923             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4924         }
4925         
4926         if(this.active){
4927             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4928         }
4929         
4930         return cfg;
4931     },
4932     
4933     initEvents: function() {
4934         
4935         this.el.on('click', this.onClick, this);
4936         
4937     },
4938     onClick : function(e)
4939     {
4940         Roo.log('PaginationItem on click ');
4941         if(this.preventDefault){
4942             e.preventDefault();
4943         }
4944         
4945         if(this.disabled){
4946             return;
4947         }
4948         
4949         this.fireEvent('click', this, e);
4950     }
4951    
4952 });
4953
4954  
4955
4956  /*
4957  * - LGPL
4958  *
4959  * slider
4960  * 
4961  */
4962
4963
4964 /**
4965  * @class Roo.bootstrap.Slider
4966  * @extends Roo.bootstrap.Component
4967  * Bootstrap Slider class
4968  *    
4969  * @constructor
4970  * Create a new Slider
4971  * @param {Object} config The config object
4972  */
4973
4974 Roo.bootstrap.Slider = function(config){
4975     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4976 };
4977
4978 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4979     
4980     getAutoCreate : function(){
4981         
4982         var cfg = {
4983             tag: 'div',
4984             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4985             cn: [
4986                 {
4987                     tag: 'a',
4988                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4989                 }
4990             ]
4991         };
4992         
4993         return cfg;
4994     }
4995    
4996 });
4997
4998  /*
4999  * Based on:
5000  * Ext JS Library 1.1.1
5001  * Copyright(c) 2006-2007, Ext JS, LLC.
5002  *
5003  * Originally Released Under LGPL - original licence link has changed is not relivant.
5004  *
5005  * Fork - LGPL
5006  * <script type="text/javascript">
5007  */
5008  
5009
5010 /**
5011  * @class Roo.grid.ColumnModel
5012  * @extends Roo.util.Observable
5013  * This is the default implementation of a ColumnModel used by the Grid. It defines
5014  * the columns in the grid.
5015  * <br>Usage:<br>
5016  <pre><code>
5017  var colModel = new Roo.grid.ColumnModel([
5018         {header: "Ticker", width: 60, sortable: true, locked: true},
5019         {header: "Company Name", width: 150, sortable: true},
5020         {header: "Market Cap.", width: 100, sortable: true},
5021         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5022         {header: "Employees", width: 100, sortable: true, resizable: false}
5023  ]);
5024  </code></pre>
5025  * <p>
5026  
5027  * The config options listed for this class are options which may appear in each
5028  * individual column definition.
5029  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5030  * @constructor
5031  * @param {Object} config An Array of column config objects. See this class's
5032  * config objects for details.
5033 */
5034 Roo.grid.ColumnModel = function(config){
5035         /**
5036      * The config passed into the constructor
5037      */
5038     this.config = config;
5039     this.lookup = {};
5040
5041     // if no id, create one
5042     // if the column does not have a dataIndex mapping,
5043     // map it to the order it is in the config
5044     for(var i = 0, len = config.length; i < len; i++){
5045         var c = config[i];
5046         if(typeof c.dataIndex == "undefined"){
5047             c.dataIndex = i;
5048         }
5049         if(typeof c.renderer == "string"){
5050             c.renderer = Roo.util.Format[c.renderer];
5051         }
5052         if(typeof c.id == "undefined"){
5053             c.id = Roo.id();
5054         }
5055         if(c.editor && c.editor.xtype){
5056             c.editor  = Roo.factory(c.editor, Roo.grid);
5057         }
5058         if(c.editor && c.editor.isFormField){
5059             c.editor = new Roo.grid.GridEditor(c.editor);
5060         }
5061         this.lookup[c.id] = c;
5062     }
5063
5064     /**
5065      * The width of columns which have no width specified (defaults to 100)
5066      * @type Number
5067      */
5068     this.defaultWidth = 100;
5069
5070     /**
5071      * Default sortable of columns which have no sortable specified (defaults to false)
5072      * @type Boolean
5073      */
5074     this.defaultSortable = false;
5075
5076     this.addEvents({
5077         /**
5078              * @event widthchange
5079              * Fires when the width of a column changes.
5080              * @param {ColumnModel} this
5081              * @param {Number} columnIndex The column index
5082              * @param {Number} newWidth The new width
5083              */
5084             "widthchange": true,
5085         /**
5086              * @event headerchange
5087              * Fires when the text of a header changes.
5088              * @param {ColumnModel} this
5089              * @param {Number} columnIndex The column index
5090              * @param {Number} newText The new header text
5091              */
5092             "headerchange": true,
5093         /**
5094              * @event hiddenchange
5095              * Fires when a column is hidden or "unhidden".
5096              * @param {ColumnModel} this
5097              * @param {Number} columnIndex The column index
5098              * @param {Boolean} hidden true if hidden, false otherwise
5099              */
5100             "hiddenchange": true,
5101             /**
5102          * @event columnmoved
5103          * Fires when a column is moved.
5104          * @param {ColumnModel} this
5105          * @param {Number} oldIndex
5106          * @param {Number} newIndex
5107          */
5108         "columnmoved" : true,
5109         /**
5110          * @event columlockchange
5111          * Fires when a column's locked state is changed
5112          * @param {ColumnModel} this
5113          * @param {Number} colIndex
5114          * @param {Boolean} locked true if locked
5115          */
5116         "columnlockchange" : true
5117     });
5118     Roo.grid.ColumnModel.superclass.constructor.call(this);
5119 };
5120 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5121     /**
5122      * @cfg {String} header The header text to display in the Grid view.
5123      */
5124     /**
5125      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5126      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5127      * specified, the column's index is used as an index into the Record's data Array.
5128      */
5129     /**
5130      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5131      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5132      */
5133     /**
5134      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5135      * Defaults to the value of the {@link #defaultSortable} property.
5136      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5137      */
5138     /**
5139      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5140      */
5141     /**
5142      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5143      */
5144     /**
5145      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5146      */
5147     /**
5148      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5149      */
5150     /**
5151      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5152      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5153      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5154      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5155      */
5156        /**
5157      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5158      */
5159     /**
5160      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5161      */
5162     /**
5163      * @cfg {String} cursor (Optional)
5164      */
5165     /**
5166      * @cfg {String} tooltip (Optional)
5167      */
5168     /**
5169      * @cfg {Number} xs (Optional)
5170      */
5171     /**
5172      * @cfg {Number} sm (Optional)
5173      */
5174     /**
5175      * @cfg {Number} md (Optional)
5176      */
5177     /**
5178      * @cfg {Number} lg (Optional)
5179      */
5180     /**
5181      * Returns the id of the column at the specified index.
5182      * @param {Number} index The column index
5183      * @return {String} the id
5184      */
5185     getColumnId : function(index){
5186         return this.config[index].id;
5187     },
5188
5189     /**
5190      * Returns the column for a specified id.
5191      * @param {String} id The column id
5192      * @return {Object} the column
5193      */
5194     getColumnById : function(id){
5195         return this.lookup[id];
5196     },
5197
5198     
5199     /**
5200      * Returns the column for a specified dataIndex.
5201      * @param {String} dataIndex The column dataIndex
5202      * @return {Object|Boolean} the column or false if not found
5203      */
5204     getColumnByDataIndex: function(dataIndex){
5205         var index = this.findColumnIndex(dataIndex);
5206         return index > -1 ? this.config[index] : false;
5207     },
5208     
5209     /**
5210      * Returns the index for a specified column id.
5211      * @param {String} id The column id
5212      * @return {Number} the index, or -1 if not found
5213      */
5214     getIndexById : function(id){
5215         for(var i = 0, len = this.config.length; i < len; i++){
5216             if(this.config[i].id == id){
5217                 return i;
5218             }
5219         }
5220         return -1;
5221     },
5222     
5223     /**
5224      * Returns the index for a specified column dataIndex.
5225      * @param {String} dataIndex The column dataIndex
5226      * @return {Number} the index, or -1 if not found
5227      */
5228     
5229     findColumnIndex : function(dataIndex){
5230         for(var i = 0, len = this.config.length; i < len; i++){
5231             if(this.config[i].dataIndex == dataIndex){
5232                 return i;
5233             }
5234         }
5235         return -1;
5236     },
5237     
5238     
5239     moveColumn : function(oldIndex, newIndex){
5240         var c = this.config[oldIndex];
5241         this.config.splice(oldIndex, 1);
5242         this.config.splice(newIndex, 0, c);
5243         this.dataMap = null;
5244         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5245     },
5246
5247     isLocked : function(colIndex){
5248         return this.config[colIndex].locked === true;
5249     },
5250
5251     setLocked : function(colIndex, value, suppressEvent){
5252         if(this.isLocked(colIndex) == value){
5253             return;
5254         }
5255         this.config[colIndex].locked = value;
5256         if(!suppressEvent){
5257             this.fireEvent("columnlockchange", this, colIndex, value);
5258         }
5259     },
5260
5261     getTotalLockedWidth : function(){
5262         var totalWidth = 0;
5263         for(var i = 0; i < this.config.length; i++){
5264             if(this.isLocked(i) && !this.isHidden(i)){
5265                 this.totalWidth += this.getColumnWidth(i);
5266             }
5267         }
5268         return totalWidth;
5269     },
5270
5271     getLockedCount : function(){
5272         for(var i = 0, len = this.config.length; i < len; i++){
5273             if(!this.isLocked(i)){
5274                 return i;
5275             }
5276         }
5277         
5278         return this.config.length;
5279     },
5280
5281     /**
5282      * Returns the number of columns.
5283      * @return {Number}
5284      */
5285     getColumnCount : function(visibleOnly){
5286         if(visibleOnly === true){
5287             var c = 0;
5288             for(var i = 0, len = this.config.length; i < len; i++){
5289                 if(!this.isHidden(i)){
5290                     c++;
5291                 }
5292             }
5293             return c;
5294         }
5295         return this.config.length;
5296     },
5297
5298     /**
5299      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5300      * @param {Function} fn
5301      * @param {Object} scope (optional)
5302      * @return {Array} result
5303      */
5304     getColumnsBy : function(fn, scope){
5305         var r = [];
5306         for(var i = 0, len = this.config.length; i < len; i++){
5307             var c = this.config[i];
5308             if(fn.call(scope||this, c, i) === true){
5309                 r[r.length] = c;
5310             }
5311         }
5312         return r;
5313     },
5314
5315     /**
5316      * Returns true if the specified column is sortable.
5317      * @param {Number} col The column index
5318      * @return {Boolean}
5319      */
5320     isSortable : function(col){
5321         if(typeof this.config[col].sortable == "undefined"){
5322             return this.defaultSortable;
5323         }
5324         return this.config[col].sortable;
5325     },
5326
5327     /**
5328      * Returns the rendering (formatting) function defined for the column.
5329      * @param {Number} col The column index.
5330      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5331      */
5332     getRenderer : function(col){
5333         if(!this.config[col].renderer){
5334             return Roo.grid.ColumnModel.defaultRenderer;
5335         }
5336         return this.config[col].renderer;
5337     },
5338
5339     /**
5340      * Sets the rendering (formatting) function for a column.
5341      * @param {Number} col The column index
5342      * @param {Function} fn The function to use to process the cell's raw data
5343      * to return HTML markup for the grid view. The render function is called with
5344      * the following parameters:<ul>
5345      * <li>Data value.</li>
5346      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5347      * <li>css A CSS style string to apply to the table cell.</li>
5348      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5349      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5350      * <li>Row index</li>
5351      * <li>Column index</li>
5352      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5353      */
5354     setRenderer : function(col, fn){
5355         this.config[col].renderer = fn;
5356     },
5357
5358     /**
5359      * Returns the width for the specified column.
5360      * @param {Number} col The column index
5361      * @return {Number}
5362      */
5363     getColumnWidth : function(col){
5364         return this.config[col].width * 1 || this.defaultWidth;
5365     },
5366
5367     /**
5368      * Sets the width for a column.
5369      * @param {Number} col The column index
5370      * @param {Number} width The new width
5371      */
5372     setColumnWidth : function(col, width, suppressEvent){
5373         this.config[col].width = width;
5374         this.totalWidth = null;
5375         if(!suppressEvent){
5376              this.fireEvent("widthchange", this, col, width);
5377         }
5378     },
5379
5380     /**
5381      * Returns the total width of all columns.
5382      * @param {Boolean} includeHidden True to include hidden column widths
5383      * @return {Number}
5384      */
5385     getTotalWidth : function(includeHidden){
5386         if(!this.totalWidth){
5387             this.totalWidth = 0;
5388             for(var i = 0, len = this.config.length; i < len; i++){
5389                 if(includeHidden || !this.isHidden(i)){
5390                     this.totalWidth += this.getColumnWidth(i);
5391                 }
5392             }
5393         }
5394         return this.totalWidth;
5395     },
5396
5397     /**
5398      * Returns the header for the specified column.
5399      * @param {Number} col The column index
5400      * @return {String}
5401      */
5402     getColumnHeader : function(col){
5403         return this.config[col].header;
5404     },
5405
5406     /**
5407      * Sets the header for a column.
5408      * @param {Number} col The column index
5409      * @param {String} header The new header
5410      */
5411     setColumnHeader : function(col, header){
5412         this.config[col].header = header;
5413         this.fireEvent("headerchange", this, col, header);
5414     },
5415
5416     /**
5417      * Returns the tooltip for the specified column.
5418      * @param {Number} col The column index
5419      * @return {String}
5420      */
5421     getColumnTooltip : function(col){
5422             return this.config[col].tooltip;
5423     },
5424     /**
5425      * Sets the tooltip for a column.
5426      * @param {Number} col The column index
5427      * @param {String} tooltip The new tooltip
5428      */
5429     setColumnTooltip : function(col, tooltip){
5430             this.config[col].tooltip = tooltip;
5431     },
5432
5433     /**
5434      * Returns the dataIndex for the specified column.
5435      * @param {Number} col The column index
5436      * @return {Number}
5437      */
5438     getDataIndex : function(col){
5439         return this.config[col].dataIndex;
5440     },
5441
5442     /**
5443      * Sets the dataIndex for a column.
5444      * @param {Number} col The column index
5445      * @param {Number} dataIndex The new dataIndex
5446      */
5447     setDataIndex : function(col, dataIndex){
5448         this.config[col].dataIndex = dataIndex;
5449     },
5450
5451     
5452     
5453     /**
5454      * Returns true if the cell is editable.
5455      * @param {Number} colIndex The column index
5456      * @param {Number} rowIndex The row index - this is nto actually used..?
5457      * @return {Boolean}
5458      */
5459     isCellEditable : function(colIndex, rowIndex){
5460         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5461     },
5462
5463     /**
5464      * Returns the editor defined for the cell/column.
5465      * return false or null to disable editing.
5466      * @param {Number} colIndex The column index
5467      * @param {Number} rowIndex The row index
5468      * @return {Object}
5469      */
5470     getCellEditor : function(colIndex, rowIndex){
5471         return this.config[colIndex].editor;
5472     },
5473
5474     /**
5475      * Sets if a column is editable.
5476      * @param {Number} col The column index
5477      * @param {Boolean} editable True if the column is editable
5478      */
5479     setEditable : function(col, editable){
5480         this.config[col].editable = editable;
5481     },
5482
5483
5484     /**
5485      * Returns true if the column is hidden.
5486      * @param {Number} colIndex The column index
5487      * @return {Boolean}
5488      */
5489     isHidden : function(colIndex){
5490         return this.config[colIndex].hidden;
5491     },
5492
5493
5494     /**
5495      * Returns true if the column width cannot be changed
5496      */
5497     isFixed : function(colIndex){
5498         return this.config[colIndex].fixed;
5499     },
5500
5501     /**
5502      * Returns true if the column can be resized
5503      * @return {Boolean}
5504      */
5505     isResizable : function(colIndex){
5506         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5507     },
5508     /**
5509      * Sets if a column is hidden.
5510      * @param {Number} colIndex The column index
5511      * @param {Boolean} hidden True if the column is hidden
5512      */
5513     setHidden : function(colIndex, hidden){
5514         this.config[colIndex].hidden = hidden;
5515         this.totalWidth = null;
5516         this.fireEvent("hiddenchange", this, colIndex, hidden);
5517     },
5518
5519     /**
5520      * Sets the editor for a column.
5521      * @param {Number} col The column index
5522      * @param {Object} editor The editor object
5523      */
5524     setEditor : function(col, editor){
5525         this.config[col].editor = editor;
5526     }
5527 });
5528
5529 Roo.grid.ColumnModel.defaultRenderer = function(value){
5530         if(typeof value == "string" && value.length < 1){
5531             return "&#160;";
5532         }
5533         return value;
5534 };
5535
5536 // Alias for backwards compatibility
5537 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5538 /*
5539  * Based on:
5540  * Ext JS Library 1.1.1
5541  * Copyright(c) 2006-2007, Ext JS, LLC.
5542  *
5543  * Originally Released Under LGPL - original licence link has changed is not relivant.
5544  *
5545  * Fork - LGPL
5546  * <script type="text/javascript">
5547  */
5548  
5549 /**
5550  * @class Roo.LoadMask
5551  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5552  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5553  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5554  * element's UpdateManager load indicator and will be destroyed after the initial load.
5555  * @constructor
5556  * Create a new LoadMask
5557  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5558  * @param {Object} config The config object
5559  */
5560 Roo.LoadMask = function(el, config){
5561     this.el = Roo.get(el);
5562     Roo.apply(this, config);
5563     if(this.store){
5564         this.store.on('beforeload', this.onBeforeLoad, this);
5565         this.store.on('load', this.onLoad, this);
5566         this.store.on('loadexception', this.onLoadException, this);
5567         this.removeMask = false;
5568     }else{
5569         var um = this.el.getUpdateManager();
5570         um.showLoadIndicator = false; // disable the default indicator
5571         um.on('beforeupdate', this.onBeforeLoad, this);
5572         um.on('update', this.onLoad, this);
5573         um.on('failure', this.onLoad, this);
5574         this.removeMask = true;
5575     }
5576 };
5577
5578 Roo.LoadMask.prototype = {
5579     /**
5580      * @cfg {Boolean} removeMask
5581      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5582      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5583      */
5584     /**
5585      * @cfg {String} msg
5586      * The text to display in a centered loading message box (defaults to 'Loading...')
5587      */
5588     msg : 'Loading...',
5589     /**
5590      * @cfg {String} msgCls
5591      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5592      */
5593     msgCls : 'x-mask-loading',
5594
5595     /**
5596      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5597      * @type Boolean
5598      */
5599     disabled: false,
5600
5601     /**
5602      * Disables the mask to prevent it from being displayed
5603      */
5604     disable : function(){
5605        this.disabled = true;
5606     },
5607
5608     /**
5609      * Enables the mask so that it can be displayed
5610      */
5611     enable : function(){
5612         this.disabled = false;
5613     },
5614     
5615     onLoadException : function()
5616     {
5617         Roo.log(arguments);
5618         
5619         if (typeof(arguments[3]) != 'undefined') {
5620             Roo.MessageBox.alert("Error loading",arguments[3]);
5621         } 
5622         /*
5623         try {
5624             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5625                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5626             }   
5627         } catch(e) {
5628             
5629         }
5630         */
5631     
5632         
5633         
5634         this.el.unmask(this.removeMask);
5635     },
5636     // private
5637     onLoad : function()
5638     {
5639         this.el.unmask(this.removeMask);
5640     },
5641
5642     // private
5643     onBeforeLoad : function(){
5644         if(!this.disabled){
5645             this.el.mask(this.msg, this.msgCls);
5646         }
5647     },
5648
5649     // private
5650     destroy : function(){
5651         if(this.store){
5652             this.store.un('beforeload', this.onBeforeLoad, this);
5653             this.store.un('load', this.onLoad, this);
5654             this.store.un('loadexception', this.onLoadException, this);
5655         }else{
5656             var um = this.el.getUpdateManager();
5657             um.un('beforeupdate', this.onBeforeLoad, this);
5658             um.un('update', this.onLoad, this);
5659             um.un('failure', this.onLoad, this);
5660         }
5661     }
5662 };/*
5663  * - LGPL
5664  *
5665  * table
5666  * 
5667  */
5668
5669 /**
5670  * @class Roo.bootstrap.Table
5671  * @extends Roo.bootstrap.Component
5672  * Bootstrap Table class
5673  * @cfg {String} cls table class
5674  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5675  * @cfg {String} bgcolor Specifies the background color for a table
5676  * @cfg {Number} border Specifies whether the table cells should have borders or not
5677  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5678  * @cfg {Number} cellspacing Specifies the space between cells
5679  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5680  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5681  * @cfg {String} sortable Specifies that the table should be sortable
5682  * @cfg {String} summary Specifies a summary of the content of a table
5683  * @cfg {Number} width Specifies the width of a table
5684  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5685  * 
5686  * @cfg {boolean} striped Should the rows be alternative striped
5687  * @cfg {boolean} bordered Add borders to the table
5688  * @cfg {boolean} hover Add hover highlighting
5689  * @cfg {boolean} condensed Format condensed
5690  * @cfg {boolean} responsive Format condensed
5691  * @cfg {Boolean} loadMask (true|false) default false
5692  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5693  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5694  * @cfg {Boolean} rowSelection (true|false) default false
5695  * @cfg {Boolean} cellSelection (true|false) default false
5696  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5697  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5698  
5699  * 
5700  * @constructor
5701  * Create a new Table
5702  * @param {Object} config The config object
5703  */
5704
5705 Roo.bootstrap.Table = function(config){
5706     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5707     
5708   
5709     
5710     // BC...
5711     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5712     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5713     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5714     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5715     
5716     
5717     if (this.sm) {
5718         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5719         this.sm = this.selModel;
5720         this.sm.xmodule = this.xmodule || false;
5721     }
5722     if (this.cm && typeof(this.cm.config) == 'undefined') {
5723         this.colModel = new Roo.grid.ColumnModel(this.cm);
5724         this.cm = this.colModel;
5725         this.cm.xmodule = this.xmodule || false;
5726     }
5727     if (this.store) {
5728         this.store= Roo.factory(this.store, Roo.data);
5729         this.ds = this.store;
5730         this.ds.xmodule = this.xmodule || false;
5731          
5732     }
5733     if (this.footer && this.store) {
5734         this.footer.dataSource = this.ds;
5735         this.footer = Roo.factory(this.footer);
5736     }
5737     
5738     /** @private */
5739     this.addEvents({
5740         /**
5741          * @event cellclick
5742          * Fires when a cell is clicked
5743          * @param {Roo.bootstrap.Table} this
5744          * @param {Roo.Element} el
5745          * @param {Number} rowIndex
5746          * @param {Number} columnIndex
5747          * @param {Roo.EventObject} e
5748          */
5749         "cellclick" : true,
5750         /**
5751          * @event celldblclick
5752          * Fires when a cell is double clicked
5753          * @param {Roo.bootstrap.Table} this
5754          * @param {Roo.Element} el
5755          * @param {Number} rowIndex
5756          * @param {Number} columnIndex
5757          * @param {Roo.EventObject} e
5758          */
5759         "celldblclick" : true,
5760         /**
5761          * @event rowclick
5762          * Fires when a row is clicked
5763          * @param {Roo.bootstrap.Table} this
5764          * @param {Roo.Element} el
5765          * @param {Number} rowIndex
5766          * @param {Roo.EventObject} e
5767          */
5768         "rowclick" : true,
5769         /**
5770          * @event rowdblclick
5771          * Fires when a row is double clicked
5772          * @param {Roo.bootstrap.Table} this
5773          * @param {Roo.Element} el
5774          * @param {Number} rowIndex
5775          * @param {Roo.EventObject} e
5776          */
5777         "rowdblclick" : true,
5778         /**
5779          * @event mouseover
5780          * Fires when a mouseover occur
5781          * @param {Roo.bootstrap.Table} this
5782          * @param {Roo.Element} el
5783          * @param {Number} rowIndex
5784          * @param {Number} columnIndex
5785          * @param {Roo.EventObject} e
5786          */
5787         "mouseover" : true,
5788         /**
5789          * @event mouseout
5790          * Fires when a mouseout occur
5791          * @param {Roo.bootstrap.Table} this
5792          * @param {Roo.Element} el
5793          * @param {Number} rowIndex
5794          * @param {Number} columnIndex
5795          * @param {Roo.EventObject} e
5796          */
5797         "mouseout" : true,
5798         /**
5799          * @event rowclass
5800          * Fires when a row is rendered, so you can change add a style to it.
5801          * @param {Roo.bootstrap.Table} this
5802          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5803          */
5804         'rowclass' : true,
5805           /**
5806          * @event rowsrendered
5807          * Fires when all the  rows have been rendered
5808          * @param {Roo.bootstrap.Table} this
5809          */
5810         'rowsrendered' : true,
5811         /**
5812          * @event contextmenu
5813          * The raw contextmenu event for the entire grid.
5814          * @param {Roo.EventObject} e
5815          */
5816         "contextmenu" : true,
5817         /**
5818          * @event rowcontextmenu
5819          * Fires when a row is right clicked
5820          * @param {Roo.bootstrap.Table} this
5821          * @param {Number} rowIndex
5822          * @param {Roo.EventObject} e
5823          */
5824         "rowcontextmenu" : true,
5825         /**
5826          * @event cellcontextmenu
5827          * Fires when a cell is right clicked
5828          * @param {Roo.bootstrap.Table} this
5829          * @param {Number} rowIndex
5830          * @param {Number} cellIndex
5831          * @param {Roo.EventObject} e
5832          */
5833          "cellcontextmenu" : true,
5834          /**
5835          * @event headercontextmenu
5836          * Fires when a header is right clicked
5837          * @param {Roo.bootstrap.Table} this
5838          * @param {Number} columnIndex
5839          * @param {Roo.EventObject} e
5840          */
5841         "headercontextmenu" : true
5842     });
5843 };
5844
5845 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5846     
5847     cls: false,
5848     align: false,
5849     bgcolor: false,
5850     border: false,
5851     cellpadding: false,
5852     cellspacing: false,
5853     frame: false,
5854     rules: false,
5855     sortable: false,
5856     summary: false,
5857     width: false,
5858     striped : false,
5859     scrollBody : false,
5860     bordered: false,
5861     hover:  false,
5862     condensed : false,
5863     responsive : false,
5864     sm : false,
5865     cm : false,
5866     store : false,
5867     loadMask : false,
5868     footerShow : true,
5869     headerShow : true,
5870   
5871     rowSelection : false,
5872     cellSelection : false,
5873     layout : false,
5874     
5875     // Roo.Element - the tbody
5876     mainBody: false,
5877     // Roo.Element - thead element
5878     mainHead: false,
5879     
5880     container: false, // used by gridpanel...
5881     
5882     getAutoCreate : function()
5883     {
5884         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5885         
5886         cfg = {
5887             tag: 'table',
5888             cls : 'table',
5889             cn : []
5890         };
5891         if (this.scrollBody) {
5892             cfg.cls += ' table-body-fixed';
5893         }    
5894         if (this.striped) {
5895             cfg.cls += ' table-striped';
5896         }
5897         
5898         if (this.hover) {
5899             cfg.cls += ' table-hover';
5900         }
5901         if (this.bordered) {
5902             cfg.cls += ' table-bordered';
5903         }
5904         if (this.condensed) {
5905             cfg.cls += ' table-condensed';
5906         }
5907         if (this.responsive) {
5908             cfg.cls += ' table-responsive';
5909         }
5910         
5911         if (this.cls) {
5912             cfg.cls+=  ' ' +this.cls;
5913         }
5914         
5915         // this lot should be simplifed...
5916         
5917         if (this.align) {
5918             cfg.align=this.align;
5919         }
5920         if (this.bgcolor) {
5921             cfg.bgcolor=this.bgcolor;
5922         }
5923         if (this.border) {
5924             cfg.border=this.border;
5925         }
5926         if (this.cellpadding) {
5927             cfg.cellpadding=this.cellpadding;
5928         }
5929         if (this.cellspacing) {
5930             cfg.cellspacing=this.cellspacing;
5931         }
5932         if (this.frame) {
5933             cfg.frame=this.frame;
5934         }
5935         if (this.rules) {
5936             cfg.rules=this.rules;
5937         }
5938         if (this.sortable) {
5939             cfg.sortable=this.sortable;
5940         }
5941         if (this.summary) {
5942             cfg.summary=this.summary;
5943         }
5944         if (this.width) {
5945             cfg.width=this.width;
5946         }
5947         if (this.layout) {
5948             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5949         }
5950         
5951         if(this.store || this.cm){
5952             if(this.headerShow){
5953                 cfg.cn.push(this.renderHeader());
5954             }
5955             
5956             cfg.cn.push(this.renderBody());
5957             
5958             if(this.footerShow){
5959                 cfg.cn.push(this.renderFooter());
5960             }
5961             // where does this come from?
5962             //cfg.cls+=  ' TableGrid';
5963         }
5964         
5965         return { cn : [ cfg ] };
5966     },
5967     
5968     initEvents : function()
5969     {   
5970         if(!this.store || !this.cm){
5971             return;
5972         }
5973         
5974         //Roo.log('initEvents with ds!!!!');
5975         
5976         this.mainBody = this.el.select('tbody', true).first();
5977         this.mainHead = this.el.select('thead', true).first();
5978         
5979         
5980         
5981         var _this = this;
5982         
5983         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5984             e.on('click', _this.sort, _this);
5985         });
5986         
5987         this.el.on("click", this.onClick, this);
5988         this.el.on("dblclick", this.onDblClick, this);
5989         
5990         // why is this done????? = it breaks dialogs??
5991         //this.parent().el.setStyle('position', 'relative');
5992         
5993         
5994         if (this.footer) {
5995             this.footer.parentId = this.id;
5996             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5997         }
5998         
5999         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6000         
6001         this.store.on('load', this.onLoad, this);
6002         this.store.on('beforeload', this.onBeforeLoad, this);
6003         this.store.on('update', this.onUpdate, this);
6004         this.store.on('add', this.onAdd, this);
6005         this.store.on("clear", this.clear, this);
6006         
6007         this.el.on("contextmenu", this.onContextMenu, this);
6008         
6009         this.mainBody.on('scroll', this.onBodyScroll, this);
6010         
6011         
6012     },
6013     
6014     onContextMenu : function(e, t)
6015     {
6016         this.processEvent("contextmenu", e);
6017     },
6018     
6019     processEvent : function(name, e)
6020     {
6021         if (name != 'touchstart' ) {
6022             this.fireEvent(name, e);    
6023         }
6024         
6025         var t = e.getTarget();
6026         
6027         var cell = Roo.get(t);
6028         
6029         if(!cell){
6030             return;
6031         }
6032         
6033         if(cell.findParent('tfoot', false, true)){
6034             return;
6035         }
6036         
6037         if(cell.findParent('thead', false, true)){
6038             
6039             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6040                 cell = Roo.get(t).findParent('th', false, true);
6041                 if (!cell) {
6042                     Roo.log("failed to find th in thead?");
6043                     Roo.log(e.getTarget());
6044                     return;
6045                 }
6046             }
6047             
6048             var cellIndex = cell.dom.cellIndex;
6049             
6050             var ename = name == 'touchstart' ? 'click' : name;
6051             this.fireEvent("header" + ename, this, cellIndex, e);
6052             
6053             return;
6054         }
6055         
6056         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6057             cell = Roo.get(t).findParent('td', false, true);
6058             if (!cell) {
6059                 Roo.log("failed to find th in tbody?");
6060                 Roo.log(e.getTarget());
6061                 return;
6062             }
6063         }
6064         
6065         var row = cell.findParent('tr', false, true);
6066         var cellIndex = cell.dom.cellIndex;
6067         var rowIndex = row.dom.rowIndex - 1;
6068         
6069         if(row !== false){
6070             
6071             this.fireEvent("row" + name, this, rowIndex, e);
6072             
6073             if(cell !== false){
6074             
6075                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6076             }
6077         }
6078         
6079     },
6080     
6081     onMouseover : function(e, el)
6082     {
6083         var cell = Roo.get(el);
6084         
6085         if(!cell){
6086             return;
6087         }
6088         
6089         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6090             cell = cell.findParent('td', false, true);
6091         }
6092         
6093         var row = cell.findParent('tr', false, true);
6094         var cellIndex = cell.dom.cellIndex;
6095         var rowIndex = row.dom.rowIndex - 1; // start from 0
6096         
6097         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6098         
6099     },
6100     
6101     onMouseout : function(e, el)
6102     {
6103         var cell = Roo.get(el);
6104         
6105         if(!cell){
6106             return;
6107         }
6108         
6109         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6110             cell = cell.findParent('td', false, true);
6111         }
6112         
6113         var row = cell.findParent('tr', false, true);
6114         var cellIndex = cell.dom.cellIndex;
6115         var rowIndex = row.dom.rowIndex - 1; // start from 0
6116         
6117         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6118         
6119     },
6120     
6121     onClick : function(e, el)
6122     {
6123         var cell = Roo.get(el);
6124         
6125         if(!cell || (!this.cellSelection && !this.rowSelection)){
6126             return;
6127         }
6128         
6129         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6130             cell = cell.findParent('td', false, true);
6131         }
6132         
6133         if(!cell || typeof(cell) == 'undefined'){
6134             return;
6135         }
6136         
6137         var row = cell.findParent('tr', false, true);
6138         
6139         if(!row || typeof(row) == 'undefined'){
6140             return;
6141         }
6142         
6143         var cellIndex = cell.dom.cellIndex;
6144         var rowIndex = this.getRowIndex(row);
6145         
6146         // why??? - should these not be based on SelectionModel?
6147         if(this.cellSelection){
6148             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6149         }
6150         
6151         if(this.rowSelection){
6152             this.fireEvent('rowclick', this, row, rowIndex, e);
6153         }
6154         
6155         
6156     },
6157     
6158     onDblClick : function(e,el)
6159     {
6160         var cell = Roo.get(el);
6161         
6162         if(!cell || (!this.CellSelection && !this.RowSelection)){
6163             return;
6164         }
6165         
6166         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6167             cell = cell.findParent('td', false, true);
6168         }
6169         
6170         if(!cell || typeof(cell) == 'undefined'){
6171             return;
6172         }
6173         
6174         var row = cell.findParent('tr', false, true);
6175         
6176         if(!row || typeof(row) == 'undefined'){
6177             return;
6178         }
6179         
6180         var cellIndex = cell.dom.cellIndex;
6181         var rowIndex = this.getRowIndex(row);
6182         
6183         if(this.CellSelection){
6184             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6185         }
6186         
6187         if(this.RowSelection){
6188             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6189         }
6190     },
6191     
6192     sort : function(e,el)
6193     {
6194         var col = Roo.get(el);
6195         
6196         if(!col.hasClass('sortable')){
6197             return;
6198         }
6199         
6200         var sort = col.attr('sort');
6201         var dir = 'ASC';
6202         
6203         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6204             dir = 'DESC';
6205         }
6206         
6207         this.store.sortInfo = {field : sort, direction : dir};
6208         
6209         if (this.footer) {
6210             Roo.log("calling footer first");
6211             this.footer.onClick('first');
6212         } else {
6213         
6214             this.store.load({ params : { start : 0 } });
6215         }
6216     },
6217     
6218     renderHeader : function()
6219     {
6220         var header = {
6221             tag: 'thead',
6222             cn : []
6223         };
6224         
6225         var cm = this.cm;
6226         this.totalWidth = 0;
6227         
6228         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6229             
6230             var config = cm.config[i];
6231             
6232             var c = {
6233                 tag: 'th',
6234                 style : '',
6235                 html: cm.getColumnHeader(i)
6236             };
6237             
6238             var hh = '';
6239             
6240             if(typeof(config.sortable) != 'undefined' && config.sortable){
6241                 c.cls = 'sortable';
6242                 c.html = '<i class="glyphicon"></i>' + c.html;
6243             }
6244             
6245             if(typeof(config.lgHeader) != 'undefined'){
6246                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6247             }
6248             
6249             if(typeof(config.mdHeader) != 'undefined'){
6250                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6251             }
6252             
6253             if(typeof(config.smHeader) != 'undefined'){
6254                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6255             }
6256             
6257             if(typeof(config.xsHeader) != 'undefined'){
6258                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6259             }
6260             
6261             if(hh.length){
6262                 c.html = hh;
6263             }
6264             
6265             if(typeof(config.tooltip) != 'undefined'){
6266                 c.tooltip = config.tooltip;
6267             }
6268             
6269             if(typeof(config.colspan) != 'undefined'){
6270                 c.colspan = config.colspan;
6271             }
6272             
6273             if(typeof(config.hidden) != 'undefined' && config.hidden){
6274                 c.style += ' display:none;';
6275             }
6276             
6277             if(typeof(config.dataIndex) != 'undefined'){
6278                 c.sort = config.dataIndex;
6279             }
6280             
6281            
6282             
6283             if(typeof(config.align) != 'undefined' && config.align.length){
6284                 c.style += ' text-align:' + config.align + ';';
6285             }
6286             
6287             if(typeof(config.width) != 'undefined'){
6288                 c.style += ' width:' + config.width + 'px;';
6289                 this.totalWidth += config.width;
6290             } else {
6291                 this.totalWidth += 100; // assume minimum of 100 per column?
6292             }
6293             
6294             if(typeof(config.cls) != 'undefined'){
6295                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6296             }
6297             
6298             ['xs','sm','md','lg'].map(function(size){
6299                 
6300                 if(typeof(config[size]) == 'undefined'){
6301                     return;
6302                 }
6303                 
6304                 if (!config[size]) { // 0 = hidden
6305                     c.cls += ' hidden-' + size;
6306                     return;
6307                 }
6308                 
6309                 c.cls += ' col-' + size + '-' + config[size];
6310
6311             });
6312             
6313             header.cn.push(c)
6314         }
6315         
6316         return header;
6317     },
6318     
6319     renderBody : function()
6320     {
6321         var body = {
6322             tag: 'tbody',
6323             cn : [
6324                 {
6325                     tag: 'tr',
6326                     cn : [
6327                         {
6328                             tag : 'td',
6329                             colspan :  this.cm.getColumnCount()
6330                         }
6331                     ]
6332                 }
6333             ]
6334         };
6335         
6336         return body;
6337     },
6338     
6339     renderFooter : function()
6340     {
6341         var footer = {
6342             tag: 'tfoot',
6343             cn : [
6344                 {
6345                     tag: 'tr',
6346                     cn : [
6347                         {
6348                             tag : 'td',
6349                             colspan :  this.cm.getColumnCount()
6350                         }
6351                     ]
6352                 }
6353             ]
6354         };
6355         
6356         return footer;
6357     },
6358     
6359     
6360     
6361     onLoad : function()
6362     {
6363 //        Roo.log('ds onload');
6364         this.clear();
6365         
6366         var _this = this;
6367         var cm = this.cm;
6368         var ds = this.store;
6369         
6370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6371             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6372             if (_this.store.sortInfo) {
6373                     
6374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6375                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6376                 }
6377                 
6378                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6379                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6380                 }
6381             }
6382         });
6383         
6384         var tbody =  this.mainBody;
6385               
6386         if(ds.getCount() > 0){
6387             ds.data.each(function(d,rowIndex){
6388                 var row =  this.renderRow(cm, ds, rowIndex);
6389                 
6390                 tbody.createChild(row);
6391                 
6392                 var _this = this;
6393                 
6394                 if(row.cellObjects.length){
6395                     Roo.each(row.cellObjects, function(r){
6396                         _this.renderCellObject(r);
6397                     })
6398                 }
6399                 
6400             }, this);
6401         }
6402         
6403         Roo.each(this.el.select('tbody td', true).elements, function(e){
6404             e.on('mouseover', _this.onMouseover, _this);
6405         });
6406         
6407         Roo.each(this.el.select('tbody td', true).elements, function(e){
6408             e.on('mouseout', _this.onMouseout, _this);
6409         });
6410         this.fireEvent('rowsrendered', this);
6411         //if(this.loadMask){
6412         //    this.maskEl.hide();
6413         //}
6414         
6415         this.autoSize();
6416     },
6417     
6418     
6419     onUpdate : function(ds,record)
6420     {
6421         this.refreshRow(record);
6422     },
6423     
6424     onRemove : function(ds, record, index, isUpdate){
6425         if(isUpdate !== true){
6426             this.fireEvent("beforerowremoved", this, index, record);
6427         }
6428         var bt = this.mainBody.dom;
6429         
6430         var rows = this.el.select('tbody > tr', true).elements;
6431         
6432         if(typeof(rows[index]) != 'undefined'){
6433             bt.removeChild(rows[index].dom);
6434         }
6435         
6436 //        if(bt.rows[index]){
6437 //            bt.removeChild(bt.rows[index]);
6438 //        }
6439         
6440         if(isUpdate !== true){
6441             //this.stripeRows(index);
6442             //this.syncRowHeights(index, index);
6443             //this.layout();
6444             this.fireEvent("rowremoved", this, index, record);
6445         }
6446     },
6447     
6448     onAdd : function(ds, records, rowIndex)
6449     {
6450         //Roo.log('on Add called');
6451         // - note this does not handle multiple adding very well..
6452         var bt = this.mainBody.dom;
6453         for (var i =0 ; i < records.length;i++) {
6454             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6455             //Roo.log(records[i]);
6456             //Roo.log(this.store.getAt(rowIndex+i));
6457             this.insertRow(this.store, rowIndex + i, false);
6458             return;
6459         }
6460         
6461     },
6462     
6463     
6464     refreshRow : function(record){
6465         var ds = this.store, index;
6466         if(typeof record == 'number'){
6467             index = record;
6468             record = ds.getAt(index);
6469         }else{
6470             index = ds.indexOf(record);
6471         }
6472         this.insertRow(ds, index, true);
6473         this.onRemove(ds, record, index+1, true);
6474         //this.syncRowHeights(index, index);
6475         //this.layout();
6476         this.fireEvent("rowupdated", this, index, record);
6477     },
6478     
6479     insertRow : function(dm, rowIndex, isUpdate){
6480         
6481         if(!isUpdate){
6482             this.fireEvent("beforerowsinserted", this, rowIndex);
6483         }
6484             //var s = this.getScrollState();
6485         var row = this.renderRow(this.cm, this.store, rowIndex);
6486         // insert before rowIndex..
6487         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6488         
6489         var _this = this;
6490                 
6491         if(row.cellObjects.length){
6492             Roo.each(row.cellObjects, function(r){
6493                 _this.renderCellObject(r);
6494             })
6495         }
6496             
6497         if(!isUpdate){
6498             this.fireEvent("rowsinserted", this, rowIndex);
6499             //this.syncRowHeights(firstRow, lastRow);
6500             //this.stripeRows(firstRow);
6501             //this.layout();
6502         }
6503         
6504     },
6505     
6506     
6507     getRowDom : function(rowIndex)
6508     {
6509         var rows = this.el.select('tbody > tr', true).elements;
6510         
6511         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6512         
6513     },
6514     // returns the object tree for a tr..
6515   
6516     
6517     renderRow : function(cm, ds, rowIndex) 
6518     {
6519         
6520         var d = ds.getAt(rowIndex);
6521         
6522         var row = {
6523             tag : 'tr',
6524             cn : []
6525         };
6526             
6527         var cellObjects = [];
6528         
6529         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6530             var config = cm.config[i];
6531             
6532             var renderer = cm.getRenderer(i);
6533             var value = '';
6534             var id = false;
6535             
6536             if(typeof(renderer) !== 'undefined'){
6537                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6538             }
6539             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6540             // and are rendered into the cells after the row is rendered - using the id for the element.
6541             
6542             if(typeof(value) === 'object'){
6543                 id = Roo.id();
6544                 cellObjects.push({
6545                     container : id,
6546                     cfg : value 
6547                 })
6548             }
6549             
6550             var rowcfg = {
6551                 record: d,
6552                 rowIndex : rowIndex,
6553                 colIndex : i,
6554                 rowClass : ''
6555             };
6556
6557             this.fireEvent('rowclass', this, rowcfg);
6558             
6559             var td = {
6560                 tag: 'td',
6561                 cls : rowcfg.rowClass,
6562                 style: '',
6563                 html: (typeof(value) === 'object') ? '' : value
6564             };
6565             
6566             if (id) {
6567                 td.id = id;
6568             }
6569             
6570             if(typeof(config.colspan) != 'undefined'){
6571                 td.colspan = config.colspan;
6572             }
6573             
6574             if(typeof(config.hidden) != 'undefined' && config.hidden){
6575                 td.style += ' display:none;';
6576             }
6577             
6578             if(typeof(config.align) != 'undefined' && config.align.length){
6579                 td.style += ' text-align:' + config.align + ';';
6580             }
6581             
6582             if(typeof(config.width) != 'undefined'){
6583                 td.style += ' width:' +  config.width + 'px;';
6584             }
6585             
6586             if(typeof(config.cursor) != 'undefined'){
6587                 td.style += ' cursor:' +  config.cursor + ';';
6588             }
6589             
6590             if(typeof(config.cls) != 'undefined'){
6591                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6592             }
6593             
6594             ['xs','sm','md','lg'].map(function(size){
6595                 
6596                 if(typeof(config[size]) == 'undefined'){
6597                     return;
6598                 }
6599                 
6600                 if (!config[size]) { // 0 = hidden
6601                     td.cls += ' hidden-' + size;
6602                     return;
6603                 }
6604                 
6605                 td.cls += ' col-' + size + '-' + config[size];
6606
6607             });
6608              
6609             row.cn.push(td);
6610            
6611         }
6612         
6613         row.cellObjects = cellObjects;
6614         
6615         return row;
6616           
6617     },
6618     
6619     
6620     
6621     onBeforeLoad : function()
6622     {
6623         //Roo.log('ds onBeforeLoad');
6624         
6625         //this.clear();
6626         
6627         //if(this.loadMask){
6628         //    this.maskEl.show();
6629         //}
6630     },
6631      /**
6632      * Remove all rows
6633      */
6634     clear : function()
6635     {
6636         this.el.select('tbody', true).first().dom.innerHTML = '';
6637     },
6638     /**
6639      * Show or hide a row.
6640      * @param {Number} rowIndex to show or hide
6641      * @param {Boolean} state hide
6642      */
6643     setRowVisibility : function(rowIndex, state)
6644     {
6645         var bt = this.mainBody.dom;
6646         
6647         var rows = this.el.select('tbody > tr', true).elements;
6648         
6649         if(typeof(rows[rowIndex]) == 'undefined'){
6650             return;
6651         }
6652         rows[rowIndex].dom.style.display = state ? '' : 'none';
6653     },
6654     
6655     
6656     getSelectionModel : function(){
6657         if(!this.selModel){
6658             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6659         }
6660         return this.selModel;
6661     },
6662     /*
6663      * Render the Roo.bootstrap object from renderder
6664      */
6665     renderCellObject : function(r)
6666     {
6667         var _this = this;
6668         
6669         var t = r.cfg.render(r.container);
6670         
6671         if(r.cfg.cn){
6672             Roo.each(r.cfg.cn, function(c){
6673                 var child = {
6674                     container: t.getChildContainer(),
6675                     cfg: c
6676                 };
6677                 _this.renderCellObject(child);
6678             })
6679         }
6680     },
6681     
6682     getRowIndex : function(row)
6683     {
6684         var rowIndex = -1;
6685         
6686         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6687             if(el != row){
6688                 return;
6689             }
6690             
6691             rowIndex = index;
6692         });
6693         
6694         return rowIndex;
6695     },
6696      /**
6697      * Returns the grid's underlying element = used by panel.Grid
6698      * @return {Element} The element
6699      */
6700     getGridEl : function(){
6701         return this.el;
6702     },
6703      /**
6704      * Forces a resize - used by panel.Grid
6705      * @return {Element} The element
6706      */
6707     autoSize : function()
6708     {
6709         //var ctr = Roo.get(this.container.dom.parentElement);
6710         var ctr = Roo.get(this.el.dom);
6711         
6712         var thd = this.getGridEl().select('thead',true).first();
6713         var tbd = this.getGridEl().select('tbody', true).first();
6714         
6715         
6716         var cw = ctr.getWidth();
6717         
6718         if (tbd) {
6719             
6720             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6721             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6722             cw -= barsize;
6723         }
6724         cw = Math.max(cw, this.totalWidth);
6725         this.getGridEl().select('tr',true).setWidth(cw);
6726         // resize 'expandable coloumn?
6727         
6728         return; // we doe not have a view in this design..
6729         
6730     },
6731     onBodyScroll: function()
6732     {
6733         
6734         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6735         this.mainHead.setStyle({
6736                     'position' : 'relative',
6737                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6738         });
6739         
6740         
6741     }
6742 });
6743
6744  
6745
6746  /*
6747  * - LGPL
6748  *
6749  * table cell
6750  * 
6751  */
6752
6753 /**
6754  * @class Roo.bootstrap.TableCell
6755  * @extends Roo.bootstrap.Component
6756  * Bootstrap TableCell class
6757  * @cfg {String} html cell contain text
6758  * @cfg {String} cls cell class
6759  * @cfg {String} tag cell tag (td|th) default td
6760  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6761  * @cfg {String} align Aligns the content in a cell
6762  * @cfg {String} axis Categorizes cells
6763  * @cfg {String} bgcolor Specifies the background color of a cell
6764  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6765  * @cfg {Number} colspan Specifies the number of columns a cell should span
6766  * @cfg {String} headers Specifies one or more header cells a cell is related to
6767  * @cfg {Number} height Sets the height of a cell
6768  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6769  * @cfg {Number} rowspan Sets the number of rows a cell should span
6770  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6771  * @cfg {String} valign Vertical aligns the content in a cell
6772  * @cfg {Number} width Specifies the width of a cell
6773  * 
6774  * @constructor
6775  * Create a new TableCell
6776  * @param {Object} config The config object
6777  */
6778
6779 Roo.bootstrap.TableCell = function(config){
6780     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6781 };
6782
6783 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6784     
6785     html: false,
6786     cls: false,
6787     tag: false,
6788     abbr: false,
6789     align: false,
6790     axis: false,
6791     bgcolor: false,
6792     charoff: false,
6793     colspan: false,
6794     headers: false,
6795     height: false,
6796     nowrap: false,
6797     rowspan: false,
6798     scope: false,
6799     valign: false,
6800     width: false,
6801     
6802     
6803     getAutoCreate : function(){
6804         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6805         
6806         cfg = {
6807             tag: 'td'
6808         };
6809         
6810         if(this.tag){
6811             cfg.tag = this.tag;
6812         }
6813         
6814         if (this.html) {
6815             cfg.html=this.html
6816         }
6817         if (this.cls) {
6818             cfg.cls=this.cls
6819         }
6820         if (this.abbr) {
6821             cfg.abbr=this.abbr
6822         }
6823         if (this.align) {
6824             cfg.align=this.align
6825         }
6826         if (this.axis) {
6827             cfg.axis=this.axis
6828         }
6829         if (this.bgcolor) {
6830             cfg.bgcolor=this.bgcolor
6831         }
6832         if (this.charoff) {
6833             cfg.charoff=this.charoff
6834         }
6835         if (this.colspan) {
6836             cfg.colspan=this.colspan
6837         }
6838         if (this.headers) {
6839             cfg.headers=this.headers
6840         }
6841         if (this.height) {
6842             cfg.height=this.height
6843         }
6844         if (this.nowrap) {
6845             cfg.nowrap=this.nowrap
6846         }
6847         if (this.rowspan) {
6848             cfg.rowspan=this.rowspan
6849         }
6850         if (this.scope) {
6851             cfg.scope=this.scope
6852         }
6853         if (this.valign) {
6854             cfg.valign=this.valign
6855         }
6856         if (this.width) {
6857             cfg.width=this.width
6858         }
6859         
6860         
6861         return cfg;
6862     }
6863    
6864 });
6865
6866  
6867
6868  /*
6869  * - LGPL
6870  *
6871  * table row
6872  * 
6873  */
6874
6875 /**
6876  * @class Roo.bootstrap.TableRow
6877  * @extends Roo.bootstrap.Component
6878  * Bootstrap TableRow class
6879  * @cfg {String} cls row class
6880  * @cfg {String} align Aligns the content in a table row
6881  * @cfg {String} bgcolor Specifies a background color for a table row
6882  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6883  * @cfg {String} valign Vertical aligns the content in a table row
6884  * 
6885  * @constructor
6886  * Create a new TableRow
6887  * @param {Object} config The config object
6888  */
6889
6890 Roo.bootstrap.TableRow = function(config){
6891     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6892 };
6893
6894 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6895     
6896     cls: false,
6897     align: false,
6898     bgcolor: false,
6899     charoff: false,
6900     valign: false,
6901     
6902     getAutoCreate : function(){
6903         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6904         
6905         cfg = {
6906             tag: 'tr'
6907         };
6908             
6909         if(this.cls){
6910             cfg.cls = this.cls;
6911         }
6912         if(this.align){
6913             cfg.align = this.align;
6914         }
6915         if(this.bgcolor){
6916             cfg.bgcolor = this.bgcolor;
6917         }
6918         if(this.charoff){
6919             cfg.charoff = this.charoff;
6920         }
6921         if(this.valign){
6922             cfg.valign = this.valign;
6923         }
6924         
6925         return cfg;
6926     }
6927    
6928 });
6929
6930  
6931
6932  /*
6933  * - LGPL
6934  *
6935  * table body
6936  * 
6937  */
6938
6939 /**
6940  * @class Roo.bootstrap.TableBody
6941  * @extends Roo.bootstrap.Component
6942  * Bootstrap TableBody class
6943  * @cfg {String} cls element class
6944  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6945  * @cfg {String} align Aligns the content inside the element
6946  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6947  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6948  * 
6949  * @constructor
6950  * Create a new TableBody
6951  * @param {Object} config The config object
6952  */
6953
6954 Roo.bootstrap.TableBody = function(config){
6955     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6956 };
6957
6958 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6959     
6960     cls: false,
6961     tag: false,
6962     align: false,
6963     charoff: false,
6964     valign: false,
6965     
6966     getAutoCreate : function(){
6967         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6968         
6969         cfg = {
6970             tag: 'tbody'
6971         };
6972             
6973         if (this.cls) {
6974             cfg.cls=this.cls
6975         }
6976         if(this.tag){
6977             cfg.tag = this.tag;
6978         }
6979         
6980         if(this.align){
6981             cfg.align = this.align;
6982         }
6983         if(this.charoff){
6984             cfg.charoff = this.charoff;
6985         }
6986         if(this.valign){
6987             cfg.valign = this.valign;
6988         }
6989         
6990         return cfg;
6991     }
6992     
6993     
6994 //    initEvents : function()
6995 //    {
6996 //        
6997 //        if(!this.store){
6998 //            return;
6999 //        }
7000 //        
7001 //        this.store = Roo.factory(this.store, Roo.data);
7002 //        this.store.on('load', this.onLoad, this);
7003 //        
7004 //        this.store.load();
7005 //        
7006 //    },
7007 //    
7008 //    onLoad: function () 
7009 //    {   
7010 //        this.fireEvent('load', this);
7011 //    }
7012 //    
7013 //   
7014 });
7015
7016  
7017
7018  /*
7019  * Based on:
7020  * Ext JS Library 1.1.1
7021  * Copyright(c) 2006-2007, Ext JS, LLC.
7022  *
7023  * Originally Released Under LGPL - original licence link has changed is not relivant.
7024  *
7025  * Fork - LGPL
7026  * <script type="text/javascript">
7027  */
7028
7029 // as we use this in bootstrap.
7030 Roo.namespace('Roo.form');
7031  /**
7032  * @class Roo.form.Action
7033  * Internal Class used to handle form actions
7034  * @constructor
7035  * @param {Roo.form.BasicForm} el The form element or its id
7036  * @param {Object} config Configuration options
7037  */
7038
7039  
7040  
7041 // define the action interface
7042 Roo.form.Action = function(form, options){
7043     this.form = form;
7044     this.options = options || {};
7045 };
7046 /**
7047  * Client Validation Failed
7048  * @const 
7049  */
7050 Roo.form.Action.CLIENT_INVALID = 'client';
7051 /**
7052  * Server Validation Failed
7053  * @const 
7054  */
7055 Roo.form.Action.SERVER_INVALID = 'server';
7056  /**
7057  * Connect to Server Failed
7058  * @const 
7059  */
7060 Roo.form.Action.CONNECT_FAILURE = 'connect';
7061 /**
7062  * Reading Data from Server Failed
7063  * @const 
7064  */
7065 Roo.form.Action.LOAD_FAILURE = 'load';
7066
7067 Roo.form.Action.prototype = {
7068     type : 'default',
7069     failureType : undefined,
7070     response : undefined,
7071     result : undefined,
7072
7073     // interface method
7074     run : function(options){
7075
7076     },
7077
7078     // interface method
7079     success : function(response){
7080
7081     },
7082
7083     // interface method
7084     handleResponse : function(response){
7085
7086     },
7087
7088     // default connection failure
7089     failure : function(response){
7090         
7091         this.response = response;
7092         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7093         this.form.afterAction(this, false);
7094     },
7095
7096     processResponse : function(response){
7097         this.response = response;
7098         if(!response.responseText){
7099             return true;
7100         }
7101         this.result = this.handleResponse(response);
7102         return this.result;
7103     },
7104
7105     // utility functions used internally
7106     getUrl : function(appendParams){
7107         var url = this.options.url || this.form.url || this.form.el.dom.action;
7108         if(appendParams){
7109             var p = this.getParams();
7110             if(p){
7111                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7112             }
7113         }
7114         return url;
7115     },
7116
7117     getMethod : function(){
7118         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7119     },
7120
7121     getParams : function(){
7122         var bp = this.form.baseParams;
7123         var p = this.options.params;
7124         if(p){
7125             if(typeof p == "object"){
7126                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7127             }else if(typeof p == 'string' && bp){
7128                 p += '&' + Roo.urlEncode(bp);
7129             }
7130         }else if(bp){
7131             p = Roo.urlEncode(bp);
7132         }
7133         return p;
7134     },
7135
7136     createCallback : function(){
7137         return {
7138             success: this.success,
7139             failure: this.failure,
7140             scope: this,
7141             timeout: (this.form.timeout*1000),
7142             upload: this.form.fileUpload ? this.success : undefined
7143         };
7144     }
7145 };
7146
7147 Roo.form.Action.Submit = function(form, options){
7148     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7149 };
7150
7151 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7152     type : 'submit',
7153
7154     haveProgress : false,
7155     uploadComplete : false,
7156     
7157     // uploadProgress indicator.
7158     uploadProgress : function()
7159     {
7160         if (!this.form.progressUrl) {
7161             return;
7162         }
7163         
7164         if (!this.haveProgress) {
7165             Roo.MessageBox.progress("Uploading", "Uploading");
7166         }
7167         if (this.uploadComplete) {
7168            Roo.MessageBox.hide();
7169            return;
7170         }
7171         
7172         this.haveProgress = true;
7173    
7174         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7175         
7176         var c = new Roo.data.Connection();
7177         c.request({
7178             url : this.form.progressUrl,
7179             params: {
7180                 id : uid
7181             },
7182             method: 'GET',
7183             success : function(req){
7184                //console.log(data);
7185                 var rdata = false;
7186                 var edata;
7187                 try  {
7188                    rdata = Roo.decode(req.responseText)
7189                 } catch (e) {
7190                     Roo.log("Invalid data from server..");
7191                     Roo.log(edata);
7192                     return;
7193                 }
7194                 if (!rdata || !rdata.success) {
7195                     Roo.log(rdata);
7196                     Roo.MessageBox.alert(Roo.encode(rdata));
7197                     return;
7198                 }
7199                 var data = rdata.data;
7200                 
7201                 if (this.uploadComplete) {
7202                    Roo.MessageBox.hide();
7203                    return;
7204                 }
7205                    
7206                 if (data){
7207                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7208                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7209                     );
7210                 }
7211                 this.uploadProgress.defer(2000,this);
7212             },
7213        
7214             failure: function(data) {
7215                 Roo.log('progress url failed ');
7216                 Roo.log(data);
7217             },
7218             scope : this
7219         });
7220            
7221     },
7222     
7223     
7224     run : function()
7225     {
7226         // run get Values on the form, so it syncs any secondary forms.
7227         this.form.getValues();
7228         
7229         var o = this.options;
7230         var method = this.getMethod();
7231         var isPost = method == 'POST';
7232         if(o.clientValidation === false || this.form.isValid()){
7233             
7234             if (this.form.progressUrl) {
7235                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7236                     (new Date() * 1) + '' + Math.random());
7237                     
7238             } 
7239             
7240             
7241             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7242                 form:this.form.el.dom,
7243                 url:this.getUrl(!isPost),
7244                 method: method,
7245                 params:isPost ? this.getParams() : null,
7246                 isUpload: this.form.fileUpload
7247             }));
7248             
7249             this.uploadProgress();
7250
7251         }else if (o.clientValidation !== false){ // client validation failed
7252             this.failureType = Roo.form.Action.CLIENT_INVALID;
7253             this.form.afterAction(this, false);
7254         }
7255     },
7256
7257     success : function(response)
7258     {
7259         this.uploadComplete= true;
7260         if (this.haveProgress) {
7261             Roo.MessageBox.hide();
7262         }
7263         
7264         
7265         var result = this.processResponse(response);
7266         if(result === true || result.success){
7267             this.form.afterAction(this, true);
7268             return;
7269         }
7270         if(result.errors){
7271             this.form.markInvalid(result.errors);
7272             this.failureType = Roo.form.Action.SERVER_INVALID;
7273         }
7274         this.form.afterAction(this, false);
7275     },
7276     failure : function(response)
7277     {
7278         this.uploadComplete= true;
7279         if (this.haveProgress) {
7280             Roo.MessageBox.hide();
7281         }
7282         
7283         this.response = response;
7284         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7285         this.form.afterAction(this, false);
7286     },
7287     
7288     handleResponse : function(response){
7289         if(this.form.errorReader){
7290             var rs = this.form.errorReader.read(response);
7291             var errors = [];
7292             if(rs.records){
7293                 for(var i = 0, len = rs.records.length; i < len; i++) {
7294                     var r = rs.records[i];
7295                     errors[i] = r.data;
7296                 }
7297             }
7298             if(errors.length < 1){
7299                 errors = null;
7300             }
7301             return {
7302                 success : rs.success,
7303                 errors : errors
7304             };
7305         }
7306         var ret = false;
7307         try {
7308             ret = Roo.decode(response.responseText);
7309         } catch (e) {
7310             ret = {
7311                 success: false,
7312                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7313                 errors : []
7314             };
7315         }
7316         return ret;
7317         
7318     }
7319 });
7320
7321
7322 Roo.form.Action.Load = function(form, options){
7323     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7324     this.reader = this.form.reader;
7325 };
7326
7327 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7328     type : 'load',
7329
7330     run : function(){
7331         
7332         Roo.Ajax.request(Roo.apply(
7333                 this.createCallback(), {
7334                     method:this.getMethod(),
7335                     url:this.getUrl(false),
7336                     params:this.getParams()
7337         }));
7338     },
7339
7340     success : function(response){
7341         
7342         var result = this.processResponse(response);
7343         if(result === true || !result.success || !result.data){
7344             this.failureType = Roo.form.Action.LOAD_FAILURE;
7345             this.form.afterAction(this, false);
7346             return;
7347         }
7348         this.form.clearInvalid();
7349         this.form.setValues(result.data);
7350         this.form.afterAction(this, true);
7351     },
7352
7353     handleResponse : function(response){
7354         if(this.form.reader){
7355             var rs = this.form.reader.read(response);
7356             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7357             return {
7358                 success : rs.success,
7359                 data : data
7360             };
7361         }
7362         return Roo.decode(response.responseText);
7363     }
7364 });
7365
7366 Roo.form.Action.ACTION_TYPES = {
7367     'load' : Roo.form.Action.Load,
7368     'submit' : Roo.form.Action.Submit
7369 };/*
7370  * - LGPL
7371  *
7372  * form
7373  * 
7374  */
7375
7376 /**
7377  * @class Roo.bootstrap.Form
7378  * @extends Roo.bootstrap.Component
7379  * Bootstrap Form class
7380  * @cfg {String} method  GET | POST (default POST)
7381  * @cfg {String} labelAlign top | left (default top)
7382  * @cfg {String} align left  | right - for navbars
7383  * @cfg {Boolean} loadMask load mask when submit (default true)
7384
7385  * 
7386  * @constructor
7387  * Create a new Form
7388  * @param {Object} config The config object
7389  */
7390
7391
7392 Roo.bootstrap.Form = function(config){
7393     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7394     this.addEvents({
7395         /**
7396          * @event clientvalidation
7397          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7398          * @param {Form} this
7399          * @param {Boolean} valid true if the form has passed client-side validation
7400          */
7401         clientvalidation: true,
7402         /**
7403          * @event beforeaction
7404          * Fires before any action is performed. Return false to cancel the action.
7405          * @param {Form} this
7406          * @param {Action} action The action to be performed
7407          */
7408         beforeaction: true,
7409         /**
7410          * @event actionfailed
7411          * Fires when an action fails.
7412          * @param {Form} this
7413          * @param {Action} action The action that failed
7414          */
7415         actionfailed : true,
7416         /**
7417          * @event actioncomplete
7418          * Fires when an action is completed.
7419          * @param {Form} this
7420          * @param {Action} action The action that completed
7421          */
7422         actioncomplete : true
7423     });
7424     
7425 };
7426
7427 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7428       
7429      /**
7430      * @cfg {String} method
7431      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7432      */
7433     method : 'POST',
7434     /**
7435      * @cfg {String} url
7436      * The URL to use for form actions if one isn't supplied in the action options.
7437      */
7438     /**
7439      * @cfg {Boolean} fileUpload
7440      * Set to true if this form is a file upload.
7441      */
7442      
7443     /**
7444      * @cfg {Object} baseParams
7445      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7446      */
7447       
7448     /**
7449      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7450      */
7451     timeout: 30,
7452     /**
7453      * @cfg {Sting} align (left|right) for navbar forms
7454      */
7455     align : 'left',
7456
7457     // private
7458     activeAction : null,
7459  
7460     /**
7461      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7462      * element by passing it or its id or mask the form itself by passing in true.
7463      * @type Mixed
7464      */
7465     waitMsgTarget : false,
7466     
7467     loadMask : true,
7468     
7469     getAutoCreate : function(){
7470         
7471         var cfg = {
7472             tag: 'form',
7473             method : this.method || 'POST',
7474             id : this.id || Roo.id(),
7475             cls : ''
7476         };
7477         if (this.parent().xtype.match(/^Nav/)) {
7478             cfg.cls = 'navbar-form navbar-' + this.align;
7479             
7480         }
7481         
7482         if (this.labelAlign == 'left' ) {
7483             cfg.cls += ' form-horizontal';
7484         }
7485         
7486         
7487         return cfg;
7488     },
7489     initEvents : function()
7490     {
7491         this.el.on('submit', this.onSubmit, this);
7492         // this was added as random key presses on the form where triggering form submit.
7493         this.el.on('keypress', function(e) {
7494             if (e.getCharCode() != 13) {
7495                 return true;
7496             }
7497             // we might need to allow it for textareas.. and some other items.
7498             // check e.getTarget().
7499             
7500             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7501                 return true;
7502             }
7503         
7504             Roo.log("keypress blocked");
7505             
7506             e.preventDefault();
7507             return false;
7508         });
7509         
7510     },
7511     // private
7512     onSubmit : function(e){
7513         e.stopEvent();
7514     },
7515     
7516      /**
7517      * Returns true if client-side validation on the form is successful.
7518      * @return Boolean
7519      */
7520     isValid : function(){
7521         var items = this.getItems();
7522         var valid = true;
7523         items.each(function(f){
7524            if(!f.validate()){
7525                valid = false;
7526                
7527            }
7528         });
7529         return valid;
7530     },
7531     /**
7532      * Returns true if any fields in this form have changed since their original load.
7533      * @return Boolean
7534      */
7535     isDirty : function(){
7536         var dirty = false;
7537         var items = this.getItems();
7538         items.each(function(f){
7539            if(f.isDirty()){
7540                dirty = true;
7541                return false;
7542            }
7543            return true;
7544         });
7545         return dirty;
7546     },
7547      /**
7548      * Performs a predefined action (submit or load) or custom actions you define on this form.
7549      * @param {String} actionName The name of the action type
7550      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7551      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7552      * accept other config options):
7553      * <pre>
7554 Property          Type             Description
7555 ----------------  ---------------  ----------------------------------------------------------------------------------
7556 url               String           The url for the action (defaults to the form's url)
7557 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7558 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7559 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7560                                    validate the form on the client (defaults to false)
7561      * </pre>
7562      * @return {BasicForm} this
7563      */
7564     doAction : function(action, options){
7565         if(typeof action == 'string'){
7566             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7567         }
7568         if(this.fireEvent('beforeaction', this, action) !== false){
7569             this.beforeAction(action);
7570             action.run.defer(100, action);
7571         }
7572         return this;
7573     },
7574     
7575     // private
7576     beforeAction : function(action){
7577         var o = action.options;
7578         
7579         if(this.loadMask){
7580             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7581         }
7582         // not really supported yet.. ??
7583         
7584         //if(this.waitMsgTarget === true){
7585         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7586         //}else if(this.waitMsgTarget){
7587         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7588         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7589         //}else {
7590         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7591        // }
7592          
7593     },
7594
7595     // private
7596     afterAction : function(action, success){
7597         this.activeAction = null;
7598         var o = action.options;
7599         
7600         //if(this.waitMsgTarget === true){
7601             this.el.unmask();
7602         //}else if(this.waitMsgTarget){
7603         //    this.waitMsgTarget.unmask();
7604         //}else{
7605         //    Roo.MessageBox.updateProgress(1);
7606         //    Roo.MessageBox.hide();
7607        // }
7608         // 
7609         if(success){
7610             if(o.reset){
7611                 this.reset();
7612             }
7613             Roo.callback(o.success, o.scope, [this, action]);
7614             this.fireEvent('actioncomplete', this, action);
7615             
7616         }else{
7617             
7618             // failure condition..
7619             // we have a scenario where updates need confirming.
7620             // eg. if a locking scenario exists..
7621             // we look for { errors : { needs_confirm : true }} in the response.
7622             if (
7623                 (typeof(action.result) != 'undefined')  &&
7624                 (typeof(action.result.errors) != 'undefined')  &&
7625                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7626            ){
7627                 var _t = this;
7628                 Roo.log("not supported yet");
7629                  /*
7630                 
7631                 Roo.MessageBox.confirm(
7632                     "Change requires confirmation",
7633                     action.result.errorMsg,
7634                     function(r) {
7635                         if (r != 'yes') {
7636                             return;
7637                         }
7638                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7639                     }
7640                     
7641                 );
7642                 */
7643                 
7644                 
7645                 return;
7646             }
7647             
7648             Roo.callback(o.failure, o.scope, [this, action]);
7649             // show an error message if no failed handler is set..
7650             if (!this.hasListener('actionfailed')) {
7651                 Roo.log("need to add dialog support");
7652                 /*
7653                 Roo.MessageBox.alert("Error",
7654                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7655                         action.result.errorMsg :
7656                         "Saving Failed, please check your entries or try again"
7657                 );
7658                 */
7659             }
7660             
7661             this.fireEvent('actionfailed', this, action);
7662         }
7663         
7664     },
7665     /**
7666      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7667      * @param {String} id The value to search for
7668      * @return Field
7669      */
7670     findField : function(id){
7671         var items = this.getItems();
7672         var field = items.get(id);
7673         if(!field){
7674              items.each(function(f){
7675                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7676                     field = f;
7677                     return false;
7678                 }
7679                 return true;
7680             });
7681         }
7682         return field || null;
7683     },
7684      /**
7685      * Mark fields in this form invalid in bulk.
7686      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7687      * @return {BasicForm} this
7688      */
7689     markInvalid : function(errors){
7690         if(errors instanceof Array){
7691             for(var i = 0, len = errors.length; i < len; i++){
7692                 var fieldError = errors[i];
7693                 var f = this.findField(fieldError.id);
7694                 if(f){
7695                     f.markInvalid(fieldError.msg);
7696                 }
7697             }
7698         }else{
7699             var field, id;
7700             for(id in errors){
7701                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7702                     field.markInvalid(errors[id]);
7703                 }
7704             }
7705         }
7706         //Roo.each(this.childForms || [], function (f) {
7707         //    f.markInvalid(errors);
7708         //});
7709         
7710         return this;
7711     },
7712
7713     /**
7714      * Set values for fields in this form in bulk.
7715      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7716      * @return {BasicForm} this
7717      */
7718     setValues : function(values){
7719         if(values instanceof Array){ // array of objects
7720             for(var i = 0, len = values.length; i < len; i++){
7721                 var v = values[i];
7722                 var f = this.findField(v.id);
7723                 if(f){
7724                     f.setValue(v.value);
7725                     if(this.trackResetOnLoad){
7726                         f.originalValue = f.getValue();
7727                     }
7728                 }
7729             }
7730         }else{ // object hash
7731             var field, id;
7732             for(id in values){
7733                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7734                     
7735                     if (field.setFromData && 
7736                         field.valueField && 
7737                         field.displayField &&
7738                         // combos' with local stores can 
7739                         // be queried via setValue()
7740                         // to set their value..
7741                         (field.store && !field.store.isLocal)
7742                         ) {
7743                         // it's a combo
7744                         var sd = { };
7745                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7746                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7747                         field.setFromData(sd);
7748                         
7749                     } else {
7750                         field.setValue(values[id]);
7751                     }
7752                     
7753                     
7754                     if(this.trackResetOnLoad){
7755                         field.originalValue = field.getValue();
7756                     }
7757                 }
7758             }
7759         }
7760          
7761         //Roo.each(this.childForms || [], function (f) {
7762         //    f.setValues(values);
7763         //});
7764                 
7765         return this;
7766     },
7767
7768     /**
7769      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7770      * they are returned as an array.
7771      * @param {Boolean} asString
7772      * @return {Object}
7773      */
7774     getValues : function(asString){
7775         //if (this.childForms) {
7776             // copy values from the child forms
7777         //    Roo.each(this.childForms, function (f) {
7778         //        this.setValues(f.getValues());
7779         //    }, this);
7780         //}
7781         
7782         
7783         
7784         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7785         if(asString === true){
7786             return fs;
7787         }
7788         return Roo.urlDecode(fs);
7789     },
7790     
7791     /**
7792      * Returns the fields in this form as an object with key/value pairs. 
7793      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7794      * @return {Object}
7795      */
7796     getFieldValues : function(with_hidden)
7797     {
7798         var items = this.getItems();
7799         var ret = {};
7800         items.each(function(f){
7801             if (!f.getName()) {
7802                 return;
7803             }
7804             var v = f.getValue();
7805             if (f.inputType =='radio') {
7806                 if (typeof(ret[f.getName()]) == 'undefined') {
7807                     ret[f.getName()] = ''; // empty..
7808                 }
7809                 
7810                 if (!f.el.dom.checked) {
7811                     return;
7812                     
7813                 }
7814                 v = f.el.dom.value;
7815                 
7816             }
7817             
7818             // not sure if this supported any more..
7819             if ((typeof(v) == 'object') && f.getRawValue) {
7820                 v = f.getRawValue() ; // dates..
7821             }
7822             // combo boxes where name != hiddenName...
7823             if (f.name != f.getName()) {
7824                 ret[f.name] = f.getRawValue();
7825             }
7826             ret[f.getName()] = v;
7827         });
7828         
7829         return ret;
7830     },
7831
7832     /**
7833      * Clears all invalid messages in this form.
7834      * @return {BasicForm} this
7835      */
7836     clearInvalid : function(){
7837         var items = this.getItems();
7838         
7839         items.each(function(f){
7840            f.clearInvalid();
7841         });
7842         
7843         
7844         
7845         return this;
7846     },
7847
7848     /**
7849      * Resets this form.
7850      * @return {BasicForm} this
7851      */
7852     reset : function(){
7853         var items = this.getItems();
7854         items.each(function(f){
7855             f.reset();
7856         });
7857         
7858         Roo.each(this.childForms || [], function (f) {
7859             f.reset();
7860         });
7861        
7862         
7863         return this;
7864     },
7865     getItems : function()
7866     {
7867         var r=new Roo.util.MixedCollection(false, function(o){
7868             return o.id || (o.id = Roo.id());
7869         });
7870         var iter = function(el) {
7871             if (el.inputEl) {
7872                 r.add(el);
7873             }
7874             if (!el.items) {
7875                 return;
7876             }
7877             Roo.each(el.items,function(e) {
7878                 iter(e);
7879             });
7880             
7881             
7882         };
7883         
7884         iter(this);
7885         return r;
7886         
7887         
7888         
7889         
7890     }
7891     
7892 });
7893
7894  
7895 /*
7896  * Based on:
7897  * Ext JS Library 1.1.1
7898  * Copyright(c) 2006-2007, Ext JS, LLC.
7899  *
7900  * Originally Released Under LGPL - original licence link has changed is not relivant.
7901  *
7902  * Fork - LGPL
7903  * <script type="text/javascript">
7904  */
7905 /**
7906  * @class Roo.form.VTypes
7907  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7908  * @singleton
7909  */
7910 Roo.form.VTypes = function(){
7911     // closure these in so they are only created once.
7912     var alpha = /^[a-zA-Z_]+$/;
7913     var alphanum = /^[a-zA-Z0-9_]+$/;
7914     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7915     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7916
7917     // All these messages and functions are configurable
7918     return {
7919         /**
7920          * The function used to validate email addresses
7921          * @param {String} value The email address
7922          */
7923         'email' : function(v){
7924             return email.test(v);
7925         },
7926         /**
7927          * The error text to display when the email validation function returns false
7928          * @type String
7929          */
7930         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7931         /**
7932          * The keystroke filter mask to be applied on email input
7933          * @type RegExp
7934          */
7935         'emailMask' : /[a-z0-9_\.\-@]/i,
7936
7937         /**
7938          * The function used to validate URLs
7939          * @param {String} value The URL
7940          */
7941         'url' : function(v){
7942             return url.test(v);
7943         },
7944         /**
7945          * The error text to display when the url validation function returns false
7946          * @type String
7947          */
7948         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7949         
7950         /**
7951          * The function used to validate alpha values
7952          * @param {String} value The value
7953          */
7954         'alpha' : function(v){
7955             return alpha.test(v);
7956         },
7957         /**
7958          * The error text to display when the alpha validation function returns false
7959          * @type String
7960          */
7961         'alphaText' : 'This field should only contain letters and _',
7962         /**
7963          * The keystroke filter mask to be applied on alpha input
7964          * @type RegExp
7965          */
7966         'alphaMask' : /[a-z_]/i,
7967
7968         /**
7969          * The function used to validate alphanumeric values
7970          * @param {String} value The value
7971          */
7972         'alphanum' : function(v){
7973             return alphanum.test(v);
7974         },
7975         /**
7976          * The error text to display when the alphanumeric validation function returns false
7977          * @type String
7978          */
7979         'alphanumText' : 'This field should only contain letters, numbers and _',
7980         /**
7981          * The keystroke filter mask to be applied on alphanumeric input
7982          * @type RegExp
7983          */
7984         'alphanumMask' : /[a-z0-9_]/i
7985     };
7986 }();/*
7987  * - LGPL
7988  *
7989  * Input
7990  * 
7991  */
7992
7993 /**
7994  * @class Roo.bootstrap.Input
7995  * @extends Roo.bootstrap.Component
7996  * Bootstrap Input class
7997  * @cfg {Boolean} disabled is it disabled
7998  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7999  * @cfg {String} name name of the input
8000  * @cfg {string} fieldLabel - the label associated
8001  * @cfg {string} placeholder - placeholder to put in text.
8002  * @cfg {string}  before - input group add on before
8003  * @cfg {string} after - input group add on after
8004  * @cfg {string} size - (lg|sm) or leave empty..
8005  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8006  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8007  * @cfg {Number} md colspan out of 12 for computer-sized screens
8008  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8009  * @cfg {string} value default value of the input
8010  * @cfg {Number} labelWidth set the width of label (0-12)
8011  * @cfg {String} labelAlign (top|left)
8012  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8013  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8014
8015  * @cfg {String} align (left|center|right) Default left
8016  * @cfg {Boolean} forceFeedback (true|false) Default false
8017  * 
8018  * 
8019  * 
8020  * 
8021  * @constructor
8022  * Create a new Input
8023  * @param {Object} config The config object
8024  */
8025
8026 Roo.bootstrap.Input = function(config){
8027     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8028    
8029         this.addEvents({
8030             /**
8031              * @event focus
8032              * Fires when this field receives input focus.
8033              * @param {Roo.form.Field} this
8034              */
8035             focus : true,
8036             /**
8037              * @event blur
8038              * Fires when this field loses input focus.
8039              * @param {Roo.form.Field} this
8040              */
8041             blur : true,
8042             /**
8043              * @event specialkey
8044              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8045              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8046              * @param {Roo.form.Field} this
8047              * @param {Roo.EventObject} e The event object
8048              */
8049             specialkey : true,
8050             /**
8051              * @event change
8052              * Fires just before the field blurs if the field value has changed.
8053              * @param {Roo.form.Field} this
8054              * @param {Mixed} newValue The new value
8055              * @param {Mixed} oldValue The original value
8056              */
8057             change : true,
8058             /**
8059              * @event invalid
8060              * Fires after the field has been marked as invalid.
8061              * @param {Roo.form.Field} this
8062              * @param {String} msg The validation message
8063              */
8064             invalid : true,
8065             /**
8066              * @event valid
8067              * Fires after the field has been validated with no errors.
8068              * @param {Roo.form.Field} this
8069              */
8070             valid : true,
8071              /**
8072              * @event keyup
8073              * Fires after the key up
8074              * @param {Roo.form.Field} this
8075              * @param {Roo.EventObject}  e The event Object
8076              */
8077             keyup : true
8078         });
8079 };
8080
8081 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8082      /**
8083      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8084       automatic validation (defaults to "keyup").
8085      */
8086     validationEvent : "keyup",
8087      /**
8088      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8089      */
8090     validateOnBlur : true,
8091     /**
8092      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8093      */
8094     validationDelay : 250,
8095      /**
8096      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8097      */
8098     focusClass : "x-form-focus",  // not needed???
8099     
8100        
8101     /**
8102      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8103      */
8104     invalidClass : "has-warning",
8105     
8106     /**
8107      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8108      */
8109     validClass : "has-success",
8110     
8111     /**
8112      * @cfg {Boolean} hasFeedback (true|false) default true
8113      */
8114     hasFeedback : true,
8115     
8116     /**
8117      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8118      */
8119     invalidFeedbackClass : "glyphicon-warning-sign",
8120     
8121     /**
8122      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8123      */
8124     validFeedbackClass : "glyphicon-ok",
8125     
8126     /**
8127      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8128      */
8129     selectOnFocus : false,
8130     
8131      /**
8132      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8133      */
8134     maskRe : null,
8135        /**
8136      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8137      */
8138     vtype : null,
8139     
8140       /**
8141      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8142      */
8143     disableKeyFilter : false,
8144     
8145        /**
8146      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8147      */
8148     disabled : false,
8149      /**
8150      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8151      */
8152     allowBlank : true,
8153     /**
8154      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8155      */
8156     blankText : "This field is required",
8157     
8158      /**
8159      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8160      */
8161     minLength : 0,
8162     /**
8163      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8164      */
8165     maxLength : Number.MAX_VALUE,
8166     /**
8167      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8168      */
8169     minLengthText : "The minimum length for this field is {0}",
8170     /**
8171      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8172      */
8173     maxLengthText : "The maximum length for this field is {0}",
8174   
8175     
8176     /**
8177      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8178      * If available, this function will be called only after the basic validators all return true, and will be passed the
8179      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8180      */
8181     validator : null,
8182     /**
8183      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8184      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8185      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8186      */
8187     regex : null,
8188     /**
8189      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8190      */
8191     regexText : "",
8192     
8193     autocomplete: false,
8194     
8195     
8196     fieldLabel : '',
8197     inputType : 'text',
8198     
8199     name : false,
8200     placeholder: false,
8201     before : false,
8202     after : false,
8203     size : false,
8204     hasFocus : false,
8205     preventMark: false,
8206     isFormField : true,
8207     value : '',
8208     labelWidth : 2,
8209     labelAlign : false,
8210     readOnly : false,
8211     align : false,
8212     formatedValue : false,
8213     forceFeedback : false,
8214     
8215     parentLabelAlign : function()
8216     {
8217         var parent = this;
8218         while (parent.parent()) {
8219             parent = parent.parent();
8220             if (typeof(parent.labelAlign) !='undefined') {
8221                 return parent.labelAlign;
8222             }
8223         }
8224         return 'left';
8225         
8226     },
8227     
8228     getAutoCreate : function(){
8229         
8230         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8231         
8232         var id = Roo.id();
8233         
8234         var cfg = {};
8235         
8236        
8237         
8238         if(this.inputType != 'hidden'){
8239             cfg.cls = 'form-group' //input-group
8240         }
8241         
8242         var input =  {
8243             tag: 'input',
8244             id : id,
8245             type : this.inputType,
8246             value : this.value,
8247             cls : 'form-control',
8248             placeholder : this.placeholder || '',
8249             autocomplete : this.autocomplete || 'new-password'
8250         };
8251         
8252         
8253         if(this.align){
8254             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8255         }
8256         
8257         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8258             input.maxLength = this.maxLength;
8259         }
8260         
8261         if (this.disabled) {
8262             input.disabled=true;
8263         }
8264         
8265         if (this.readOnly) {
8266             input.readonly=true;
8267         }
8268         
8269         if (this.name) {
8270             input.name = this.name;
8271         }
8272         if (this.size) {
8273             input.cls += ' input-' + this.size;
8274         }
8275         var settings=this;
8276         ['xs','sm','md','lg'].map(function(size){
8277             if (settings[size]) {
8278                 cfg.cls += ' col-' + size + '-' + settings[size];
8279             }
8280         });
8281         
8282         var inputblock = input;
8283         
8284         var feedback = {
8285             tag: 'span',
8286             cls: 'glyphicon form-control-feedback'
8287         };
8288             
8289         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8290             
8291             inputblock = {
8292                 cls : 'has-feedback',
8293                 cn :  [
8294                     input,
8295                     feedback
8296                 ] 
8297             };  
8298         }
8299         
8300         if (this.before || this.after) {
8301             
8302             inputblock = {
8303                 cls : 'input-group',
8304                 cn :  [] 
8305             };
8306             
8307             if (this.before && typeof(this.before) == 'string') {
8308                 
8309                 inputblock.cn.push({
8310                     tag :'span',
8311                     cls : 'roo-input-before input-group-addon',
8312                     html : this.before
8313                 });
8314             }
8315             if (this.before && typeof(this.before) == 'object') {
8316                 this.before = Roo.factory(this.before);
8317                 
8318                 inputblock.cn.push({
8319                     tag :'span',
8320                     cls : 'roo-input-before input-group-' +
8321                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8322                 });
8323             }
8324             
8325             inputblock.cn.push(input);
8326             
8327             if (this.after && typeof(this.after) == 'string') {
8328                 inputblock.cn.push({
8329                     tag :'span',
8330                     cls : 'roo-input-after input-group-addon',
8331                     html : this.after
8332                 });
8333             }
8334             if (this.after && typeof(this.after) == 'object') {
8335                 this.after = Roo.factory(this.after);
8336                 
8337                 inputblock.cn.push({
8338                     tag :'span',
8339                     cls : 'roo-input-after input-group-' +
8340                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8341                 });
8342             }
8343             
8344             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8345                 inputblock.cls += ' has-feedback';
8346                 inputblock.cn.push(feedback);
8347             }
8348         };
8349         
8350         if (align ==='left' && this.fieldLabel.length) {
8351                 
8352                 cfg.cn = [
8353                     
8354                     {
8355                         tag: 'label',
8356                         'for' :  id,
8357                         cls : 'control-label col-sm-' + this.labelWidth,
8358                         html : this.fieldLabel
8359                         
8360                     },
8361                     {
8362                         cls : "col-sm-" + (12 - this.labelWidth), 
8363                         cn: [
8364                             inputblock
8365                         ]
8366                     }
8367                     
8368                 ];
8369         } else if ( this.fieldLabel.length) {
8370                 
8371                  cfg.cn = [
8372                    
8373                     {
8374                         tag: 'label',
8375                         //cls : 'input-group-addon',
8376                         html : this.fieldLabel
8377                         
8378                     },
8379                     
8380                     inputblock
8381                     
8382                 ];
8383
8384         } else {
8385             
8386                 cfg.cn = [
8387                     
8388                         inputblock
8389                     
8390                 ];
8391                 
8392                 
8393         };
8394         
8395         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8396            cfg.cls += ' navbar-form';
8397         }
8398         if (this.parentType === 'NavGroup') {
8399            cfg.cls += ' navbar-form';
8400            cfg.tag = 'li';
8401         }
8402         return cfg;
8403         
8404     },
8405     /**
8406      * return the real input element.
8407      */
8408     inputEl: function ()
8409     {
8410         return this.el.select('input.form-control',true).first();
8411     },
8412     
8413     tooltipEl : function()
8414     {
8415         return this.inputEl();
8416     },
8417     
8418     setDisabled : function(v)
8419     {
8420         var i  = this.inputEl().dom;
8421         if (!v) {
8422             i.removeAttribute('disabled');
8423             return;
8424             
8425         }
8426         i.setAttribute('disabled','true');
8427     },
8428     initEvents : function()
8429     {
8430           
8431         this.inputEl().on("keydown" , this.fireKey,  this);
8432         this.inputEl().on("focus", this.onFocus,  this);
8433         this.inputEl().on("blur", this.onBlur,  this);
8434         
8435         this.inputEl().relayEvent('keyup', this);
8436  
8437         // reference to original value for reset
8438         this.originalValue = this.getValue();
8439         //Roo.form.TextField.superclass.initEvents.call(this);
8440         if(this.validationEvent == 'keyup'){
8441             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8442             this.inputEl().on('keyup', this.filterValidation, this);
8443         }
8444         else if(this.validationEvent !== false){
8445             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8446         }
8447         
8448         if(this.selectOnFocus){
8449             this.on("focus", this.preFocus, this);
8450             
8451         }
8452         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8453             this.inputEl().on("keypress", this.filterKeys, this);
8454         }
8455        /* if(this.grow){
8456             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8457             this.el.on("click", this.autoSize,  this);
8458         }
8459         */
8460         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8461             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8462         }
8463         
8464         if (typeof(this.before) == 'object') {
8465             this.before.render(this.el.select('.roo-input-before',true).first());
8466         }
8467         if (typeof(this.after) == 'object') {
8468             this.after.render(this.el.select('.roo-input-after',true).first());
8469         }
8470         
8471         
8472     },
8473     filterValidation : function(e){
8474         if(!e.isNavKeyPress()){
8475             this.validationTask.delay(this.validationDelay);
8476         }
8477     },
8478      /**
8479      * Validates the field value
8480      * @return {Boolean} True if the value is valid, else false
8481      */
8482     validate : function(){
8483         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8484         if(this.disabled || this.validateValue(this.getRawValue())){
8485             this.markValid();
8486             return true;
8487         }
8488         
8489         this.markInvalid();
8490         return false;
8491     },
8492     
8493     
8494     /**
8495      * Validates a value according to the field's validation rules and marks the field as invalid
8496      * if the validation fails
8497      * @param {Mixed} value The value to validate
8498      * @return {Boolean} True if the value is valid, else false
8499      */
8500     validateValue : function(value){
8501         if(value.length < 1)  { // if it's blank
8502             if(this.allowBlank){
8503                 return true;
8504             }
8505             return false;
8506         }
8507         
8508         if(value.length < this.minLength){
8509             return false;
8510         }
8511         if(value.length > this.maxLength){
8512             return false;
8513         }
8514         if(this.vtype){
8515             var vt = Roo.form.VTypes;
8516             if(!vt[this.vtype](value, this)){
8517                 return false;
8518             }
8519         }
8520         if(typeof this.validator == "function"){
8521             var msg = this.validator(value);
8522             if(msg !== true){
8523                 return false;
8524             }
8525         }
8526         
8527         if(this.regex && !this.regex.test(value)){
8528             return false;
8529         }
8530         
8531         return true;
8532     },
8533
8534     
8535     
8536      // private
8537     fireKey : function(e){
8538         //Roo.log('field ' + e.getKey());
8539         if(e.isNavKeyPress()){
8540             this.fireEvent("specialkey", this, e);
8541         }
8542     },
8543     focus : function (selectText){
8544         if(this.rendered){
8545             this.inputEl().focus();
8546             if(selectText === true){
8547                 this.inputEl().dom.select();
8548             }
8549         }
8550         return this;
8551     } ,
8552     
8553     onFocus : function(){
8554         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8555            // this.el.addClass(this.focusClass);
8556         }
8557         if(!this.hasFocus){
8558             this.hasFocus = true;
8559             this.startValue = this.getValue();
8560             this.fireEvent("focus", this);
8561         }
8562     },
8563     
8564     beforeBlur : Roo.emptyFn,
8565
8566     
8567     // private
8568     onBlur : function(){
8569         this.beforeBlur();
8570         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8571             //this.el.removeClass(this.focusClass);
8572         }
8573         this.hasFocus = false;
8574         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8575             this.validate();
8576         }
8577         var v = this.getValue();
8578         if(String(v) !== String(this.startValue)){
8579             this.fireEvent('change', this, v, this.startValue);
8580         }
8581         this.fireEvent("blur", this);
8582     },
8583     
8584     /**
8585      * Resets the current field value to the originally loaded value and clears any validation messages
8586      */
8587     reset : function(){
8588         this.setValue(this.originalValue);
8589         this.validate();
8590     },
8591      /**
8592      * Returns the name of the field
8593      * @return {Mixed} name The name field
8594      */
8595     getName: function(){
8596         return this.name;
8597     },
8598      /**
8599      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8600      * @return {Mixed} value The field value
8601      */
8602     getValue : function(){
8603         
8604         var v = this.inputEl().getValue();
8605         
8606         return v;
8607     },
8608     /**
8609      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8610      * @return {Mixed} value The field value
8611      */
8612     getRawValue : function(){
8613         var v = this.inputEl().getValue();
8614         
8615         return v;
8616     },
8617     
8618     /**
8619      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8620      * @param {Mixed} value The value to set
8621      */
8622     setRawValue : function(v){
8623         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8624     },
8625     
8626     selectText : function(start, end){
8627         var v = this.getRawValue();
8628         if(v.length > 0){
8629             start = start === undefined ? 0 : start;
8630             end = end === undefined ? v.length : end;
8631             var d = this.inputEl().dom;
8632             if(d.setSelectionRange){
8633                 d.setSelectionRange(start, end);
8634             }else if(d.createTextRange){
8635                 var range = d.createTextRange();
8636                 range.moveStart("character", start);
8637                 range.moveEnd("character", v.length-end);
8638                 range.select();
8639             }
8640         }
8641     },
8642     
8643     /**
8644      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8645      * @param {Mixed} value The value to set
8646      */
8647     setValue : function(v){
8648         this.value = v;
8649         if(this.rendered){
8650             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8651             this.validate();
8652         }
8653     },
8654     
8655     /*
8656     processValue : function(value){
8657         if(this.stripCharsRe){
8658             var newValue = value.replace(this.stripCharsRe, '');
8659             if(newValue !== value){
8660                 this.setRawValue(newValue);
8661                 return newValue;
8662             }
8663         }
8664         return value;
8665     },
8666   */
8667     preFocus : function(){
8668         
8669         if(this.selectOnFocus){
8670             this.inputEl().dom.select();
8671         }
8672     },
8673     filterKeys : function(e){
8674         var k = e.getKey();
8675         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8676             return;
8677         }
8678         var c = e.getCharCode(), cc = String.fromCharCode(c);
8679         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8680             return;
8681         }
8682         if(!this.maskRe.test(cc)){
8683             e.stopEvent();
8684         }
8685     },
8686      /**
8687      * Clear any invalid styles/messages for this field
8688      */
8689     clearInvalid : function(){
8690         
8691         if(!this.el || this.preventMark){ // not rendered
8692             return;
8693         }
8694         
8695         var label = this.el.select('label', true).first();
8696         var icon = this.el.select('i.fa-star', true).first();
8697         
8698         if(label && icon){
8699             icon.remove();
8700         }
8701         
8702         this.el.removeClass(this.invalidClass);
8703         
8704         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8705             
8706             var feedback = this.el.select('.form-control-feedback', true).first();
8707             
8708             if(feedback){
8709                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8710             }
8711             
8712         }
8713         
8714         this.fireEvent('valid', this);
8715     },
8716     
8717      /**
8718      * Mark this field as valid
8719      */
8720     markValid : function()
8721     {
8722         if(!this.el  || this.preventMark){ // not rendered
8723             return;
8724         }
8725         
8726         this.el.removeClass([this.invalidClass, this.validClass]);
8727         
8728         var feedback = this.el.select('.form-control-feedback', true).first();
8729             
8730         if(feedback){
8731             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8732         }
8733
8734         if(this.disabled || this.allowBlank){
8735             return;
8736         }
8737         
8738         var formGroup = this.el.findParent('.form-group', false, true);
8739         
8740         if(formGroup){
8741             
8742             var label = formGroup.select('label', true).first();
8743             var icon = formGroup.select('i.fa-star', true).first();
8744             
8745             if(label && icon){
8746                 icon.remove();
8747             }
8748         }
8749         
8750         this.el.addClass(this.validClass);
8751         
8752         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8753             
8754             var feedback = this.el.select('.form-control-feedback', true).first();
8755             
8756             if(feedback){
8757                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8758                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8759             }
8760             
8761         }
8762         
8763         this.fireEvent('valid', this);
8764     },
8765     
8766      /**
8767      * Mark this field as invalid
8768      * @param {String} msg The validation message
8769      */
8770     markInvalid : function(msg)
8771     {
8772         if(!this.el  || this.preventMark){ // not rendered
8773             return;
8774         }
8775         
8776         this.el.removeClass([this.invalidClass, this.validClass]);
8777         
8778         var feedback = this.el.select('.form-control-feedback', true).first();
8779             
8780         if(feedback){
8781             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8782         }
8783
8784         if(this.disabled || this.allowBlank){
8785             return;
8786         }
8787         
8788         var formGroup = this.el.findParent('.form-group', false, true);
8789         
8790         if(formGroup){
8791             var label = formGroup.select('label', true).first();
8792             var icon = formGroup.select('i.fa-star', true).first();
8793
8794             if(!this.getValue().length && label && !icon){
8795                 this.el.findParent('.form-group', false, true).createChild({
8796                     tag : 'i',
8797                     cls : 'text-danger fa fa-lg fa-star',
8798                     tooltip : 'This field is required',
8799                     style : 'margin-right:5px;'
8800                 }, label, true);
8801             }
8802         }
8803         
8804         
8805         this.el.addClass(this.invalidClass);
8806         
8807         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8808             
8809             var feedback = this.el.select('.form-control-feedback', true).first();
8810             
8811             if(feedback){
8812                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8813                 
8814                 if(this.getValue().length || this.forceFeedback){
8815                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8816                 }
8817                 
8818             }
8819             
8820         }
8821         
8822         this.fireEvent('invalid', this, msg);
8823     },
8824     // private
8825     SafariOnKeyDown : function(event)
8826     {
8827         // this is a workaround for a password hang bug on chrome/ webkit.
8828         
8829         var isSelectAll = false;
8830         
8831         if(this.inputEl().dom.selectionEnd > 0){
8832             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8833         }
8834         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8835             event.preventDefault();
8836             this.setValue('');
8837             return;
8838         }
8839         
8840         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8841             
8842             event.preventDefault();
8843             // this is very hacky as keydown always get's upper case.
8844             //
8845             var cc = String.fromCharCode(event.getCharCode());
8846             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8847             
8848         }
8849     },
8850     adjustWidth : function(tag, w){
8851         tag = tag.toLowerCase();
8852         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8853             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8854                 if(tag == 'input'){
8855                     return w + 2;
8856                 }
8857                 if(tag == 'textarea'){
8858                     return w-2;
8859                 }
8860             }else if(Roo.isOpera){
8861                 if(tag == 'input'){
8862                     return w + 2;
8863                 }
8864                 if(tag == 'textarea'){
8865                     return w-2;
8866                 }
8867             }
8868         }
8869         return w;
8870     }
8871     
8872 });
8873
8874  
8875 /*
8876  * - LGPL
8877  *
8878  * Input
8879  * 
8880  */
8881
8882 /**
8883  * @class Roo.bootstrap.TextArea
8884  * @extends Roo.bootstrap.Input
8885  * Bootstrap TextArea class
8886  * @cfg {Number} cols Specifies the visible width of a text area
8887  * @cfg {Number} rows Specifies the visible number of lines in a text area
8888  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8889  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8890  * @cfg {string} html text
8891  * 
8892  * @constructor
8893  * Create a new TextArea
8894  * @param {Object} config The config object
8895  */
8896
8897 Roo.bootstrap.TextArea = function(config){
8898     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8899    
8900 };
8901
8902 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8903      
8904     cols : false,
8905     rows : 5,
8906     readOnly : false,
8907     warp : 'soft',
8908     resize : false,
8909     value: false,
8910     html: false,
8911     
8912     getAutoCreate : function(){
8913         
8914         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8915         
8916         var id = Roo.id();
8917         
8918         var cfg = {};
8919         
8920         var input =  {
8921             tag: 'textarea',
8922             id : id,
8923             warp : this.warp,
8924             rows : this.rows,
8925             value : this.value || '',
8926             html: this.html || '',
8927             cls : 'form-control',
8928             placeholder : this.placeholder || '' 
8929             
8930         };
8931         
8932         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8933             input.maxLength = this.maxLength;
8934         }
8935         
8936         if(this.resize){
8937             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8938         }
8939         
8940         if(this.cols){
8941             input.cols = this.cols;
8942         }
8943         
8944         if (this.readOnly) {
8945             input.readonly = true;
8946         }
8947         
8948         if (this.name) {
8949             input.name = this.name;
8950         }
8951         
8952         if (this.size) {
8953             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8954         }
8955         
8956         var settings=this;
8957         ['xs','sm','md','lg'].map(function(size){
8958             if (settings[size]) {
8959                 cfg.cls += ' col-' + size + '-' + settings[size];
8960             }
8961         });
8962         
8963         var inputblock = input;
8964         
8965         if(this.hasFeedback && !this.allowBlank){
8966             
8967             var feedback = {
8968                 tag: 'span',
8969                 cls: 'glyphicon form-control-feedback'
8970             };
8971
8972             inputblock = {
8973                 cls : 'has-feedback',
8974                 cn :  [
8975                     input,
8976                     feedback
8977                 ] 
8978             };  
8979         }
8980         
8981         
8982         if (this.before || this.after) {
8983             
8984             inputblock = {
8985                 cls : 'input-group',
8986                 cn :  [] 
8987             };
8988             if (this.before) {
8989                 inputblock.cn.push({
8990                     tag :'span',
8991                     cls : 'input-group-addon',
8992                     html : this.before
8993                 });
8994             }
8995             
8996             inputblock.cn.push(input);
8997             
8998             if(this.hasFeedback && !this.allowBlank){
8999                 inputblock.cls += ' has-feedback';
9000                 inputblock.cn.push(feedback);
9001             }
9002             
9003             if (this.after) {
9004                 inputblock.cn.push({
9005                     tag :'span',
9006                     cls : 'input-group-addon',
9007                     html : this.after
9008                 });
9009             }
9010             
9011         }
9012         
9013         if (align ==='left' && this.fieldLabel.length) {
9014 //                Roo.log("left and has label");
9015                 cfg.cn = [
9016                     
9017                     {
9018                         tag: 'label',
9019                         'for' :  id,
9020                         cls : 'control-label col-sm-' + this.labelWidth,
9021                         html : this.fieldLabel
9022                         
9023                     },
9024                     {
9025                         cls : "col-sm-" + (12 - this.labelWidth), 
9026                         cn: [
9027                             inputblock
9028                         ]
9029                     }
9030                     
9031                 ];
9032         } else if ( this.fieldLabel.length) {
9033 //                Roo.log(" label");
9034                  cfg.cn = [
9035                    
9036                     {
9037                         tag: 'label',
9038                         //cls : 'input-group-addon',
9039                         html : this.fieldLabel
9040                         
9041                     },
9042                     
9043                     inputblock
9044                     
9045                 ];
9046
9047         } else {
9048             
9049 //                   Roo.log(" no label && no align");
9050                 cfg.cn = [
9051                     
9052                         inputblock
9053                     
9054                 ];
9055                 
9056                 
9057         }
9058         
9059         if (this.disabled) {
9060             input.disabled=true;
9061         }
9062         
9063         return cfg;
9064         
9065     },
9066     /**
9067      * return the real textarea element.
9068      */
9069     inputEl: function ()
9070     {
9071         return this.el.select('textarea.form-control',true).first();
9072     },
9073     
9074     /**
9075      * Clear any invalid styles/messages for this field
9076      */
9077     clearInvalid : function()
9078     {
9079         
9080         if(!this.el || this.preventMark){ // not rendered
9081             return;
9082         }
9083         
9084         var label = this.el.select('label', true).first();
9085         var icon = this.el.select('i.fa-star', true).first();
9086         
9087         if(label && icon){
9088             icon.remove();
9089         }
9090         
9091         this.el.removeClass(this.invalidClass);
9092         
9093         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9094             
9095             var feedback = this.el.select('.form-control-feedback', true).first();
9096             
9097             if(feedback){
9098                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9099             }
9100             
9101         }
9102         
9103         this.fireEvent('valid', this);
9104     },
9105     
9106      /**
9107      * Mark this field as valid
9108      */
9109     markValid : function()
9110     {
9111         if(!this.el  || this.preventMark){ // not rendered
9112             return;
9113         }
9114         
9115         this.el.removeClass([this.invalidClass, this.validClass]);
9116         
9117         var feedback = this.el.select('.form-control-feedback', true).first();
9118             
9119         if(feedback){
9120             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9121         }
9122
9123         if(this.disabled || this.allowBlank){
9124             return;
9125         }
9126         
9127         var label = this.el.select('label', true).first();
9128         var icon = this.el.select('i.fa-star', true).first();
9129         
9130         if(label && icon){
9131             icon.remove();
9132         }
9133         
9134         this.el.addClass(this.validClass);
9135         
9136         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9137             
9138             var feedback = this.el.select('.form-control-feedback', true).first();
9139             
9140             if(feedback){
9141                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9142                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9143             }
9144             
9145         }
9146         
9147         this.fireEvent('valid', this);
9148     },
9149     
9150      /**
9151      * Mark this field as invalid
9152      * @param {String} msg The validation message
9153      */
9154     markInvalid : function(msg)
9155     {
9156         if(!this.el  || this.preventMark){ // not rendered
9157             return;
9158         }
9159         
9160         this.el.removeClass([this.invalidClass, this.validClass]);
9161         
9162         var feedback = this.el.select('.form-control-feedback', true).first();
9163             
9164         if(feedback){
9165             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9166         }
9167
9168         if(this.disabled || this.allowBlank){
9169             return;
9170         }
9171         
9172         var label = this.el.select('label', true).first();
9173         var icon = this.el.select('i.fa-star', true).first();
9174         
9175         if(!this.getValue().length && label && !icon){
9176             this.el.createChild({
9177                 tag : 'i',
9178                 cls : 'text-danger fa fa-lg fa-star',
9179                 tooltip : 'This field is required',
9180                 style : 'margin-right:5px;'
9181             }, label, true);
9182         }
9183
9184         this.el.addClass(this.invalidClass);
9185         
9186         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9187             
9188             var feedback = this.el.select('.form-control-feedback', true).first();
9189             
9190             if(feedback){
9191                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9192                 
9193                 if(this.getValue().length || this.forceFeedback){
9194                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9195                 }
9196                 
9197             }
9198             
9199         }
9200         
9201         this.fireEvent('invalid', this, msg);
9202     }
9203 });
9204
9205  
9206 /*
9207  * - LGPL
9208  *
9209  * trigger field - base class for combo..
9210  * 
9211  */
9212  
9213 /**
9214  * @class Roo.bootstrap.TriggerField
9215  * @extends Roo.bootstrap.Input
9216  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9217  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9218  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9219  * for which you can provide a custom implementation.  For example:
9220  * <pre><code>
9221 var trigger = new Roo.bootstrap.TriggerField();
9222 trigger.onTriggerClick = myTriggerFn;
9223 trigger.applyTo('my-field');
9224 </code></pre>
9225  *
9226  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9227  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9228  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9229  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9230  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9231
9232  * @constructor
9233  * Create a new TriggerField.
9234  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9235  * to the base TextField)
9236  */
9237 Roo.bootstrap.TriggerField = function(config){
9238     this.mimicing = false;
9239     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9240 };
9241
9242 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9243     /**
9244      * @cfg {String} triggerClass A CSS class to apply to the trigger
9245      */
9246      /**
9247      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9248      */
9249     hideTrigger:false,
9250
9251     /**
9252      * @cfg {Boolean} removable (true|false) special filter default false
9253      */
9254     removable : false,
9255     
9256     /** @cfg {Boolean} grow @hide */
9257     /** @cfg {Number} growMin @hide */
9258     /** @cfg {Number} growMax @hide */
9259
9260     /**
9261      * @hide 
9262      * @method
9263      */
9264     autoSize: Roo.emptyFn,
9265     // private
9266     monitorTab : true,
9267     // private
9268     deferHeight : true,
9269
9270     
9271     actionMode : 'wrap',
9272     
9273     caret : false,
9274     
9275     
9276     getAutoCreate : function(){
9277        
9278         var align = this.labelAlign || this.parentLabelAlign();
9279         
9280         var id = Roo.id();
9281         
9282         var cfg = {
9283             cls: 'form-group' //input-group
9284         };
9285         
9286         
9287         var input =  {
9288             tag: 'input',
9289             id : id,
9290             type : this.inputType,
9291             cls : 'form-control',
9292             autocomplete: 'new-password',
9293             placeholder : this.placeholder || '' 
9294             
9295         };
9296         if (this.name) {
9297             input.name = this.name;
9298         }
9299         if (this.size) {
9300             input.cls += ' input-' + this.size;
9301         }
9302         
9303         if (this.disabled) {
9304             input.disabled=true;
9305         }
9306         
9307         var inputblock = input;
9308         
9309         if(this.hasFeedback && !this.allowBlank){
9310             
9311             var feedback = {
9312                 tag: 'span',
9313                 cls: 'glyphicon form-control-feedback'
9314             };
9315             
9316             if(this.removable && !this.editable && !this.tickable){
9317                 inputblock = {
9318                     cls : 'has-feedback',
9319                     cn :  [
9320                         inputblock,
9321                         {
9322                             tag: 'button',
9323                             html : 'x',
9324                             cls : 'roo-combo-removable-btn close'
9325                         },
9326                         feedback
9327                     ] 
9328                 };
9329             } else {
9330                 inputblock = {
9331                     cls : 'has-feedback',
9332                     cn :  [
9333                         inputblock,
9334                         feedback
9335                     ] 
9336                 };
9337             }
9338
9339         } else {
9340             if(this.removable && !this.editable && !this.tickable){
9341                 inputblock = {
9342                     cls : 'roo-removable',
9343                     cn :  [
9344                         inputblock,
9345                         {
9346                             tag: 'button',
9347                             html : 'x',
9348                             cls : 'roo-combo-removable-btn close'
9349                         }
9350                     ] 
9351                 };
9352             }
9353         }
9354         
9355         if (this.before || this.after) {
9356             
9357             inputblock = {
9358                 cls : 'input-group',
9359                 cn :  [] 
9360             };
9361             if (this.before) {
9362                 inputblock.cn.push({
9363                     tag :'span',
9364                     cls : 'input-group-addon',
9365                     html : this.before
9366                 });
9367             }
9368             
9369             inputblock.cn.push(input);
9370             
9371             if(this.hasFeedback && !this.allowBlank){
9372                 inputblock.cls += ' has-feedback';
9373                 inputblock.cn.push(feedback);
9374             }
9375             
9376             if (this.after) {
9377                 inputblock.cn.push({
9378                     tag :'span',
9379                     cls : 'input-group-addon',
9380                     html : this.after
9381                 });
9382             }
9383             
9384         };
9385         
9386         var box = {
9387             tag: 'div',
9388             cn: [
9389                 {
9390                     tag: 'input',
9391                     type : 'hidden',
9392                     cls: 'form-hidden-field'
9393                 },
9394                 inputblock
9395             ]
9396             
9397         };
9398         
9399         if(this.multiple){
9400             box = {
9401                 tag: 'div',
9402                 cn: [
9403                     {
9404                         tag: 'input',
9405                         type : 'hidden',
9406                         cls: 'form-hidden-field'
9407                     },
9408                     {
9409                         tag: 'ul',
9410                         cls: 'roo-select2-choices',
9411                         cn:[
9412                             {
9413                                 tag: 'li',
9414                                 cls: 'roo-select2-search-field',
9415                                 cn: [
9416
9417                                     inputblock
9418                                 ]
9419                             }
9420                         ]
9421                     }
9422                 ]
9423             }
9424         };
9425         
9426         var combobox = {
9427             cls: 'roo-select2-container input-group',
9428             cn: [
9429                 box
9430 //                {
9431 //                    tag: 'ul',
9432 //                    cls: 'typeahead typeahead-long dropdown-menu',
9433 //                    style: 'display:none'
9434 //                }
9435             ]
9436         };
9437         
9438         if(!this.multiple && this.showToggleBtn){
9439             
9440             var caret = {
9441                         tag: 'span',
9442                         cls: 'caret'
9443              };
9444             if (this.caret != false) {
9445                 caret = {
9446                      tag: 'i',
9447                      cls: 'fa fa-' + this.caret
9448                 };
9449                 
9450             }
9451             
9452             combobox.cn.push({
9453                 tag :'span',
9454                 cls : 'input-group-addon btn dropdown-toggle',
9455                 cn : [
9456                     caret,
9457                     {
9458                         tag: 'span',
9459                         cls: 'combobox-clear',
9460                         cn  : [
9461                             {
9462                                 tag : 'i',
9463                                 cls: 'icon-remove'
9464                             }
9465                         ]
9466                     }
9467                 ]
9468
9469             })
9470         }
9471         
9472         if(this.multiple){
9473             combobox.cls += ' roo-select2-container-multi';
9474         }
9475         
9476         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9477             
9478 //                Roo.log("left and has label");
9479                 cfg.cn = [
9480                     
9481                     {
9482                         tag: 'label',
9483                         'for' :  id,
9484                         cls : 'control-label col-sm-' + this.labelWidth,
9485                         html : this.fieldLabel
9486                         
9487                     },
9488                     {
9489                         cls : "col-sm-" + (12 - this.labelWidth), 
9490                         cn: [
9491                             combobox
9492                         ]
9493                     }
9494                     
9495                 ];
9496         } else if ( this.fieldLabel.length) {
9497 //                Roo.log(" label");
9498                  cfg.cn = [
9499                    
9500                     {
9501                         tag: 'label',
9502                         //cls : 'input-group-addon',
9503                         html : this.fieldLabel
9504                         
9505                     },
9506                     
9507                     combobox
9508                     
9509                 ];
9510
9511         } else {
9512             
9513 //                Roo.log(" no label && no align");
9514                 cfg = combobox
9515                      
9516                 
9517         }
9518          
9519         var settings=this;
9520         ['xs','sm','md','lg'].map(function(size){
9521             if (settings[size]) {
9522                 cfg.cls += ' col-' + size + '-' + settings[size];
9523             }
9524         });
9525         
9526         return cfg;
9527         
9528     },
9529     
9530     
9531     
9532     // private
9533     onResize : function(w, h){
9534 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9535 //        if(typeof w == 'number'){
9536 //            var x = w - this.trigger.getWidth();
9537 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9538 //            this.trigger.setStyle('left', x+'px');
9539 //        }
9540     },
9541
9542     // private
9543     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9544
9545     // private
9546     getResizeEl : function(){
9547         return this.inputEl();
9548     },
9549
9550     // private
9551     getPositionEl : function(){
9552         return this.inputEl();
9553     },
9554
9555     // private
9556     alignErrorIcon : function(){
9557         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9558     },
9559
9560     // private
9561     initEvents : function(){
9562         
9563         this.createList();
9564         
9565         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9566         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9567         if(!this.multiple && this.showToggleBtn){
9568             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9569             if(this.hideTrigger){
9570                 this.trigger.setDisplayed(false);
9571             }
9572             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9573         }
9574         
9575         if(this.multiple){
9576             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9577         }
9578         
9579         if(this.removable && !this.editable && !this.tickable){
9580             var close = this.closeTriggerEl();
9581             
9582             if(close){
9583                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9584                 close.on('click', this.removeBtnClick, this, close);
9585             }
9586         }
9587         
9588         //this.trigger.addClassOnOver('x-form-trigger-over');
9589         //this.trigger.addClassOnClick('x-form-trigger-click');
9590         
9591         //if(!this.width){
9592         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9593         //}
9594     },
9595     
9596     closeTriggerEl : function()
9597     {
9598         var close = this.el.select('.roo-combo-removable-btn', true).first();
9599         return close ? close : false;
9600     },
9601     
9602     removeBtnClick : function(e, h, el)
9603     {
9604         e.preventDefault();
9605         
9606         if(this.fireEvent("remove", this) !== false){
9607             this.reset();
9608             this.fireEvent("afterremove", this)
9609         }
9610     },
9611     
9612     createList : function()
9613     {
9614         this.list = Roo.get(document.body).createChild({
9615             tag: 'ul',
9616             cls: 'typeahead typeahead-long dropdown-menu',
9617             style: 'display:none'
9618         });
9619         
9620         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9621         
9622     },
9623
9624     // private
9625     initTrigger : function(){
9626        
9627     },
9628
9629     // private
9630     onDestroy : function(){
9631         if(this.trigger){
9632             this.trigger.removeAllListeners();
9633           //  this.trigger.remove();
9634         }
9635         //if(this.wrap){
9636         //    this.wrap.remove();
9637         //}
9638         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9639     },
9640
9641     // private
9642     onFocus : function(){
9643         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9644         /*
9645         if(!this.mimicing){
9646             this.wrap.addClass('x-trigger-wrap-focus');
9647             this.mimicing = true;
9648             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9649             if(this.monitorTab){
9650                 this.el.on("keydown", this.checkTab, this);
9651             }
9652         }
9653         */
9654     },
9655
9656     // private
9657     checkTab : function(e){
9658         if(e.getKey() == e.TAB){
9659             this.triggerBlur();
9660         }
9661     },
9662
9663     // private
9664     onBlur : function(){
9665         // do nothing
9666     },
9667
9668     // private
9669     mimicBlur : function(e, t){
9670         /*
9671         if(!this.wrap.contains(t) && this.validateBlur()){
9672             this.triggerBlur();
9673         }
9674         */
9675     },
9676
9677     // private
9678     triggerBlur : function(){
9679         this.mimicing = false;
9680         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9681         if(this.monitorTab){
9682             this.el.un("keydown", this.checkTab, this);
9683         }
9684         //this.wrap.removeClass('x-trigger-wrap-focus');
9685         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9686     },
9687
9688     // private
9689     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9690     validateBlur : function(e, t){
9691         return true;
9692     },
9693
9694     // private
9695     onDisable : function(){
9696         this.inputEl().dom.disabled = true;
9697         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9698         //if(this.wrap){
9699         //    this.wrap.addClass('x-item-disabled');
9700         //}
9701     },
9702
9703     // private
9704     onEnable : function(){
9705         this.inputEl().dom.disabled = false;
9706         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9707         //if(this.wrap){
9708         //    this.el.removeClass('x-item-disabled');
9709         //}
9710     },
9711
9712     // private
9713     onShow : function(){
9714         var ae = this.getActionEl();
9715         
9716         if(ae){
9717             ae.dom.style.display = '';
9718             ae.dom.style.visibility = 'visible';
9719         }
9720     },
9721
9722     // private
9723     
9724     onHide : function(){
9725         var ae = this.getActionEl();
9726         ae.dom.style.display = 'none';
9727     },
9728
9729     /**
9730      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9731      * by an implementing function.
9732      * @method
9733      * @param {EventObject} e
9734      */
9735     onTriggerClick : Roo.emptyFn
9736 });
9737  /*
9738  * Based on:
9739  * Ext JS Library 1.1.1
9740  * Copyright(c) 2006-2007, Ext JS, LLC.
9741  *
9742  * Originally Released Under LGPL - original licence link has changed is not relivant.
9743  *
9744  * Fork - LGPL
9745  * <script type="text/javascript">
9746  */
9747
9748
9749 /**
9750  * @class Roo.data.SortTypes
9751  * @singleton
9752  * Defines the default sorting (casting?) comparison functions used when sorting data.
9753  */
9754 Roo.data.SortTypes = {
9755     /**
9756      * Default sort that does nothing
9757      * @param {Mixed} s The value being converted
9758      * @return {Mixed} The comparison value
9759      */
9760     none : function(s){
9761         return s;
9762     },
9763     
9764     /**
9765      * The regular expression used to strip tags
9766      * @type {RegExp}
9767      * @property
9768      */
9769     stripTagsRE : /<\/?[^>]+>/gi,
9770     
9771     /**
9772      * Strips all HTML tags to sort on text only
9773      * @param {Mixed} s The value being converted
9774      * @return {String} The comparison value
9775      */
9776     asText : function(s){
9777         return String(s).replace(this.stripTagsRE, "");
9778     },
9779     
9780     /**
9781      * Strips all HTML tags to sort on text only - Case insensitive
9782      * @param {Mixed} s The value being converted
9783      * @return {String} The comparison value
9784      */
9785     asUCText : function(s){
9786         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9787     },
9788     
9789     /**
9790      * Case insensitive string
9791      * @param {Mixed} s The value being converted
9792      * @return {String} The comparison value
9793      */
9794     asUCString : function(s) {
9795         return String(s).toUpperCase();
9796     },
9797     
9798     /**
9799      * Date sorting
9800      * @param {Mixed} s The value being converted
9801      * @return {Number} The comparison value
9802      */
9803     asDate : function(s) {
9804         if(!s){
9805             return 0;
9806         }
9807         if(s instanceof Date){
9808             return s.getTime();
9809         }
9810         return Date.parse(String(s));
9811     },
9812     
9813     /**
9814      * Float sorting
9815      * @param {Mixed} s The value being converted
9816      * @return {Float} The comparison value
9817      */
9818     asFloat : function(s) {
9819         var val = parseFloat(String(s).replace(/,/g, ""));
9820         if(isNaN(val)) {
9821             val = 0;
9822         }
9823         return val;
9824     },
9825     
9826     /**
9827      * Integer sorting
9828      * @param {Mixed} s The value being converted
9829      * @return {Number} The comparison value
9830      */
9831     asInt : function(s) {
9832         var val = parseInt(String(s).replace(/,/g, ""));
9833         if(isNaN(val)) {
9834             val = 0;
9835         }
9836         return val;
9837     }
9838 };/*
9839  * Based on:
9840  * Ext JS Library 1.1.1
9841  * Copyright(c) 2006-2007, Ext JS, LLC.
9842  *
9843  * Originally Released Under LGPL - original licence link has changed is not relivant.
9844  *
9845  * Fork - LGPL
9846  * <script type="text/javascript">
9847  */
9848
9849 /**
9850 * @class Roo.data.Record
9851  * Instances of this class encapsulate both record <em>definition</em> information, and record
9852  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9853  * to access Records cached in an {@link Roo.data.Store} object.<br>
9854  * <p>
9855  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9856  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9857  * objects.<br>
9858  * <p>
9859  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9860  * @constructor
9861  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9862  * {@link #create}. The parameters are the same.
9863  * @param {Array} data An associative Array of data values keyed by the field name.
9864  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9865  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9866  * not specified an integer id is generated.
9867  */
9868 Roo.data.Record = function(data, id){
9869     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9870     this.data = data;
9871 };
9872
9873 /**
9874  * Generate a constructor for a specific record layout.
9875  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9876  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9877  * Each field definition object may contain the following properties: <ul>
9878  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
9879  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9880  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9881  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9882  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9883  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9884  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9885  * this may be omitted.</p></li>
9886  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9887  * <ul><li>auto (Default, implies no conversion)</li>
9888  * <li>string</li>
9889  * <li>int</li>
9890  * <li>float</li>
9891  * <li>boolean</li>
9892  * <li>date</li></ul></p></li>
9893  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9894  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9895  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9896  * by the Reader into an object that will be stored in the Record. It is passed the
9897  * following parameters:<ul>
9898  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9899  * </ul></p></li>
9900  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9901  * </ul>
9902  * <br>usage:<br><pre><code>
9903 var TopicRecord = Roo.data.Record.create(
9904     {name: 'title', mapping: 'topic_title'},
9905     {name: 'author', mapping: 'username'},
9906     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9907     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9908     {name: 'lastPoster', mapping: 'user2'},
9909     {name: 'excerpt', mapping: 'post_text'}
9910 );
9911
9912 var myNewRecord = new TopicRecord({
9913     title: 'Do my job please',
9914     author: 'noobie',
9915     totalPosts: 1,
9916     lastPost: new Date(),
9917     lastPoster: 'Animal',
9918     excerpt: 'No way dude!'
9919 });
9920 myStore.add(myNewRecord);
9921 </code></pre>
9922  * @method create
9923  * @static
9924  */
9925 Roo.data.Record.create = function(o){
9926     var f = function(){
9927         f.superclass.constructor.apply(this, arguments);
9928     };
9929     Roo.extend(f, Roo.data.Record);
9930     var p = f.prototype;
9931     p.fields = new Roo.util.MixedCollection(false, function(field){
9932         return field.name;
9933     });
9934     for(var i = 0, len = o.length; i < len; i++){
9935         p.fields.add(new Roo.data.Field(o[i]));
9936     }
9937     f.getField = function(name){
9938         return p.fields.get(name);  
9939     };
9940     return f;
9941 };
9942
9943 Roo.data.Record.AUTO_ID = 1000;
9944 Roo.data.Record.EDIT = 'edit';
9945 Roo.data.Record.REJECT = 'reject';
9946 Roo.data.Record.COMMIT = 'commit';
9947
9948 Roo.data.Record.prototype = {
9949     /**
9950      * Readonly flag - true if this record has been modified.
9951      * @type Boolean
9952      */
9953     dirty : false,
9954     editing : false,
9955     error: null,
9956     modified: null,
9957
9958     // private
9959     join : function(store){
9960         this.store = store;
9961     },
9962
9963     /**
9964      * Set the named field to the specified value.
9965      * @param {String} name The name of the field to set.
9966      * @param {Object} value The value to set the field to.
9967      */
9968     set : function(name, value){
9969         if(this.data[name] == value){
9970             return;
9971         }
9972         this.dirty = true;
9973         if(!this.modified){
9974             this.modified = {};
9975         }
9976         if(typeof this.modified[name] == 'undefined'){
9977             this.modified[name] = this.data[name];
9978         }
9979         this.data[name] = value;
9980         if(!this.editing && this.store){
9981             this.store.afterEdit(this);
9982         }       
9983     },
9984
9985     /**
9986      * Get the value of the named field.
9987      * @param {String} name The name of the field to get the value of.
9988      * @return {Object} The value of the field.
9989      */
9990     get : function(name){
9991         return this.data[name]; 
9992     },
9993
9994     // private
9995     beginEdit : function(){
9996         this.editing = true;
9997         this.modified = {}; 
9998     },
9999
10000     // private
10001     cancelEdit : function(){
10002         this.editing = false;
10003         delete this.modified;
10004     },
10005
10006     // private
10007     endEdit : function(){
10008         this.editing = false;
10009         if(this.dirty && this.store){
10010             this.store.afterEdit(this);
10011         }
10012     },
10013
10014     /**
10015      * Usually called by the {@link Roo.data.Store} which owns the Record.
10016      * Rejects all changes made to the Record since either creation, or the last commit operation.
10017      * Modified fields are reverted to their original values.
10018      * <p>
10019      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10020      * of reject operations.
10021      */
10022     reject : function(){
10023         var m = this.modified;
10024         for(var n in m){
10025             if(typeof m[n] != "function"){
10026                 this.data[n] = m[n];
10027             }
10028         }
10029         this.dirty = false;
10030         delete this.modified;
10031         this.editing = false;
10032         if(this.store){
10033             this.store.afterReject(this);
10034         }
10035     },
10036
10037     /**
10038      * Usually called by the {@link Roo.data.Store} which owns the Record.
10039      * Commits all changes made to the Record since either creation, or the last commit operation.
10040      * <p>
10041      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10042      * of commit operations.
10043      */
10044     commit : function(){
10045         this.dirty = false;
10046         delete this.modified;
10047         this.editing = false;
10048         if(this.store){
10049             this.store.afterCommit(this);
10050         }
10051     },
10052
10053     // private
10054     hasError : function(){
10055         return this.error != null;
10056     },
10057
10058     // private
10059     clearError : function(){
10060         this.error = null;
10061     },
10062
10063     /**
10064      * Creates a copy of this record.
10065      * @param {String} id (optional) A new record id if you don't want to use this record's id
10066      * @return {Record}
10067      */
10068     copy : function(newId) {
10069         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10070     }
10071 };/*
10072  * Based on:
10073  * Ext JS Library 1.1.1
10074  * Copyright(c) 2006-2007, Ext JS, LLC.
10075  *
10076  * Originally Released Under LGPL - original licence link has changed is not relivant.
10077  *
10078  * Fork - LGPL
10079  * <script type="text/javascript">
10080  */
10081
10082
10083
10084 /**
10085  * @class Roo.data.Store
10086  * @extends Roo.util.Observable
10087  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10088  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10089  * <p>
10090  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10091  * has no knowledge of the format of the data returned by the Proxy.<br>
10092  * <p>
10093  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10094  * instances from the data object. These records are cached and made available through accessor functions.
10095  * @constructor
10096  * Creates a new Store.
10097  * @param {Object} config A config object containing the objects needed for the Store to access data,
10098  * and read the data into Records.
10099  */
10100 Roo.data.Store = function(config){
10101     this.data = new Roo.util.MixedCollection(false);
10102     this.data.getKey = function(o){
10103         return o.id;
10104     };
10105     this.baseParams = {};
10106     // private
10107     this.paramNames = {
10108         "start" : "start",
10109         "limit" : "limit",
10110         "sort" : "sort",
10111         "dir" : "dir",
10112         "multisort" : "_multisort"
10113     };
10114
10115     if(config && config.data){
10116         this.inlineData = config.data;
10117         delete config.data;
10118     }
10119
10120     Roo.apply(this, config);
10121     
10122     if(this.reader){ // reader passed
10123         this.reader = Roo.factory(this.reader, Roo.data);
10124         this.reader.xmodule = this.xmodule || false;
10125         if(!this.recordType){
10126             this.recordType = this.reader.recordType;
10127         }
10128         if(this.reader.onMetaChange){
10129             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10130         }
10131     }
10132
10133     if(this.recordType){
10134         this.fields = this.recordType.prototype.fields;
10135     }
10136     this.modified = [];
10137
10138     this.addEvents({
10139         /**
10140          * @event datachanged
10141          * Fires when the data cache has changed, and a widget which is using this Store
10142          * as a Record cache should refresh its view.
10143          * @param {Store} this
10144          */
10145         datachanged : true,
10146         /**
10147          * @event metachange
10148          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10149          * @param {Store} this
10150          * @param {Object} meta The JSON metadata
10151          */
10152         metachange : true,
10153         /**
10154          * @event add
10155          * Fires when Records have been added to the Store
10156          * @param {Store} this
10157          * @param {Roo.data.Record[]} records The array of Records added
10158          * @param {Number} index The index at which the record(s) were added
10159          */
10160         add : true,
10161         /**
10162          * @event remove
10163          * Fires when a Record has been removed from the Store
10164          * @param {Store} this
10165          * @param {Roo.data.Record} record The Record that was removed
10166          * @param {Number} index The index at which the record was removed
10167          */
10168         remove : true,
10169         /**
10170          * @event update
10171          * Fires when a Record has been updated
10172          * @param {Store} this
10173          * @param {Roo.data.Record} record The Record that was updated
10174          * @param {String} operation The update operation being performed.  Value may be one of:
10175          * <pre><code>
10176  Roo.data.Record.EDIT
10177  Roo.data.Record.REJECT
10178  Roo.data.Record.COMMIT
10179          * </code></pre>
10180          */
10181         update : true,
10182         /**
10183          * @event clear
10184          * Fires when the data cache has been cleared.
10185          * @param {Store} this
10186          */
10187         clear : true,
10188         /**
10189          * @event beforeload
10190          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10191          * the load action will be canceled.
10192          * @param {Store} this
10193          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10194          */
10195         beforeload : true,
10196         /**
10197          * @event beforeloadadd
10198          * Fires after a new set of Records has been loaded.
10199          * @param {Store} this
10200          * @param {Roo.data.Record[]} records The Records that were loaded
10201          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10202          */
10203         beforeloadadd : true,
10204         /**
10205          * @event load
10206          * Fires after a new set of Records has been loaded, before they are added to the store.
10207          * @param {Store} this
10208          * @param {Roo.data.Record[]} records The Records that were loaded
10209          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10210          * @params {Object} return from reader
10211          */
10212         load : true,
10213         /**
10214          * @event loadexception
10215          * Fires if an exception occurs in the Proxy during loading.
10216          * Called with the signature of the Proxy's "loadexception" event.
10217          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10218          * 
10219          * @param {Proxy} 
10220          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10221          * @param {Object} load options 
10222          * @param {Object} jsonData from your request (normally this contains the Exception)
10223          */
10224         loadexception : true
10225     });
10226     
10227     if(this.proxy){
10228         this.proxy = Roo.factory(this.proxy, Roo.data);
10229         this.proxy.xmodule = this.xmodule || false;
10230         this.relayEvents(this.proxy,  ["loadexception"]);
10231     }
10232     this.sortToggle = {};
10233     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10234
10235     Roo.data.Store.superclass.constructor.call(this);
10236
10237     if(this.inlineData){
10238         this.loadData(this.inlineData);
10239         delete this.inlineData;
10240     }
10241 };
10242
10243 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10244      /**
10245     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10246     * without a remote query - used by combo/forms at present.
10247     */
10248     
10249     /**
10250     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10251     */
10252     /**
10253     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10254     */
10255     /**
10256     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10257     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10258     */
10259     /**
10260     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10261     * on any HTTP request
10262     */
10263     /**
10264     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10265     */
10266     /**
10267     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10268     */
10269     multiSort: false,
10270     /**
10271     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10272     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10273     */
10274     remoteSort : false,
10275
10276     /**
10277     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10278      * loaded or when a record is removed. (defaults to false).
10279     */
10280     pruneModifiedRecords : false,
10281
10282     // private
10283     lastOptions : null,
10284
10285     /**
10286      * Add Records to the Store and fires the add event.
10287      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10288      */
10289     add : function(records){
10290         records = [].concat(records);
10291         for(var i = 0, len = records.length; i < len; i++){
10292             records[i].join(this);
10293         }
10294         var index = this.data.length;
10295         this.data.addAll(records);
10296         this.fireEvent("add", this, records, index);
10297     },
10298
10299     /**
10300      * Remove a Record from the Store and fires the remove event.
10301      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10302      */
10303     remove : function(record){
10304         var index = this.data.indexOf(record);
10305         this.data.removeAt(index);
10306         if(this.pruneModifiedRecords){
10307             this.modified.remove(record);
10308         }
10309         this.fireEvent("remove", this, record, index);
10310     },
10311
10312     /**
10313      * Remove all Records from the Store and fires the clear event.
10314      */
10315     removeAll : function(){
10316         this.data.clear();
10317         if(this.pruneModifiedRecords){
10318             this.modified = [];
10319         }
10320         this.fireEvent("clear", this);
10321     },
10322
10323     /**
10324      * Inserts Records to the Store at the given index and fires the add event.
10325      * @param {Number} index The start index at which to insert the passed Records.
10326      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10327      */
10328     insert : function(index, records){
10329         records = [].concat(records);
10330         for(var i = 0, len = records.length; i < len; i++){
10331             this.data.insert(index, records[i]);
10332             records[i].join(this);
10333         }
10334         this.fireEvent("add", this, records, index);
10335     },
10336
10337     /**
10338      * Get the index within the cache of the passed Record.
10339      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10340      * @return {Number} The index of the passed Record. Returns -1 if not found.
10341      */
10342     indexOf : function(record){
10343         return this.data.indexOf(record);
10344     },
10345
10346     /**
10347      * Get the index within the cache of the Record with the passed id.
10348      * @param {String} id The id of the Record to find.
10349      * @return {Number} The index of the Record. Returns -1 if not found.
10350      */
10351     indexOfId : function(id){
10352         return this.data.indexOfKey(id);
10353     },
10354
10355     /**
10356      * Get the Record with the specified id.
10357      * @param {String} id The id of the Record to find.
10358      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10359      */
10360     getById : function(id){
10361         return this.data.key(id);
10362     },
10363
10364     /**
10365      * Get the Record at the specified index.
10366      * @param {Number} index The index of the Record to find.
10367      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10368      */
10369     getAt : function(index){
10370         return this.data.itemAt(index);
10371     },
10372
10373     /**
10374      * Returns a range of Records between specified indices.
10375      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10376      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10377      * @return {Roo.data.Record[]} An array of Records
10378      */
10379     getRange : function(start, end){
10380         return this.data.getRange(start, end);
10381     },
10382
10383     // private
10384     storeOptions : function(o){
10385         o = Roo.apply({}, o);
10386         delete o.callback;
10387         delete o.scope;
10388         this.lastOptions = o;
10389     },
10390
10391     /**
10392      * Loads the Record cache from the configured Proxy using the configured Reader.
10393      * <p>
10394      * If using remote paging, then the first load call must specify the <em>start</em>
10395      * and <em>limit</em> properties in the options.params property to establish the initial
10396      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10397      * <p>
10398      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10399      * and this call will return before the new data has been loaded. Perform any post-processing
10400      * in a callback function, or in a "load" event handler.</strong>
10401      * <p>
10402      * @param {Object} options An object containing properties which control loading options:<ul>
10403      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10404      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10405      * passed the following arguments:<ul>
10406      * <li>r : Roo.data.Record[]</li>
10407      * <li>options: Options object from the load call</li>
10408      * <li>success: Boolean success indicator</li></ul></li>
10409      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10410      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10411      * </ul>
10412      */
10413     load : function(options){
10414         options = options || {};
10415         if(this.fireEvent("beforeload", this, options) !== false){
10416             this.storeOptions(options);
10417             var p = Roo.apply(options.params || {}, this.baseParams);
10418             // if meta was not loaded from remote source.. try requesting it.
10419             if (!this.reader.metaFromRemote) {
10420                 p._requestMeta = 1;
10421             }
10422             if(this.sortInfo && this.remoteSort){
10423                 var pn = this.paramNames;
10424                 p[pn["sort"]] = this.sortInfo.field;
10425                 p[pn["dir"]] = this.sortInfo.direction;
10426             }
10427             if (this.multiSort) {
10428                 var pn = this.paramNames;
10429                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10430             }
10431             
10432             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10433         }
10434     },
10435
10436     /**
10437      * Reloads the Record cache from the configured Proxy using the configured Reader and
10438      * the options from the last load operation performed.
10439      * @param {Object} options (optional) An object containing properties which may override the options
10440      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10441      * the most recently used options are reused).
10442      */
10443     reload : function(options){
10444         this.load(Roo.applyIf(options||{}, this.lastOptions));
10445     },
10446
10447     // private
10448     // Called as a callback by the Reader during a load operation.
10449     loadRecords : function(o, options, success){
10450         if(!o || success === false){
10451             if(success !== false){
10452                 this.fireEvent("load", this, [], options, o);
10453             }
10454             if(options.callback){
10455                 options.callback.call(options.scope || this, [], options, false);
10456             }
10457             return;
10458         }
10459         // if data returned failure - throw an exception.
10460         if (o.success === false) {
10461             // show a message if no listener is registered.
10462             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10463                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10464             }
10465             // loadmask wil be hooked into this..
10466             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10467             return;
10468         }
10469         var r = o.records, t = o.totalRecords || r.length;
10470         
10471         this.fireEvent("beforeloadadd", this, r, options, o);
10472         
10473         if(!options || options.add !== true){
10474             if(this.pruneModifiedRecords){
10475                 this.modified = [];
10476             }
10477             for(var i = 0, len = r.length; i < len; i++){
10478                 r[i].join(this);
10479             }
10480             if(this.snapshot){
10481                 this.data = this.snapshot;
10482                 delete this.snapshot;
10483             }
10484             this.data.clear();
10485             this.data.addAll(r);
10486             this.totalLength = t;
10487             this.applySort();
10488             this.fireEvent("datachanged", this);
10489         }else{
10490             this.totalLength = Math.max(t, this.data.length+r.length);
10491             this.add(r);
10492         }
10493         this.fireEvent("load", this, r, options, o);
10494         if(options.callback){
10495             options.callback.call(options.scope || this, r, options, true);
10496         }
10497     },
10498
10499
10500     /**
10501      * Loads data from a passed data block. A Reader which understands the format of the data
10502      * must have been configured in the constructor.
10503      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10504      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10505      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10506      */
10507     loadData : function(o, append){
10508         var r = this.reader.readRecords(o);
10509         this.loadRecords(r, {add: append}, true);
10510     },
10511
10512     /**
10513      * Gets the number of cached records.
10514      * <p>
10515      * <em>If using paging, this may not be the total size of the dataset. If the data object
10516      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10517      * the data set size</em>
10518      */
10519     getCount : function(){
10520         return this.data.length || 0;
10521     },
10522
10523     /**
10524      * Gets the total number of records in the dataset as returned by the server.
10525      * <p>
10526      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10527      * the dataset size</em>
10528      */
10529     getTotalCount : function(){
10530         return this.totalLength || 0;
10531     },
10532
10533     /**
10534      * Returns the sort state of the Store as an object with two properties:
10535      * <pre><code>
10536  field {String} The name of the field by which the Records are sorted
10537  direction {String} The sort order, "ASC" or "DESC"
10538      * </code></pre>
10539      */
10540     getSortState : function(){
10541         return this.sortInfo;
10542     },
10543
10544     // private
10545     applySort : function(){
10546         if(this.sortInfo && !this.remoteSort){
10547             var s = this.sortInfo, f = s.field;
10548             var st = this.fields.get(f).sortType;
10549             var fn = function(r1, r2){
10550                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10551                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10552             };
10553             this.data.sort(s.direction, fn);
10554             if(this.snapshot && this.snapshot != this.data){
10555                 this.snapshot.sort(s.direction, fn);
10556             }
10557         }
10558     },
10559
10560     /**
10561      * Sets the default sort column and order to be used by the next load operation.
10562      * @param {String} fieldName The name of the field to sort by.
10563      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10564      */
10565     setDefaultSort : function(field, dir){
10566         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10567     },
10568
10569     /**
10570      * Sort the Records.
10571      * If remote sorting is used, the sort is performed on the server, and the cache is
10572      * reloaded. If local sorting is used, the cache is sorted internally.
10573      * @param {String} fieldName The name of the field to sort by.
10574      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10575      */
10576     sort : function(fieldName, dir){
10577         var f = this.fields.get(fieldName);
10578         if(!dir){
10579             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10580             
10581             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10582                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10583             }else{
10584                 dir = f.sortDir;
10585             }
10586         }
10587         this.sortToggle[f.name] = dir;
10588         this.sortInfo = {field: f.name, direction: dir};
10589         if(!this.remoteSort){
10590             this.applySort();
10591             this.fireEvent("datachanged", this);
10592         }else{
10593             this.load(this.lastOptions);
10594         }
10595     },
10596
10597     /**
10598      * Calls the specified function for each of the Records in the cache.
10599      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10600      * Returning <em>false</em> aborts and exits the iteration.
10601      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10602      */
10603     each : function(fn, scope){
10604         this.data.each(fn, scope);
10605     },
10606
10607     /**
10608      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10609      * (e.g., during paging).
10610      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10611      */
10612     getModifiedRecords : function(){
10613         return this.modified;
10614     },
10615
10616     // private
10617     createFilterFn : function(property, value, anyMatch){
10618         if(!value.exec){ // not a regex
10619             value = String(value);
10620             if(value.length == 0){
10621                 return false;
10622             }
10623             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10624         }
10625         return function(r){
10626             return value.test(r.data[property]);
10627         };
10628     },
10629
10630     /**
10631      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10632      * @param {String} property A field on your records
10633      * @param {Number} start The record index to start at (defaults to 0)
10634      * @param {Number} end The last record index to include (defaults to length - 1)
10635      * @return {Number} The sum
10636      */
10637     sum : function(property, start, end){
10638         var rs = this.data.items, v = 0;
10639         start = start || 0;
10640         end = (end || end === 0) ? end : rs.length-1;
10641
10642         for(var i = start; i <= end; i++){
10643             v += (rs[i].data[property] || 0);
10644         }
10645         return v;
10646     },
10647
10648     /**
10649      * Filter the records by a specified property.
10650      * @param {String} field A field on your records
10651      * @param {String/RegExp} value Either a string that the field
10652      * should start with or a RegExp to test against the field
10653      * @param {Boolean} anyMatch True to match any part not just the beginning
10654      */
10655     filter : function(property, value, anyMatch){
10656         var fn = this.createFilterFn(property, value, anyMatch);
10657         return fn ? this.filterBy(fn) : this.clearFilter();
10658     },
10659
10660     /**
10661      * Filter by a function. The specified function will be called with each
10662      * record in this data source. If the function returns true the record is included,
10663      * otherwise it is filtered.
10664      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10665      * @param {Object} scope (optional) The scope of the function (defaults to this)
10666      */
10667     filterBy : function(fn, scope){
10668         this.snapshot = this.snapshot || this.data;
10669         this.data = this.queryBy(fn, scope||this);
10670         this.fireEvent("datachanged", this);
10671     },
10672
10673     /**
10674      * Query the records by a specified property.
10675      * @param {String} field A field on your records
10676      * @param {String/RegExp} value Either a string that the field
10677      * should start with or a RegExp to test against the field
10678      * @param {Boolean} anyMatch True to match any part not just the beginning
10679      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10680      */
10681     query : function(property, value, anyMatch){
10682         var fn = this.createFilterFn(property, value, anyMatch);
10683         return fn ? this.queryBy(fn) : this.data.clone();
10684     },
10685
10686     /**
10687      * Query by a function. The specified function will be called with each
10688      * record in this data source. If the function returns true the record is included
10689      * in the results.
10690      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10691      * @param {Object} scope (optional) The scope of the function (defaults to this)
10692       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10693      **/
10694     queryBy : function(fn, scope){
10695         var data = this.snapshot || this.data;
10696         return data.filterBy(fn, scope||this);
10697     },
10698
10699     /**
10700      * Collects unique values for a particular dataIndex from this store.
10701      * @param {String} dataIndex The property to collect
10702      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10703      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10704      * @return {Array} An array of the unique values
10705      **/
10706     collect : function(dataIndex, allowNull, bypassFilter){
10707         var d = (bypassFilter === true && this.snapshot) ?
10708                 this.snapshot.items : this.data.items;
10709         var v, sv, r = [], l = {};
10710         for(var i = 0, len = d.length; i < len; i++){
10711             v = d[i].data[dataIndex];
10712             sv = String(v);
10713             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10714                 l[sv] = true;
10715                 r[r.length] = v;
10716             }
10717         }
10718         return r;
10719     },
10720
10721     /**
10722      * Revert to a view of the Record cache with no filtering applied.
10723      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10724      */
10725     clearFilter : function(suppressEvent){
10726         if(this.snapshot && this.snapshot != this.data){
10727             this.data = this.snapshot;
10728             delete this.snapshot;
10729             if(suppressEvent !== true){
10730                 this.fireEvent("datachanged", this);
10731             }
10732         }
10733     },
10734
10735     // private
10736     afterEdit : function(record){
10737         if(this.modified.indexOf(record) == -1){
10738             this.modified.push(record);
10739         }
10740         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10741     },
10742     
10743     // private
10744     afterReject : function(record){
10745         this.modified.remove(record);
10746         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10747     },
10748
10749     // private
10750     afterCommit : function(record){
10751         this.modified.remove(record);
10752         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10753     },
10754
10755     /**
10756      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10757      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10758      */
10759     commitChanges : function(){
10760         var m = this.modified.slice(0);
10761         this.modified = [];
10762         for(var i = 0, len = m.length; i < len; i++){
10763             m[i].commit();
10764         }
10765     },
10766
10767     /**
10768      * Cancel outstanding changes on all changed records.
10769      */
10770     rejectChanges : function(){
10771         var m = this.modified.slice(0);
10772         this.modified = [];
10773         for(var i = 0, len = m.length; i < len; i++){
10774             m[i].reject();
10775         }
10776     },
10777
10778     onMetaChange : function(meta, rtype, o){
10779         this.recordType = rtype;
10780         this.fields = rtype.prototype.fields;
10781         delete this.snapshot;
10782         this.sortInfo = meta.sortInfo || this.sortInfo;
10783         this.modified = [];
10784         this.fireEvent('metachange', this, this.reader.meta);
10785     },
10786     
10787     moveIndex : function(data, type)
10788     {
10789         var index = this.indexOf(data);
10790         
10791         var newIndex = index + type;
10792         
10793         this.remove(data);
10794         
10795         this.insert(newIndex, data);
10796         
10797     }
10798 });/*
10799  * Based on:
10800  * Ext JS Library 1.1.1
10801  * Copyright(c) 2006-2007, Ext JS, LLC.
10802  *
10803  * Originally Released Under LGPL - original licence link has changed is not relivant.
10804  *
10805  * Fork - LGPL
10806  * <script type="text/javascript">
10807  */
10808
10809 /**
10810  * @class Roo.data.SimpleStore
10811  * @extends Roo.data.Store
10812  * Small helper class to make creating Stores from Array data easier.
10813  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10814  * @cfg {Array} fields An array of field definition objects, or field name strings.
10815  * @cfg {Array} data The multi-dimensional array of data
10816  * @constructor
10817  * @param {Object} config
10818  */
10819 Roo.data.SimpleStore = function(config){
10820     Roo.data.SimpleStore.superclass.constructor.call(this, {
10821         isLocal : true,
10822         reader: new Roo.data.ArrayReader({
10823                 id: config.id
10824             },
10825             Roo.data.Record.create(config.fields)
10826         ),
10827         proxy : new Roo.data.MemoryProxy(config.data)
10828     });
10829     this.load();
10830 };
10831 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10832  * Based on:
10833  * Ext JS Library 1.1.1
10834  * Copyright(c) 2006-2007, Ext JS, LLC.
10835  *
10836  * Originally Released Under LGPL - original licence link has changed is not relivant.
10837  *
10838  * Fork - LGPL
10839  * <script type="text/javascript">
10840  */
10841
10842 /**
10843 /**
10844  * @extends Roo.data.Store
10845  * @class Roo.data.JsonStore
10846  * Small helper class to make creating Stores for JSON data easier. <br/>
10847 <pre><code>
10848 var store = new Roo.data.JsonStore({
10849     url: 'get-images.php',
10850     root: 'images',
10851     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10852 });
10853 </code></pre>
10854  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10855  * JsonReader and HttpProxy (unless inline data is provided).</b>
10856  * @cfg {Array} fields An array of field definition objects, or field name strings.
10857  * @constructor
10858  * @param {Object} config
10859  */
10860 Roo.data.JsonStore = function(c){
10861     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10862         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10863         reader: new Roo.data.JsonReader(c, c.fields)
10864     }));
10865 };
10866 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10867  * Based on:
10868  * Ext JS Library 1.1.1
10869  * Copyright(c) 2006-2007, Ext JS, LLC.
10870  *
10871  * Originally Released Under LGPL - original licence link has changed is not relivant.
10872  *
10873  * Fork - LGPL
10874  * <script type="text/javascript">
10875  */
10876
10877  
10878 Roo.data.Field = function(config){
10879     if(typeof config == "string"){
10880         config = {name: config};
10881     }
10882     Roo.apply(this, config);
10883     
10884     if(!this.type){
10885         this.type = "auto";
10886     }
10887     
10888     var st = Roo.data.SortTypes;
10889     // named sortTypes are supported, here we look them up
10890     if(typeof this.sortType == "string"){
10891         this.sortType = st[this.sortType];
10892     }
10893     
10894     // set default sortType for strings and dates
10895     if(!this.sortType){
10896         switch(this.type){
10897             case "string":
10898                 this.sortType = st.asUCString;
10899                 break;
10900             case "date":
10901                 this.sortType = st.asDate;
10902                 break;
10903             default:
10904                 this.sortType = st.none;
10905         }
10906     }
10907
10908     // define once
10909     var stripRe = /[\$,%]/g;
10910
10911     // prebuilt conversion function for this field, instead of
10912     // switching every time we're reading a value
10913     if(!this.convert){
10914         var cv, dateFormat = this.dateFormat;
10915         switch(this.type){
10916             case "":
10917             case "auto":
10918             case undefined:
10919                 cv = function(v){ return v; };
10920                 break;
10921             case "string":
10922                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10923                 break;
10924             case "int":
10925                 cv = function(v){
10926                     return v !== undefined && v !== null && v !== '' ?
10927                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10928                     };
10929                 break;
10930             case "float":
10931                 cv = function(v){
10932                     return v !== undefined && v !== null && v !== '' ?
10933                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10934                     };
10935                 break;
10936             case "bool":
10937             case "boolean":
10938                 cv = function(v){ return v === true || v === "true" || v == 1; };
10939                 break;
10940             case "date":
10941                 cv = function(v){
10942                     if(!v){
10943                         return '';
10944                     }
10945                     if(v instanceof Date){
10946                         return v;
10947                     }
10948                     if(dateFormat){
10949                         if(dateFormat == "timestamp"){
10950                             return new Date(v*1000);
10951                         }
10952                         return Date.parseDate(v, dateFormat);
10953                     }
10954                     var parsed = Date.parse(v);
10955                     return parsed ? new Date(parsed) : null;
10956                 };
10957              break;
10958             
10959         }
10960         this.convert = cv;
10961     }
10962 };
10963
10964 Roo.data.Field.prototype = {
10965     dateFormat: null,
10966     defaultValue: "",
10967     mapping: null,
10968     sortType : null,
10969     sortDir : "ASC"
10970 };/*
10971  * Based on:
10972  * Ext JS Library 1.1.1
10973  * Copyright(c) 2006-2007, Ext JS, LLC.
10974  *
10975  * Originally Released Under LGPL - original licence link has changed is not relivant.
10976  *
10977  * Fork - LGPL
10978  * <script type="text/javascript">
10979  */
10980  
10981 // Base class for reading structured data from a data source.  This class is intended to be
10982 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10983
10984 /**
10985  * @class Roo.data.DataReader
10986  * Base class for reading structured data from a data source.  This class is intended to be
10987  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10988  */
10989
10990 Roo.data.DataReader = function(meta, recordType){
10991     
10992     this.meta = meta;
10993     
10994     this.recordType = recordType instanceof Array ? 
10995         Roo.data.Record.create(recordType) : recordType;
10996 };
10997
10998 Roo.data.DataReader.prototype = {
10999      /**
11000      * Create an empty record
11001      * @param {Object} data (optional) - overlay some values
11002      * @return {Roo.data.Record} record created.
11003      */
11004     newRow :  function(d) {
11005         var da =  {};
11006         this.recordType.prototype.fields.each(function(c) {
11007             switch( c.type) {
11008                 case 'int' : da[c.name] = 0; break;
11009                 case 'date' : da[c.name] = new Date(); break;
11010                 case 'float' : da[c.name] = 0.0; break;
11011                 case 'boolean' : da[c.name] = false; break;
11012                 default : da[c.name] = ""; break;
11013             }
11014             
11015         });
11016         return new this.recordType(Roo.apply(da, d));
11017     }
11018     
11019 };/*
11020  * Based on:
11021  * Ext JS Library 1.1.1
11022  * Copyright(c) 2006-2007, Ext JS, LLC.
11023  *
11024  * Originally Released Under LGPL - original licence link has changed is not relivant.
11025  *
11026  * Fork - LGPL
11027  * <script type="text/javascript">
11028  */
11029
11030 /**
11031  * @class Roo.data.DataProxy
11032  * @extends Roo.data.Observable
11033  * This class is an abstract base class for implementations which provide retrieval of
11034  * unformatted data objects.<br>
11035  * <p>
11036  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11037  * (of the appropriate type which knows how to parse the data object) to provide a block of
11038  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11039  * <p>
11040  * Custom implementations must implement the load method as described in
11041  * {@link Roo.data.HttpProxy#load}.
11042  */
11043 Roo.data.DataProxy = function(){
11044     this.addEvents({
11045         /**
11046          * @event beforeload
11047          * Fires before a network request is made to retrieve a data object.
11048          * @param {Object} This DataProxy object.
11049          * @param {Object} params The params parameter to the load function.
11050          */
11051         beforeload : true,
11052         /**
11053          * @event load
11054          * Fires before the load method's callback is called.
11055          * @param {Object} This DataProxy object.
11056          * @param {Object} o The data object.
11057          * @param {Object} arg The callback argument object passed to the load function.
11058          */
11059         load : true,
11060         /**
11061          * @event loadexception
11062          * Fires if an Exception occurs during data retrieval.
11063          * @param {Object} This DataProxy object.
11064          * @param {Object} o The data object.
11065          * @param {Object} arg The callback argument object passed to the load function.
11066          * @param {Object} e The Exception.
11067          */
11068         loadexception : true
11069     });
11070     Roo.data.DataProxy.superclass.constructor.call(this);
11071 };
11072
11073 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11074
11075     /**
11076      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11077      */
11078 /*
11079  * Based on:
11080  * Ext JS Library 1.1.1
11081  * Copyright(c) 2006-2007, Ext JS, LLC.
11082  *
11083  * Originally Released Under LGPL - original licence link has changed is not relivant.
11084  *
11085  * Fork - LGPL
11086  * <script type="text/javascript">
11087  */
11088 /**
11089  * @class Roo.data.MemoryProxy
11090  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11091  * to the Reader when its load method is called.
11092  * @constructor
11093  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11094  */
11095 Roo.data.MemoryProxy = function(data){
11096     if (data.data) {
11097         data = data.data;
11098     }
11099     Roo.data.MemoryProxy.superclass.constructor.call(this);
11100     this.data = data;
11101 };
11102
11103 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11104     
11105     /**
11106      * Load data from the requested source (in this case an in-memory
11107      * data object passed to the constructor), read the data object into
11108      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11109      * process that block using the passed callback.
11110      * @param {Object} params This parameter is not used by the MemoryProxy class.
11111      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11112      * object into a block of Roo.data.Records.
11113      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11114      * The function must be passed <ul>
11115      * <li>The Record block object</li>
11116      * <li>The "arg" argument from the load function</li>
11117      * <li>A boolean success indicator</li>
11118      * </ul>
11119      * @param {Object} scope The scope in which to call the callback
11120      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11121      */
11122     load : function(params, reader, callback, scope, arg){
11123         params = params || {};
11124         var result;
11125         try {
11126             result = reader.readRecords(this.data);
11127         }catch(e){
11128             this.fireEvent("loadexception", this, arg, null, e);
11129             callback.call(scope, null, arg, false);
11130             return;
11131         }
11132         callback.call(scope, result, arg, true);
11133     },
11134     
11135     // private
11136     update : function(params, records){
11137         
11138     }
11139 });/*
11140  * Based on:
11141  * Ext JS Library 1.1.1
11142  * Copyright(c) 2006-2007, Ext JS, LLC.
11143  *
11144  * Originally Released Under LGPL - original licence link has changed is not relivant.
11145  *
11146  * Fork - LGPL
11147  * <script type="text/javascript">
11148  */
11149 /**
11150  * @class Roo.data.HttpProxy
11151  * @extends Roo.data.DataProxy
11152  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11153  * configured to reference a certain URL.<br><br>
11154  * <p>
11155  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11156  * from which the running page was served.<br><br>
11157  * <p>
11158  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11159  * <p>
11160  * Be aware that to enable the browser to parse an XML document, the server must set
11161  * the Content-Type header in the HTTP response to "text/xml".
11162  * @constructor
11163  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11164  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11165  * will be used to make the request.
11166  */
11167 Roo.data.HttpProxy = function(conn){
11168     Roo.data.HttpProxy.superclass.constructor.call(this);
11169     // is conn a conn config or a real conn?
11170     this.conn = conn;
11171     this.useAjax = !conn || !conn.events;
11172   
11173 };
11174
11175 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11176     // thse are take from connection...
11177     
11178     /**
11179      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11180      */
11181     /**
11182      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11183      * extra parameters to each request made by this object. (defaults to undefined)
11184      */
11185     /**
11186      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11187      *  to each request made by this object. (defaults to undefined)
11188      */
11189     /**
11190      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11191      */
11192     /**
11193      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11194      */
11195      /**
11196      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11197      * @type Boolean
11198      */
11199   
11200
11201     /**
11202      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11203      * @type Boolean
11204      */
11205     /**
11206      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11207      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11208      * a finer-grained basis than the DataProxy events.
11209      */
11210     getConnection : function(){
11211         return this.useAjax ? Roo.Ajax : this.conn;
11212     },
11213
11214     /**
11215      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11216      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11217      * process that block using the passed callback.
11218      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11219      * for the request to the remote server.
11220      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11221      * object into a block of Roo.data.Records.
11222      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11223      * The function must be passed <ul>
11224      * <li>The Record block object</li>
11225      * <li>The "arg" argument from the load function</li>
11226      * <li>A boolean success indicator</li>
11227      * </ul>
11228      * @param {Object} scope The scope in which to call the callback
11229      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11230      */
11231     load : function(params, reader, callback, scope, arg){
11232         if(this.fireEvent("beforeload", this, params) !== false){
11233             var  o = {
11234                 params : params || {},
11235                 request: {
11236                     callback : callback,
11237                     scope : scope,
11238                     arg : arg
11239                 },
11240                 reader: reader,
11241                 callback : this.loadResponse,
11242                 scope: this
11243             };
11244             if(this.useAjax){
11245                 Roo.applyIf(o, this.conn);
11246                 if(this.activeRequest){
11247                     Roo.Ajax.abort(this.activeRequest);
11248                 }
11249                 this.activeRequest = Roo.Ajax.request(o);
11250             }else{
11251                 this.conn.request(o);
11252             }
11253         }else{
11254             callback.call(scope||this, null, arg, false);
11255         }
11256     },
11257
11258     // private
11259     loadResponse : function(o, success, response){
11260         delete this.activeRequest;
11261         if(!success){
11262             this.fireEvent("loadexception", this, o, response);
11263             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11264             return;
11265         }
11266         var result;
11267         try {
11268             result = o.reader.read(response);
11269         }catch(e){
11270             this.fireEvent("loadexception", this, o, response, e);
11271             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11272             return;
11273         }
11274         
11275         this.fireEvent("load", this, o, o.request.arg);
11276         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11277     },
11278
11279     // private
11280     update : function(dataSet){
11281
11282     },
11283
11284     // private
11285     updateResponse : function(dataSet){
11286
11287     }
11288 });/*
11289  * Based on:
11290  * Ext JS Library 1.1.1
11291  * Copyright(c) 2006-2007, Ext JS, LLC.
11292  *
11293  * Originally Released Under LGPL - original licence link has changed is not relivant.
11294  *
11295  * Fork - LGPL
11296  * <script type="text/javascript">
11297  */
11298
11299 /**
11300  * @class Roo.data.ScriptTagProxy
11301  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11302  * other than the originating domain of the running page.<br><br>
11303  * <p>
11304  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
11305  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11306  * <p>
11307  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11308  * source code that is used as the source inside a &lt;script> tag.<br><br>
11309  * <p>
11310  * In order for the browser to process the returned data, the server must wrap the data object
11311  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11312  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11313  * depending on whether the callback name was passed:
11314  * <p>
11315  * <pre><code>
11316 boolean scriptTag = false;
11317 String cb = request.getParameter("callback");
11318 if (cb != null) {
11319     scriptTag = true;
11320     response.setContentType("text/javascript");
11321 } else {
11322     response.setContentType("application/x-json");
11323 }
11324 Writer out = response.getWriter();
11325 if (scriptTag) {
11326     out.write(cb + "(");
11327 }
11328 out.print(dataBlock.toJsonString());
11329 if (scriptTag) {
11330     out.write(");");
11331 }
11332 </pre></code>
11333  *
11334  * @constructor
11335  * @param {Object} config A configuration object.
11336  */
11337 Roo.data.ScriptTagProxy = function(config){
11338     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11339     Roo.apply(this, config);
11340     this.head = document.getElementsByTagName("head")[0];
11341 };
11342
11343 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11344
11345 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11346     /**
11347      * @cfg {String} url The URL from which to request the data object.
11348      */
11349     /**
11350      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11351      */
11352     timeout : 30000,
11353     /**
11354      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11355      * the server the name of the callback function set up by the load call to process the returned data object.
11356      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11357      * javascript output which calls this named function passing the data object as its only parameter.
11358      */
11359     callbackParam : "callback",
11360     /**
11361      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11362      * name to the request.
11363      */
11364     nocache : true,
11365
11366     /**
11367      * Load data from the configured URL, read the data object into
11368      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11369      * process that block using the passed callback.
11370      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11371      * for the request to the remote server.
11372      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11373      * object into a block of Roo.data.Records.
11374      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11375      * The function must be passed <ul>
11376      * <li>The Record block object</li>
11377      * <li>The "arg" argument from the load function</li>
11378      * <li>A boolean success indicator</li>
11379      * </ul>
11380      * @param {Object} scope The scope in which to call the callback
11381      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11382      */
11383     load : function(params, reader, callback, scope, arg){
11384         if(this.fireEvent("beforeload", this, params) !== false){
11385
11386             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11387
11388             var url = this.url;
11389             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11390             if(this.nocache){
11391                 url += "&_dc=" + (new Date().getTime());
11392             }
11393             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11394             var trans = {
11395                 id : transId,
11396                 cb : "stcCallback"+transId,
11397                 scriptId : "stcScript"+transId,
11398                 params : params,
11399                 arg : arg,
11400                 url : url,
11401                 callback : callback,
11402                 scope : scope,
11403                 reader : reader
11404             };
11405             var conn = this;
11406
11407             window[trans.cb] = function(o){
11408                 conn.handleResponse(o, trans);
11409             };
11410
11411             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11412
11413             if(this.autoAbort !== false){
11414                 this.abort();
11415             }
11416
11417             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11418
11419             var script = document.createElement("script");
11420             script.setAttribute("src", url);
11421             script.setAttribute("type", "text/javascript");
11422             script.setAttribute("id", trans.scriptId);
11423             this.head.appendChild(script);
11424
11425             this.trans = trans;
11426         }else{
11427             callback.call(scope||this, null, arg, false);
11428         }
11429     },
11430
11431     // private
11432     isLoading : function(){
11433         return this.trans ? true : false;
11434     },
11435
11436     /**
11437      * Abort the current server request.
11438      */
11439     abort : function(){
11440         if(this.isLoading()){
11441             this.destroyTrans(this.trans);
11442         }
11443     },
11444
11445     // private
11446     destroyTrans : function(trans, isLoaded){
11447         this.head.removeChild(document.getElementById(trans.scriptId));
11448         clearTimeout(trans.timeoutId);
11449         if(isLoaded){
11450             window[trans.cb] = undefined;
11451             try{
11452                 delete window[trans.cb];
11453             }catch(e){}
11454         }else{
11455             // if hasn't been loaded, wait for load to remove it to prevent script error
11456             window[trans.cb] = function(){
11457                 window[trans.cb] = undefined;
11458                 try{
11459                     delete window[trans.cb];
11460                 }catch(e){}
11461             };
11462         }
11463     },
11464
11465     // private
11466     handleResponse : function(o, trans){
11467         this.trans = false;
11468         this.destroyTrans(trans, true);
11469         var result;
11470         try {
11471             result = trans.reader.readRecords(o);
11472         }catch(e){
11473             this.fireEvent("loadexception", this, o, trans.arg, e);
11474             trans.callback.call(trans.scope||window, null, trans.arg, false);
11475             return;
11476         }
11477         this.fireEvent("load", this, o, trans.arg);
11478         trans.callback.call(trans.scope||window, result, trans.arg, true);
11479     },
11480
11481     // private
11482     handleFailure : function(trans){
11483         this.trans = false;
11484         this.destroyTrans(trans, false);
11485         this.fireEvent("loadexception", this, null, trans.arg);
11486         trans.callback.call(trans.scope||window, null, trans.arg, false);
11487     }
11488 });/*
11489  * Based on:
11490  * Ext JS Library 1.1.1
11491  * Copyright(c) 2006-2007, Ext JS, LLC.
11492  *
11493  * Originally Released Under LGPL - original licence link has changed is not relivant.
11494  *
11495  * Fork - LGPL
11496  * <script type="text/javascript">
11497  */
11498
11499 /**
11500  * @class Roo.data.JsonReader
11501  * @extends Roo.data.DataReader
11502  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11503  * based on mappings in a provided Roo.data.Record constructor.
11504  * 
11505  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11506  * in the reply previously. 
11507  * 
11508  * <p>
11509  * Example code:
11510  * <pre><code>
11511 var RecordDef = Roo.data.Record.create([
11512     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11513     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11514 ]);
11515 var myReader = new Roo.data.JsonReader({
11516     totalProperty: "results",    // The property which contains the total dataset size (optional)
11517     root: "rows",                // The property which contains an Array of row objects
11518     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11519 }, RecordDef);
11520 </code></pre>
11521  * <p>
11522  * This would consume a JSON file like this:
11523  * <pre><code>
11524 { 'results': 2, 'rows': [
11525     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11526     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11527 }
11528 </code></pre>
11529  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11530  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11531  * paged from the remote server.
11532  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11533  * @cfg {String} root name of the property which contains the Array of row objects.
11534  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11535  * @cfg {Array} fields Array of field definition objects
11536  * @constructor
11537  * Create a new JsonReader
11538  * @param {Object} meta Metadata configuration options
11539  * @param {Object} recordType Either an Array of field definition objects,
11540  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11541  */
11542 Roo.data.JsonReader = function(meta, recordType){
11543     
11544     meta = meta || {};
11545     // set some defaults:
11546     Roo.applyIf(meta, {
11547         totalProperty: 'total',
11548         successProperty : 'success',
11549         root : 'data',
11550         id : 'id'
11551     });
11552     
11553     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11554 };
11555 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11556     
11557     /**
11558      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11559      * Used by Store query builder to append _requestMeta to params.
11560      * 
11561      */
11562     metaFromRemote : false,
11563     /**
11564      * This method is only used by a DataProxy which has retrieved data from a remote server.
11565      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11566      * @return {Object} data A data block which is used by an Roo.data.Store object as
11567      * a cache of Roo.data.Records.
11568      */
11569     read : function(response){
11570         var json = response.responseText;
11571        
11572         var o = /* eval:var:o */ eval("("+json+")");
11573         if(!o) {
11574             throw {message: "JsonReader.read: Json object not found"};
11575         }
11576         
11577         if(o.metaData){
11578             
11579             delete this.ef;
11580             this.metaFromRemote = true;
11581             this.meta = o.metaData;
11582             this.recordType = Roo.data.Record.create(o.metaData.fields);
11583             this.onMetaChange(this.meta, this.recordType, o);
11584         }
11585         return this.readRecords(o);
11586     },
11587
11588     // private function a store will implement
11589     onMetaChange : function(meta, recordType, o){
11590
11591     },
11592
11593     /**
11594          * @ignore
11595          */
11596     simpleAccess: function(obj, subsc) {
11597         return obj[subsc];
11598     },
11599
11600         /**
11601          * @ignore
11602          */
11603     getJsonAccessor: function(){
11604         var re = /[\[\.]/;
11605         return function(expr) {
11606             try {
11607                 return(re.test(expr))
11608                     ? new Function("obj", "return obj." + expr)
11609                     : function(obj){
11610                         return obj[expr];
11611                     };
11612             } catch(e){}
11613             return Roo.emptyFn;
11614         };
11615     }(),
11616
11617     /**
11618      * Create a data block containing Roo.data.Records from an XML document.
11619      * @param {Object} o An object which contains an Array of row objects in the property specified
11620      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11621      * which contains the total size of the dataset.
11622      * @return {Object} data A data block which is used by an Roo.data.Store object as
11623      * a cache of Roo.data.Records.
11624      */
11625     readRecords : function(o){
11626         /**
11627          * After any data loads, the raw JSON data is available for further custom processing.
11628          * @type Object
11629          */
11630         this.o = o;
11631         var s = this.meta, Record = this.recordType,
11632             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11633
11634 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11635         if (!this.ef) {
11636             if(s.totalProperty) {
11637                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11638                 }
11639                 if(s.successProperty) {
11640                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11641                 }
11642                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11643                 if (s.id) {
11644                         var g = this.getJsonAccessor(s.id);
11645                         this.getId = function(rec) {
11646                                 var r = g(rec);  
11647                                 return (r === undefined || r === "") ? null : r;
11648                         };
11649                 } else {
11650                         this.getId = function(){return null;};
11651                 }
11652             this.ef = [];
11653             for(var jj = 0; jj < fl; jj++){
11654                 f = fi[jj];
11655                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11656                 this.ef[jj] = this.getJsonAccessor(map);
11657             }
11658         }
11659
11660         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11661         if(s.totalProperty){
11662             var vt = parseInt(this.getTotal(o), 10);
11663             if(!isNaN(vt)){
11664                 totalRecords = vt;
11665             }
11666         }
11667         if(s.successProperty){
11668             var vs = this.getSuccess(o);
11669             if(vs === false || vs === 'false'){
11670                 success = false;
11671             }
11672         }
11673         var records = [];
11674         for(var i = 0; i < c; i++){
11675                 var n = root[i];
11676             var values = {};
11677             var id = this.getId(n);
11678             for(var j = 0; j < fl; j++){
11679                 f = fi[j];
11680             var v = this.ef[j](n);
11681             if (!f.convert) {
11682                 Roo.log('missing convert for ' + f.name);
11683                 Roo.log(f);
11684                 continue;
11685             }
11686             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11687             }
11688             var record = new Record(values, id);
11689             record.json = n;
11690             records[i] = record;
11691         }
11692         return {
11693             raw : o,
11694             success : success,
11695             records : records,
11696             totalRecords : totalRecords
11697         };
11698     }
11699 });/*
11700  * Based on:
11701  * Ext JS Library 1.1.1
11702  * Copyright(c) 2006-2007, Ext JS, LLC.
11703  *
11704  * Originally Released Under LGPL - original licence link has changed is not relivant.
11705  *
11706  * Fork - LGPL
11707  * <script type="text/javascript">
11708  */
11709
11710 /**
11711  * @class Roo.data.ArrayReader
11712  * @extends Roo.data.DataReader
11713  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11714  * Each element of that Array represents a row of data fields. The
11715  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11716  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11717  * <p>
11718  * Example code:.
11719  * <pre><code>
11720 var RecordDef = Roo.data.Record.create([
11721     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11722     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11723 ]);
11724 var myReader = new Roo.data.ArrayReader({
11725     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11726 }, RecordDef);
11727 </code></pre>
11728  * <p>
11729  * This would consume an Array like this:
11730  * <pre><code>
11731 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11732   </code></pre>
11733  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11734  * @constructor
11735  * Create a new JsonReader
11736  * @param {Object} meta Metadata configuration options.
11737  * @param {Object} recordType Either an Array of field definition objects
11738  * as specified to {@link Roo.data.Record#create},
11739  * or an {@link Roo.data.Record} object
11740  * created using {@link Roo.data.Record#create}.
11741  */
11742 Roo.data.ArrayReader = function(meta, recordType){
11743     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11744 };
11745
11746 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11747     /**
11748      * Create a data block containing Roo.data.Records from an XML document.
11749      * @param {Object} o An Array of row objects which represents the dataset.
11750      * @return {Object} data A data block which is used by an Roo.data.Store object as
11751      * a cache of Roo.data.Records.
11752      */
11753     readRecords : function(o){
11754         var sid = this.meta ? this.meta.id : null;
11755         var recordType = this.recordType, fields = recordType.prototype.fields;
11756         var records = [];
11757         var root = o;
11758             for(var i = 0; i < root.length; i++){
11759                     var n = root[i];
11760                 var values = {};
11761                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11762                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11763                 var f = fields.items[j];
11764                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11765                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11766                 v = f.convert(v);
11767                 values[f.name] = v;
11768             }
11769                 var record = new recordType(values, id);
11770                 record.json = n;
11771                 records[records.length] = record;
11772             }
11773             return {
11774                 records : records,
11775                 totalRecords : records.length
11776             };
11777     }
11778 });/*
11779  * - LGPL
11780  * * 
11781  */
11782
11783 /**
11784  * @class Roo.bootstrap.ComboBox
11785  * @extends Roo.bootstrap.TriggerField
11786  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11787  * @cfg {Boolean} append (true|false) default false
11788  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11789  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11790  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11791  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11792  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11793  * @cfg {Boolean} animate default true
11794  * @cfg {Boolean} emptyResultText only for touch device
11795  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11796  * @constructor
11797  * Create a new ComboBox.
11798  * @param {Object} config Configuration options
11799  */
11800 Roo.bootstrap.ComboBox = function(config){
11801     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11802     this.addEvents({
11803         /**
11804          * @event expand
11805          * Fires when the dropdown list is expanded
11806              * @param {Roo.bootstrap.ComboBox} combo This combo box
11807              */
11808         'expand' : true,
11809         /**
11810          * @event collapse
11811          * Fires when the dropdown list is collapsed
11812              * @param {Roo.bootstrap.ComboBox} combo This combo box
11813              */
11814         'collapse' : true,
11815         /**
11816          * @event beforeselect
11817          * Fires before a list item is selected. Return false to cancel the selection.
11818              * @param {Roo.bootstrap.ComboBox} combo This combo box
11819              * @param {Roo.data.Record} record The data record returned from the underlying store
11820              * @param {Number} index The index of the selected item in the dropdown list
11821              */
11822         'beforeselect' : true,
11823         /**
11824          * @event select
11825          * Fires when a list item is selected
11826              * @param {Roo.bootstrap.ComboBox} combo This combo box
11827              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11828              * @param {Number} index The index of the selected item in the dropdown list
11829              */
11830         'select' : true,
11831         /**
11832          * @event beforequery
11833          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11834          * The event object passed has these properties:
11835              * @param {Roo.bootstrap.ComboBox} combo This combo box
11836              * @param {String} query The query
11837              * @param {Boolean} forceAll true to force "all" query
11838              * @param {Boolean} cancel true to cancel the query
11839              * @param {Object} e The query event object
11840              */
11841         'beforequery': true,
11842          /**
11843          * @event add
11844          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11845              * @param {Roo.bootstrap.ComboBox} combo This combo box
11846              */
11847         'add' : true,
11848         /**
11849          * @event edit
11850          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11851              * @param {Roo.bootstrap.ComboBox} combo This combo box
11852              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11853              */
11854         'edit' : true,
11855         /**
11856          * @event remove
11857          * Fires when the remove value from the combobox array
11858              * @param {Roo.bootstrap.ComboBox} combo This combo box
11859              */
11860         'remove' : true,
11861         /**
11862          * @event afterremove
11863          * Fires when the remove value from the combobox array
11864              * @param {Roo.bootstrap.ComboBox} combo This combo box
11865              */
11866         'afterremove' : true,
11867         /**
11868          * @event specialfilter
11869          * Fires when specialfilter
11870             * @param {Roo.bootstrap.ComboBox} combo This combo box
11871             */
11872         'specialfilter' : true,
11873         /**
11874          * @event tick
11875          * Fires when tick the element
11876             * @param {Roo.bootstrap.ComboBox} combo This combo box
11877             */
11878         'tick' : true,
11879         /**
11880          * @event touchviewdisplay
11881          * Fires when touch view require special display (default is using displayField)
11882             * @param {Roo.bootstrap.ComboBox} combo This combo box
11883             * @param {Object} cfg set html .
11884             */
11885         'touchviewdisplay' : true
11886         
11887     });
11888     
11889     this.item = [];
11890     this.tickItems = [];
11891     
11892     this.selectedIndex = -1;
11893     if(this.mode == 'local'){
11894         if(config.queryDelay === undefined){
11895             this.queryDelay = 10;
11896         }
11897         if(config.minChars === undefined){
11898             this.minChars = 0;
11899         }
11900     }
11901 };
11902
11903 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11904      
11905     /**
11906      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11907      * rendering into an Roo.Editor, defaults to false)
11908      */
11909     /**
11910      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11911      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11912      */
11913     /**
11914      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11915      */
11916     /**
11917      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11918      * the dropdown list (defaults to undefined, with no header element)
11919      */
11920
11921      /**
11922      * @cfg {String/Roo.Template} tpl The template to use to render the output
11923      */
11924      
11925      /**
11926      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11927      */
11928     listWidth: undefined,
11929     /**
11930      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11931      * mode = 'remote' or 'text' if mode = 'local')
11932      */
11933     displayField: undefined,
11934     
11935     /**
11936      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11937      * mode = 'remote' or 'value' if mode = 'local'). 
11938      * Note: use of a valueField requires the user make a selection
11939      * in order for a value to be mapped.
11940      */
11941     valueField: undefined,
11942     /**
11943      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
11944      */
11945     modalTitle : '',
11946     
11947     /**
11948      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11949      * field's data value (defaults to the underlying DOM element's name)
11950      */
11951     hiddenName: undefined,
11952     /**
11953      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11954      */
11955     listClass: '',
11956     /**
11957      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11958      */
11959     selectedClass: 'active',
11960     
11961     /**
11962      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11963      */
11964     shadow:'sides',
11965     /**
11966      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11967      * anchor positions (defaults to 'tl-bl')
11968      */
11969     listAlign: 'tl-bl?',
11970     /**
11971      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11972      */
11973     maxHeight: 300,
11974     /**
11975      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11976      * query specified by the allQuery config option (defaults to 'query')
11977      */
11978     triggerAction: 'query',
11979     /**
11980      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11981      * (defaults to 4, does not apply if editable = false)
11982      */
11983     minChars : 4,
11984     /**
11985      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11986      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11987      */
11988     typeAhead: false,
11989     /**
11990      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11991      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11992      */
11993     queryDelay: 500,
11994     /**
11995      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11996      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11997      */
11998     pageSize: 0,
11999     /**
12000      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12001      * when editable = true (defaults to false)
12002      */
12003     selectOnFocus:false,
12004     /**
12005      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12006      */
12007     queryParam: 'query',
12008     /**
12009      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12010      * when mode = 'remote' (defaults to 'Loading...')
12011      */
12012     loadingText: 'Loading...',
12013     /**
12014      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12015      */
12016     resizable: false,
12017     /**
12018      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12019      */
12020     handleHeight : 8,
12021     /**
12022      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12023      * traditional select (defaults to true)
12024      */
12025     editable: true,
12026     /**
12027      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12028      */
12029     allQuery: '',
12030     /**
12031      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12032      */
12033     mode: 'remote',
12034     /**
12035      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12036      * listWidth has a higher value)
12037      */
12038     minListWidth : 70,
12039     /**
12040      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12041      * allow the user to set arbitrary text into the field (defaults to false)
12042      */
12043     forceSelection:false,
12044     /**
12045      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12046      * if typeAhead = true (defaults to 250)
12047      */
12048     typeAheadDelay : 250,
12049     /**
12050      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12051      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12052      */
12053     valueNotFoundText : undefined,
12054     /**
12055      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12056      */
12057     blockFocus : false,
12058     
12059     /**
12060      * @cfg {Boolean} disableClear Disable showing of clear button.
12061      */
12062     disableClear : false,
12063     /**
12064      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12065      */
12066     alwaysQuery : false,
12067     
12068     /**
12069      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12070      */
12071     multiple : false,
12072     
12073     /**
12074      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12075      */
12076     invalidClass : "has-warning",
12077     
12078     /**
12079      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12080      */
12081     validClass : "has-success",
12082     
12083     /**
12084      * @cfg {Boolean} specialFilter (true|false) special filter default false
12085      */
12086     specialFilter : false,
12087     
12088     /**
12089      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12090      */
12091     mobileTouchView : true,
12092     
12093     //private
12094     addicon : false,
12095     editicon: false,
12096     
12097     page: 0,
12098     hasQuery: false,
12099     append: false,
12100     loadNext: false,
12101     autoFocus : true,
12102     tickable : false,
12103     btnPosition : 'right',
12104     triggerList : true,
12105     showToggleBtn : true,
12106     animate : true,
12107     emptyResultText: 'Empty',
12108     triggerText : 'Select',
12109     
12110     // element that contains real text value.. (when hidden is used..)
12111     
12112     getAutoCreate : function()
12113     {
12114         var cfg = false;
12115         
12116         /*
12117          * Touch Devices
12118          */
12119         
12120         if(Roo.isTouch && this.mobileTouchView){
12121             cfg = this.getAutoCreateTouchView();
12122             return cfg;;
12123         }
12124         
12125         /*
12126          *  Normal ComboBox
12127          */
12128         if(!this.tickable){
12129             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12130             return cfg;
12131         }
12132         
12133         /*
12134          *  ComboBox with tickable selections
12135          */
12136              
12137         var align = this.labelAlign || this.parentLabelAlign();
12138         
12139         cfg = {
12140             cls : 'form-group roo-combobox-tickable' //input-group
12141         };
12142         
12143         var buttons = {
12144             tag : 'div',
12145             cls : 'tickable-buttons',
12146             cn : [
12147                 {
12148                     tag : 'button',
12149                     type : 'button',
12150                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12151                     html : this.triggerText
12152                 },
12153                 {
12154                     tag : 'button',
12155                     type : 'button',
12156                     name : 'ok',
12157                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12158                     html : 'Done'
12159                 },
12160                 {
12161                     tag : 'button',
12162                     type : 'button',
12163                     name : 'cancel',
12164                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12165                     html : 'Cancel'
12166                 }
12167             ]
12168         };
12169         
12170         if(this.editable){
12171             buttons.cn.unshift({
12172                 tag: 'input',
12173                 cls: 'roo-select2-search-field-input'
12174             });
12175         }
12176         
12177         var _this = this;
12178         
12179         Roo.each(buttons.cn, function(c){
12180             if (_this.size) {
12181                 c.cls += ' btn-' + _this.size;
12182             }
12183
12184             if (_this.disabled) {
12185                 c.disabled = true;
12186             }
12187         });
12188         
12189         var box = {
12190             tag: 'div',
12191             cn: [
12192                 {
12193                     tag: 'input',
12194                     type : 'hidden',
12195                     cls: 'form-hidden-field'
12196                 },
12197                 {
12198                     tag: 'ul',
12199                     cls: 'roo-select2-choices',
12200                     cn:[
12201                         {
12202                             tag: 'li',
12203                             cls: 'roo-select2-search-field',
12204                             cn: [
12205
12206                                 buttons
12207                             ]
12208                         }
12209                     ]
12210                 }
12211             ]
12212         };
12213         
12214         var combobox = {
12215             cls: 'roo-select2-container input-group roo-select2-container-multi',
12216             cn: [
12217                 box
12218 //                {
12219 //                    tag: 'ul',
12220 //                    cls: 'typeahead typeahead-long dropdown-menu',
12221 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12222 //                }
12223             ]
12224         };
12225         
12226         if(this.hasFeedback && !this.allowBlank){
12227             
12228             var feedback = {
12229                 tag: 'span',
12230                 cls: 'glyphicon form-control-feedback'
12231             };
12232
12233             combobox.cn.push(feedback);
12234         }
12235         
12236         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12237             
12238 //                Roo.log("left and has label");
12239                 cfg.cn = [
12240                     
12241                     {
12242                         tag: 'label',
12243                         'for' :  id,
12244                         cls : 'control-label col-sm-' + this.labelWidth,
12245                         html : this.fieldLabel
12246                         
12247                     },
12248                     {
12249                         cls : "col-sm-" + (12 - this.labelWidth), 
12250                         cn: [
12251                             combobox
12252                         ]
12253                     }
12254                     
12255                 ];
12256         } else if ( this.fieldLabel.length) {
12257 //                Roo.log(" label");
12258                  cfg.cn = [
12259                    
12260                     {
12261                         tag: 'label',
12262                         //cls : 'input-group-addon',
12263                         html : this.fieldLabel
12264                         
12265                     },
12266                     
12267                     combobox
12268                     
12269                 ];
12270
12271         } else {
12272             
12273 //                Roo.log(" no label && no align");
12274                 cfg = combobox
12275                      
12276                 
12277         }
12278          
12279         var settings=this;
12280         ['xs','sm','md','lg'].map(function(size){
12281             if (settings[size]) {
12282                 cfg.cls += ' col-' + size + '-' + settings[size];
12283             }
12284         });
12285         
12286         return cfg;
12287         
12288     },
12289     
12290     _initEventsCalled : false,
12291     
12292     // private
12293     initEvents: function()
12294     {
12295         
12296         if (this._initEventsCalled) { // as we call render... prevent looping...
12297             return;
12298         }
12299         this._initEventsCalled = true;
12300         
12301         if (!this.store) {
12302             throw "can not find store for combo";
12303         }
12304         
12305         this.store = Roo.factory(this.store, Roo.data);
12306         
12307         // if we are building from html. then this element is so complex, that we can not really
12308         // use the rendered HTML.
12309         // so we have to trash and replace the previous code.
12310         if (Roo.XComponent.build_from_html) {
12311             
12312             // remove this element....
12313             var e = this.el.dom, k=0;
12314             while (e ) { e = e.previousSibling;  ++k;}
12315
12316             this.el.remove();
12317             
12318             this.el=false;
12319             this.rendered = false;
12320             
12321             this.render(this.parent().getChildContainer(true), k);
12322             
12323             
12324             
12325         }
12326         
12327         
12328         /*
12329          * Touch Devices
12330          */
12331         
12332         if(Roo.isTouch && this.mobileTouchView){
12333             this.initTouchView();
12334             return;
12335         }
12336         
12337         if(this.tickable){
12338             this.initTickableEvents();
12339             return;
12340         }
12341         
12342         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12343         
12344         if(this.hiddenName){
12345             
12346             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12347             
12348             this.hiddenField.dom.value =
12349                 this.hiddenValue !== undefined ? this.hiddenValue :
12350                 this.value !== undefined ? this.value : '';
12351
12352             // prevent input submission
12353             this.el.dom.removeAttribute('name');
12354             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12355              
12356              
12357         }
12358         //if(Roo.isGecko){
12359         //    this.el.dom.setAttribute('autocomplete', 'off');
12360         //}
12361         
12362         var cls = 'x-combo-list';
12363         
12364         //this.list = new Roo.Layer({
12365         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12366         //});
12367         
12368         var _this = this;
12369         
12370         (function(){
12371             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12372             _this.list.setWidth(lw);
12373         }).defer(100);
12374         
12375         this.list.on('mouseover', this.onViewOver, this);
12376         this.list.on('mousemove', this.onViewMove, this);
12377         
12378         this.list.on('scroll', this.onViewScroll, this);
12379         
12380         /*
12381         this.list.swallowEvent('mousewheel');
12382         this.assetHeight = 0;
12383
12384         if(this.title){
12385             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12386             this.assetHeight += this.header.getHeight();
12387         }
12388
12389         this.innerList = this.list.createChild({cls:cls+'-inner'});
12390         this.innerList.on('mouseover', this.onViewOver, this);
12391         this.innerList.on('mousemove', this.onViewMove, this);
12392         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12393         
12394         if(this.allowBlank && !this.pageSize && !this.disableClear){
12395             this.footer = this.list.createChild({cls:cls+'-ft'});
12396             this.pageTb = new Roo.Toolbar(this.footer);
12397            
12398         }
12399         if(this.pageSize){
12400             this.footer = this.list.createChild({cls:cls+'-ft'});
12401             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12402                     {pageSize: this.pageSize});
12403             
12404         }
12405         
12406         if (this.pageTb && this.allowBlank && !this.disableClear) {
12407             var _this = this;
12408             this.pageTb.add(new Roo.Toolbar.Fill(), {
12409                 cls: 'x-btn-icon x-btn-clear',
12410                 text: '&#160;',
12411                 handler: function()
12412                 {
12413                     _this.collapse();
12414                     _this.clearValue();
12415                     _this.onSelect(false, -1);
12416                 }
12417             });
12418         }
12419         if (this.footer) {
12420             this.assetHeight += this.footer.getHeight();
12421         }
12422         */
12423             
12424         if(!this.tpl){
12425             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12426         }
12427
12428         this.view = new Roo.View(this.list, this.tpl, {
12429             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12430         });
12431         //this.view.wrapEl.setDisplayed(false);
12432         this.view.on('click', this.onViewClick, this);
12433         
12434         
12435         
12436         this.store.on('beforeload', this.onBeforeLoad, this);
12437         this.store.on('load', this.onLoad, this);
12438         this.store.on('loadexception', this.onLoadException, this);
12439         /*
12440         if(this.resizable){
12441             this.resizer = new Roo.Resizable(this.list,  {
12442                pinned:true, handles:'se'
12443             });
12444             this.resizer.on('resize', function(r, w, h){
12445                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12446                 this.listWidth = w;
12447                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12448                 this.restrictHeight();
12449             }, this);
12450             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12451         }
12452         */
12453         if(!this.editable){
12454             this.editable = true;
12455             this.setEditable(false);
12456         }
12457         
12458         /*
12459         
12460         if (typeof(this.events.add.listeners) != 'undefined') {
12461             
12462             this.addicon = this.wrap.createChild(
12463                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12464        
12465             this.addicon.on('click', function(e) {
12466                 this.fireEvent('add', this);
12467             }, this);
12468         }
12469         if (typeof(this.events.edit.listeners) != 'undefined') {
12470             
12471             this.editicon = this.wrap.createChild(
12472                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12473             if (this.addicon) {
12474                 this.editicon.setStyle('margin-left', '40px');
12475             }
12476             this.editicon.on('click', function(e) {
12477                 
12478                 // we fire even  if inothing is selected..
12479                 this.fireEvent('edit', this, this.lastData );
12480                 
12481             }, this);
12482         }
12483         */
12484         
12485         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12486             "up" : function(e){
12487                 this.inKeyMode = true;
12488                 this.selectPrev();
12489             },
12490
12491             "down" : function(e){
12492                 if(!this.isExpanded()){
12493                     this.onTriggerClick();
12494                 }else{
12495                     this.inKeyMode = true;
12496                     this.selectNext();
12497                 }
12498             },
12499
12500             "enter" : function(e){
12501 //                this.onViewClick();
12502                 //return true;
12503                 this.collapse();
12504                 
12505                 if(this.fireEvent("specialkey", this, e)){
12506                     this.onViewClick(false);
12507                 }
12508                 
12509                 return true;
12510             },
12511
12512             "esc" : function(e){
12513                 this.collapse();
12514             },
12515
12516             "tab" : function(e){
12517                 this.collapse();
12518                 
12519                 if(this.fireEvent("specialkey", this, e)){
12520                     this.onViewClick(false);
12521                 }
12522                 
12523                 return true;
12524             },
12525
12526             scope : this,
12527
12528             doRelay : function(foo, bar, hname){
12529                 if(hname == 'down' || this.scope.isExpanded()){
12530                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12531                 }
12532                 return true;
12533             },
12534
12535             forceKeyDown: true
12536         });
12537         
12538         
12539         this.queryDelay = Math.max(this.queryDelay || 10,
12540                 this.mode == 'local' ? 10 : 250);
12541         
12542         
12543         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12544         
12545         if(this.typeAhead){
12546             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12547         }
12548         if(this.editable !== false){
12549             this.inputEl().on("keyup", this.onKeyUp, this);
12550         }
12551         if(this.forceSelection){
12552             this.inputEl().on('blur', this.doForce, this);
12553         }
12554         
12555         if(this.multiple){
12556             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12557             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12558         }
12559     },
12560     
12561     initTickableEvents: function()
12562     {   
12563         this.createList();
12564         
12565         if(this.hiddenName){
12566             
12567             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12568             
12569             this.hiddenField.dom.value =
12570                 this.hiddenValue !== undefined ? this.hiddenValue :
12571                 this.value !== undefined ? this.value : '';
12572
12573             // prevent input submission
12574             this.el.dom.removeAttribute('name');
12575             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12576              
12577              
12578         }
12579         
12580 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12581         
12582         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12583         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12584         if(this.triggerList){
12585             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12586         }
12587          
12588         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12589         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12590         
12591         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12592         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12593         
12594         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12595         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12596         
12597         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12598         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12599         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12600         
12601         this.okBtn.hide();
12602         this.cancelBtn.hide();
12603         
12604         var _this = this;
12605         
12606         (function(){
12607             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12608             _this.list.setWidth(lw);
12609         }).defer(100);
12610         
12611         this.list.on('mouseover', this.onViewOver, this);
12612         this.list.on('mousemove', this.onViewMove, this);
12613         
12614         this.list.on('scroll', this.onViewScroll, this);
12615         
12616         if(!this.tpl){
12617             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>';
12618         }
12619
12620         this.view = new Roo.View(this.list, this.tpl, {
12621             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12622         });
12623         
12624         //this.view.wrapEl.setDisplayed(false);
12625         this.view.on('click', this.onViewClick, this);
12626         
12627         
12628         
12629         this.store.on('beforeload', this.onBeforeLoad, this);
12630         this.store.on('load', this.onLoad, this);
12631         this.store.on('loadexception', this.onLoadException, this);
12632         
12633         if(this.editable){
12634             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12635                 "up" : function(e){
12636                     this.inKeyMode = true;
12637                     this.selectPrev();
12638                 },
12639
12640                 "down" : function(e){
12641                     this.inKeyMode = true;
12642                     this.selectNext();
12643                 },
12644
12645                 "enter" : function(e){
12646                     if(this.fireEvent("specialkey", this, e)){
12647                         this.onViewClick(false);
12648                     }
12649                     
12650                     return true;
12651                 },
12652
12653                 "esc" : function(e){
12654                     this.onTickableFooterButtonClick(e, false, false);
12655                 },
12656
12657                 "tab" : function(e){
12658                     this.fireEvent("specialkey", this, e);
12659                     
12660                     this.onTickableFooterButtonClick(e, false, false);
12661                     
12662                     return true;
12663                 },
12664
12665                 scope : this,
12666
12667                 doRelay : function(e, fn, key){
12668                     if(this.scope.isExpanded()){
12669                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12670                     }
12671                     return true;
12672                 },
12673
12674                 forceKeyDown: true
12675             });
12676         }
12677         
12678         this.queryDelay = Math.max(this.queryDelay || 10,
12679                 this.mode == 'local' ? 10 : 250);
12680         
12681         
12682         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12683         
12684         if(this.typeAhead){
12685             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12686         }
12687         
12688         if(this.editable !== false){
12689             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12690         }
12691         
12692     },
12693
12694     onDestroy : function(){
12695         if(this.view){
12696             this.view.setStore(null);
12697             this.view.el.removeAllListeners();
12698             this.view.el.remove();
12699             this.view.purgeListeners();
12700         }
12701         if(this.list){
12702             this.list.dom.innerHTML  = '';
12703         }
12704         
12705         if(this.store){
12706             this.store.un('beforeload', this.onBeforeLoad, this);
12707             this.store.un('load', this.onLoad, this);
12708             this.store.un('loadexception', this.onLoadException, this);
12709         }
12710         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12711     },
12712
12713     // private
12714     fireKey : function(e){
12715         if(e.isNavKeyPress() && !this.list.isVisible()){
12716             this.fireEvent("specialkey", this, e);
12717         }
12718     },
12719
12720     // private
12721     onResize: function(w, h){
12722 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12723 //        
12724 //        if(typeof w != 'number'){
12725 //            // we do not handle it!?!?
12726 //            return;
12727 //        }
12728 //        var tw = this.trigger.getWidth();
12729 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12730 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12731 //        var x = w - tw;
12732 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12733 //            
12734 //        //this.trigger.setStyle('left', x+'px');
12735 //        
12736 //        if(this.list && this.listWidth === undefined){
12737 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12738 //            this.list.setWidth(lw);
12739 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12740 //        }
12741         
12742     
12743         
12744     },
12745
12746     /**
12747      * Allow or prevent the user from directly editing the field text.  If false is passed,
12748      * the user will only be able to select from the items defined in the dropdown list.  This method
12749      * is the runtime equivalent of setting the 'editable' config option at config time.
12750      * @param {Boolean} value True to allow the user to directly edit the field text
12751      */
12752     setEditable : function(value){
12753         if(value == this.editable){
12754             return;
12755         }
12756         this.editable = value;
12757         if(!value){
12758             this.inputEl().dom.setAttribute('readOnly', true);
12759             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12760             this.inputEl().addClass('x-combo-noedit');
12761         }else{
12762             this.inputEl().dom.setAttribute('readOnly', false);
12763             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12764             this.inputEl().removeClass('x-combo-noedit');
12765         }
12766     },
12767
12768     // private
12769     
12770     onBeforeLoad : function(combo,opts){
12771         if(!this.hasFocus){
12772             return;
12773         }
12774          if (!opts.add) {
12775             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12776          }
12777         this.restrictHeight();
12778         this.selectedIndex = -1;
12779     },
12780
12781     // private
12782     onLoad : function(){
12783         
12784         this.hasQuery = false;
12785         
12786         if(!this.hasFocus){
12787             return;
12788         }
12789         
12790         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12791             this.loading.hide();
12792         }
12793              
12794         if(this.store.getCount() > 0){
12795             this.expand();
12796             this.restrictHeight();
12797             if(this.lastQuery == this.allQuery){
12798                 if(this.editable && !this.tickable){
12799                     this.inputEl().dom.select();
12800                 }
12801                 
12802                 if(
12803                     !this.selectByValue(this.value, true) &&
12804                     this.autoFocus && 
12805                     (
12806                         !this.store.lastOptions ||
12807                         typeof(this.store.lastOptions.add) == 'undefined' || 
12808                         this.store.lastOptions.add != true
12809                     )
12810                 ){
12811                     this.select(0, true);
12812                 }
12813             }else{
12814                 if(this.autoFocus){
12815                     this.selectNext();
12816                 }
12817                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12818                     this.taTask.delay(this.typeAheadDelay);
12819                 }
12820             }
12821         }else{
12822             this.onEmptyResults();
12823         }
12824         
12825         //this.el.focus();
12826     },
12827     // private
12828     onLoadException : function()
12829     {
12830         this.hasQuery = false;
12831         
12832         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12833             this.loading.hide();
12834         }
12835         
12836         if(this.tickable && this.editable){
12837             return;
12838         }
12839         
12840         this.collapse();
12841         // only causes errors at present
12842         //Roo.log(this.store.reader.jsonData);
12843         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12844             // fixme
12845             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12846         //}
12847         
12848         
12849     },
12850     // private
12851     onTypeAhead : function(){
12852         if(this.store.getCount() > 0){
12853             var r = this.store.getAt(0);
12854             var newValue = r.data[this.displayField];
12855             var len = newValue.length;
12856             var selStart = this.getRawValue().length;
12857             
12858             if(selStart != len){
12859                 this.setRawValue(newValue);
12860                 this.selectText(selStart, newValue.length);
12861             }
12862         }
12863     },
12864
12865     // private
12866     onSelect : function(record, index){
12867         
12868         if(this.fireEvent('beforeselect', this, record, index) !== false){
12869         
12870             this.setFromData(index > -1 ? record.data : false);
12871             
12872             this.collapse();
12873             this.fireEvent('select', this, record, index);
12874         }
12875     },
12876
12877     /**
12878      * Returns the currently selected field value or empty string if no value is set.
12879      * @return {String} value The selected value
12880      */
12881     getValue : function(){
12882         
12883         if(this.multiple){
12884             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12885         }
12886         
12887         if(this.valueField){
12888             return typeof this.value != 'undefined' ? this.value : '';
12889         }else{
12890             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12891         }
12892     },
12893
12894     /**
12895      * Clears any text/value currently set in the field
12896      */
12897     clearValue : function(){
12898         if(this.hiddenField){
12899             this.hiddenField.dom.value = '';
12900         }
12901         this.value = '';
12902         this.setRawValue('');
12903         this.lastSelectionText = '';
12904         this.lastData = false;
12905         
12906         var close = this.closeTriggerEl();
12907         
12908         if(close){
12909             close.hide();
12910         }
12911         
12912     },
12913
12914     /**
12915      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12916      * will be displayed in the field.  If the value does not match the data value of an existing item,
12917      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12918      * Otherwise the field will be blank (although the value will still be set).
12919      * @param {String} value The value to match
12920      */
12921     setValue : function(v){
12922         if(this.multiple){
12923             this.syncValue();
12924             return;
12925         }
12926         
12927         var text = v;
12928         if(this.valueField){
12929             var r = this.findRecord(this.valueField, v);
12930             if(r){
12931                 text = r.data[this.displayField];
12932             }else if(this.valueNotFoundText !== undefined){
12933                 text = this.valueNotFoundText;
12934             }
12935         }
12936         this.lastSelectionText = text;
12937         if(this.hiddenField){
12938             this.hiddenField.dom.value = v;
12939         }
12940         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12941         this.value = v;
12942         
12943         var close = this.closeTriggerEl();
12944         
12945         if(close){
12946             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12947         }
12948     },
12949     /**
12950      * @property {Object} the last set data for the element
12951      */
12952     
12953     lastData : false,
12954     /**
12955      * Sets the value of the field based on a object which is related to the record format for the store.
12956      * @param {Object} value the value to set as. or false on reset?
12957      */
12958     setFromData : function(o){
12959         
12960         if(this.multiple){
12961             this.addItem(o);
12962             return;
12963         }
12964             
12965         var dv = ''; // display value
12966         var vv = ''; // value value..
12967         this.lastData = o;
12968         if (this.displayField) {
12969             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12970         } else {
12971             // this is an error condition!!!
12972             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12973         }
12974         
12975         if(this.valueField){
12976             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12977         }
12978         
12979         var close = this.closeTriggerEl();
12980         
12981         if(close){
12982             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12983         }
12984         
12985         if(this.hiddenField){
12986             this.hiddenField.dom.value = vv;
12987             
12988             this.lastSelectionText = dv;
12989             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12990             this.value = vv;
12991             return;
12992         }
12993         // no hidden field.. - we store the value in 'value', but still display
12994         // display field!!!!
12995         this.lastSelectionText = dv;
12996         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12997         this.value = vv;
12998         
12999         
13000         
13001     },
13002     // private
13003     reset : function(){
13004         // overridden so that last data is reset..
13005         
13006         if(this.multiple){
13007             this.clearItem();
13008             return;
13009         }
13010         
13011         this.setValue(this.originalValue);
13012         this.clearInvalid();
13013         this.lastData = false;
13014         if (this.view) {
13015             this.view.clearSelections();
13016         }
13017     },
13018     // private
13019     findRecord : function(prop, value){
13020         var record;
13021         if(this.store.getCount() > 0){
13022             this.store.each(function(r){
13023                 if(r.data[prop] == value){
13024                     record = r;
13025                     return false;
13026                 }
13027                 return true;
13028             });
13029         }
13030         return record;
13031     },
13032     
13033     getName: function()
13034     {
13035         // returns hidden if it's set..
13036         if (!this.rendered) {return ''};
13037         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13038         
13039     },
13040     // private
13041     onViewMove : function(e, t){
13042         this.inKeyMode = false;
13043     },
13044
13045     // private
13046     onViewOver : function(e, t){
13047         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13048             return;
13049         }
13050         var item = this.view.findItemFromChild(t);
13051         
13052         if(item){
13053             var index = this.view.indexOf(item);
13054             this.select(index, false);
13055         }
13056     },
13057
13058     // private
13059     onViewClick : function(view, doFocus, el, e)
13060     {
13061         var index = this.view.getSelectedIndexes()[0];
13062         
13063         var r = this.store.getAt(index);
13064         
13065         if(this.tickable){
13066             
13067             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13068                 return;
13069             }
13070             
13071             var rm = false;
13072             var _this = this;
13073             
13074             Roo.each(this.tickItems, function(v,k){
13075                 
13076                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13077                     Roo.log(v);
13078                     _this.tickItems.splice(k, 1);
13079                     
13080                     if(typeof(e) == 'undefined' && view == false){
13081                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13082                     }
13083                     
13084                     rm = true;
13085                     return;
13086                 }
13087             });
13088             
13089             if(rm){
13090                 return;
13091             }
13092             
13093             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13094                 this.tickItems.push(r.data);
13095             }
13096             
13097             if(typeof(e) == 'undefined' && view == false){
13098                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13099             }
13100                     
13101             return;
13102         }
13103         
13104         if(r){
13105             this.onSelect(r, index);
13106         }
13107         if(doFocus !== false && !this.blockFocus){
13108             this.inputEl().focus();
13109         }
13110     },
13111
13112     // private
13113     restrictHeight : function(){
13114         //this.innerList.dom.style.height = '';
13115         //var inner = this.innerList.dom;
13116         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13117         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13118         //this.list.beginUpdate();
13119         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13120         this.list.alignTo(this.inputEl(), this.listAlign);
13121         this.list.alignTo(this.inputEl(), this.listAlign);
13122         //this.list.endUpdate();
13123     },
13124
13125     // private
13126     onEmptyResults : function(){
13127         
13128         if(this.tickable && this.editable){
13129             this.restrictHeight();
13130             return;
13131         }
13132         
13133         this.collapse();
13134     },
13135
13136     /**
13137      * Returns true if the dropdown list is expanded, else false.
13138      */
13139     isExpanded : function(){
13140         return this.list.isVisible();
13141     },
13142
13143     /**
13144      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13145      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13146      * @param {String} value The data value of the item to select
13147      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13148      * selected item if it is not currently in view (defaults to true)
13149      * @return {Boolean} True if the value matched an item in the list, else false
13150      */
13151     selectByValue : function(v, scrollIntoView){
13152         if(v !== undefined && v !== null){
13153             var r = this.findRecord(this.valueField || this.displayField, v);
13154             if(r){
13155                 this.select(this.store.indexOf(r), scrollIntoView);
13156                 return true;
13157             }
13158         }
13159         return false;
13160     },
13161
13162     /**
13163      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13164      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13165      * @param {Number} index The zero-based index of the list item to select
13166      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13167      * selected item if it is not currently in view (defaults to true)
13168      */
13169     select : function(index, scrollIntoView){
13170         this.selectedIndex = index;
13171         this.view.select(index);
13172         if(scrollIntoView !== false){
13173             var el = this.view.getNode(index);
13174             /*
13175              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13176              */
13177             if(el){
13178                 this.list.scrollChildIntoView(el, false);
13179             }
13180         }
13181     },
13182
13183     // private
13184     selectNext : function(){
13185         var ct = this.store.getCount();
13186         if(ct > 0){
13187             if(this.selectedIndex == -1){
13188                 this.select(0);
13189             }else if(this.selectedIndex < ct-1){
13190                 this.select(this.selectedIndex+1);
13191             }
13192         }
13193     },
13194
13195     // private
13196     selectPrev : function(){
13197         var ct = this.store.getCount();
13198         if(ct > 0){
13199             if(this.selectedIndex == -1){
13200                 this.select(0);
13201             }else if(this.selectedIndex != 0){
13202                 this.select(this.selectedIndex-1);
13203             }
13204         }
13205     },
13206
13207     // private
13208     onKeyUp : function(e){
13209         if(this.editable !== false && !e.isSpecialKey()){
13210             this.lastKey = e.getKey();
13211             this.dqTask.delay(this.queryDelay);
13212         }
13213     },
13214
13215     // private
13216     validateBlur : function(){
13217         return !this.list || !this.list.isVisible();   
13218     },
13219
13220     // private
13221     initQuery : function(){
13222         
13223         var v = this.getRawValue();
13224         
13225         if(this.tickable && this.editable){
13226             v = this.tickableInputEl().getValue();
13227         }
13228         
13229         this.doQuery(v);
13230     },
13231
13232     // private
13233     doForce : function(){
13234         if(this.inputEl().dom.value.length > 0){
13235             this.inputEl().dom.value =
13236                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13237              
13238         }
13239     },
13240
13241     /**
13242      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13243      * query allowing the query action to be canceled if needed.
13244      * @param {String} query The SQL query to execute
13245      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13246      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13247      * saved in the current store (defaults to false)
13248      */
13249     doQuery : function(q, forceAll){
13250         
13251         if(q === undefined || q === null){
13252             q = '';
13253         }
13254         var qe = {
13255             query: q,
13256             forceAll: forceAll,
13257             combo: this,
13258             cancel:false
13259         };
13260         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13261             return false;
13262         }
13263         q = qe.query;
13264         
13265         forceAll = qe.forceAll;
13266         if(forceAll === true || (q.length >= this.minChars)){
13267             
13268             this.hasQuery = true;
13269             
13270             if(this.lastQuery != q || this.alwaysQuery){
13271                 this.lastQuery = q;
13272                 if(this.mode == 'local'){
13273                     this.selectedIndex = -1;
13274                     if(forceAll){
13275                         this.store.clearFilter();
13276                     }else{
13277                         
13278                         if(this.specialFilter){
13279                             this.fireEvent('specialfilter', this);
13280                             this.onLoad();
13281                             return;
13282                         }
13283                         
13284                         this.store.filter(this.displayField, q);
13285                     }
13286                     
13287                     this.store.fireEvent("datachanged", this.store);
13288                     
13289                     this.onLoad();
13290                     
13291                     
13292                 }else{
13293                     
13294                     this.store.baseParams[this.queryParam] = q;
13295                     
13296                     var options = {params : this.getParams(q)};
13297                     
13298                     if(this.loadNext){
13299                         options.add = true;
13300                         options.params.start = this.page * this.pageSize;
13301                     }
13302                     
13303                     this.store.load(options);
13304                     
13305                     /*
13306                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13307                      *  we should expand the list on onLoad
13308                      *  so command out it
13309                      */
13310 //                    this.expand();
13311                 }
13312             }else{
13313                 this.selectedIndex = -1;
13314                 this.onLoad();   
13315             }
13316         }
13317         
13318         this.loadNext = false;
13319     },
13320     
13321     // private
13322     getParams : function(q){
13323         var p = {};
13324         //p[this.queryParam] = q;
13325         
13326         if(this.pageSize){
13327             p.start = 0;
13328             p.limit = this.pageSize;
13329         }
13330         return p;
13331     },
13332
13333     /**
13334      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13335      */
13336     collapse : function(){
13337         if(!this.isExpanded()){
13338             return;
13339         }
13340         
13341         this.list.hide();
13342         
13343         if(this.tickable){
13344             this.hasFocus = false;
13345             this.okBtn.hide();
13346             this.cancelBtn.hide();
13347             this.trigger.show();
13348             
13349             if(this.editable){
13350                 this.tickableInputEl().dom.value = '';
13351                 this.tickableInputEl().blur();
13352             }
13353             
13354         }
13355         
13356         Roo.get(document).un('mousedown', this.collapseIf, this);
13357         Roo.get(document).un('mousewheel', this.collapseIf, this);
13358         if (!this.editable) {
13359             Roo.get(document).un('keydown', this.listKeyPress, this);
13360         }
13361         this.fireEvent('collapse', this);
13362     },
13363
13364     // private
13365     collapseIf : function(e){
13366         var in_combo  = e.within(this.el);
13367         var in_list =  e.within(this.list);
13368         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13369         
13370         if (in_combo || in_list || is_list) {
13371             //e.stopPropagation();
13372             return;
13373         }
13374         
13375         if(this.tickable){
13376             this.onTickableFooterButtonClick(e, false, false);
13377         }
13378
13379         this.collapse();
13380         
13381     },
13382
13383     /**
13384      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13385      */
13386     expand : function(){
13387        
13388         if(this.isExpanded() || !this.hasFocus){
13389             return;
13390         }
13391         
13392         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13393         this.list.setWidth(lw);
13394         
13395         
13396          Roo.log('expand');
13397         
13398         this.list.show();
13399         
13400         this.restrictHeight();
13401         
13402         if(this.tickable){
13403             
13404             this.tickItems = Roo.apply([], this.item);
13405             
13406             this.okBtn.show();
13407             this.cancelBtn.show();
13408             this.trigger.hide();
13409             
13410             if(this.editable){
13411                 this.tickableInputEl().focus();
13412             }
13413             
13414         }
13415         
13416         Roo.get(document).on('mousedown', this.collapseIf, this);
13417         Roo.get(document).on('mousewheel', this.collapseIf, this);
13418         if (!this.editable) {
13419             Roo.get(document).on('keydown', this.listKeyPress, this);
13420         }
13421         
13422         this.fireEvent('expand', this);
13423     },
13424
13425     // private
13426     // Implements the default empty TriggerField.onTriggerClick function
13427     onTriggerClick : function(e)
13428     {
13429         Roo.log('trigger click');
13430         
13431         if(this.disabled || !this.triggerList){
13432             return;
13433         }
13434         
13435         this.page = 0;
13436         this.loadNext = false;
13437         
13438         if(this.isExpanded()){
13439             this.collapse();
13440             if (!this.blockFocus) {
13441                 this.inputEl().focus();
13442             }
13443             
13444         }else {
13445             this.hasFocus = true;
13446             if(this.triggerAction == 'all') {
13447                 this.doQuery(this.allQuery, true);
13448             } else {
13449                 this.doQuery(this.getRawValue());
13450             }
13451             if (!this.blockFocus) {
13452                 this.inputEl().focus();
13453             }
13454         }
13455     },
13456     
13457     onTickableTriggerClick : function(e)
13458     {
13459         if(this.disabled){
13460             return;
13461         }
13462         
13463         this.page = 0;
13464         this.loadNext = false;
13465         this.hasFocus = true;
13466         
13467         if(this.triggerAction == 'all') {
13468             this.doQuery(this.allQuery, true);
13469         } else {
13470             this.doQuery(this.getRawValue());
13471         }
13472     },
13473     
13474     onSearchFieldClick : function(e)
13475     {
13476         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13477             this.onTickableFooterButtonClick(e, false, false);
13478             return;
13479         }
13480         
13481         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13482             return;
13483         }
13484         
13485         this.page = 0;
13486         this.loadNext = false;
13487         this.hasFocus = true;
13488         
13489         if(this.triggerAction == 'all') {
13490             this.doQuery(this.allQuery, true);
13491         } else {
13492             this.doQuery(this.getRawValue());
13493         }
13494     },
13495     
13496     listKeyPress : function(e)
13497     {
13498         //Roo.log('listkeypress');
13499         // scroll to first matching element based on key pres..
13500         if (e.isSpecialKey()) {
13501             return false;
13502         }
13503         var k = String.fromCharCode(e.getKey()).toUpperCase();
13504         //Roo.log(k);
13505         var match  = false;
13506         var csel = this.view.getSelectedNodes();
13507         var cselitem = false;
13508         if (csel.length) {
13509             var ix = this.view.indexOf(csel[0]);
13510             cselitem  = this.store.getAt(ix);
13511             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13512                 cselitem = false;
13513             }
13514             
13515         }
13516         
13517         this.store.each(function(v) { 
13518             if (cselitem) {
13519                 // start at existing selection.
13520                 if (cselitem.id == v.id) {
13521                     cselitem = false;
13522                 }
13523                 return true;
13524             }
13525                 
13526             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13527                 match = this.store.indexOf(v);
13528                 return false;
13529             }
13530             return true;
13531         }, this);
13532         
13533         if (match === false) {
13534             return true; // no more action?
13535         }
13536         // scroll to?
13537         this.view.select(match);
13538         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13539         sn.scrollIntoView(sn.dom.parentNode, false);
13540     },
13541     
13542     onViewScroll : function(e, t){
13543         
13544         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){
13545             return;
13546         }
13547         
13548         this.hasQuery = true;
13549         
13550         this.loading = this.list.select('.loading', true).first();
13551         
13552         if(this.loading === null){
13553             this.list.createChild({
13554                 tag: 'div',
13555                 cls: 'loading roo-select2-more-results roo-select2-active',
13556                 html: 'Loading more results...'
13557             });
13558             
13559             this.loading = this.list.select('.loading', true).first();
13560             
13561             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13562             
13563             this.loading.hide();
13564         }
13565         
13566         this.loading.show();
13567         
13568         var _combo = this;
13569         
13570         this.page++;
13571         this.loadNext = true;
13572         
13573         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13574         
13575         return;
13576     },
13577     
13578     addItem : function(o)
13579     {   
13580         var dv = ''; // display value
13581         
13582         if (this.displayField) {
13583             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13584         } else {
13585             // this is an error condition!!!
13586             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13587         }
13588         
13589         if(!dv.length){
13590             return;
13591         }
13592         
13593         var choice = this.choices.createChild({
13594             tag: 'li',
13595             cls: 'roo-select2-search-choice',
13596             cn: [
13597                 {
13598                     tag: 'div',
13599                     html: dv
13600                 },
13601                 {
13602                     tag: 'a',
13603                     href: '#',
13604                     cls: 'roo-select2-search-choice-close',
13605                     tabindex: '-1'
13606                 }
13607             ]
13608             
13609         }, this.searchField);
13610         
13611         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13612         
13613         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13614         
13615         this.item.push(o);
13616         
13617         this.lastData = o;
13618         
13619         this.syncValue();
13620         
13621         this.inputEl().dom.value = '';
13622         
13623         this.validate();
13624     },
13625     
13626     onRemoveItem : function(e, _self, o)
13627     {
13628         e.preventDefault();
13629         
13630         this.lastItem = Roo.apply([], this.item);
13631         
13632         var index = this.item.indexOf(o.data) * 1;
13633         
13634         if( index < 0){
13635             Roo.log('not this item?!');
13636             return;
13637         }
13638         
13639         this.item.splice(index, 1);
13640         o.item.remove();
13641         
13642         this.syncValue();
13643         
13644         this.fireEvent('remove', this, e);
13645         
13646         this.validate();
13647         
13648     },
13649     
13650     syncValue : function()
13651     {
13652         if(!this.item.length){
13653             this.clearValue();
13654             return;
13655         }
13656             
13657         var value = [];
13658         var _this = this;
13659         Roo.each(this.item, function(i){
13660             if(_this.valueField){
13661                 value.push(i[_this.valueField]);
13662                 return;
13663             }
13664
13665             value.push(i);
13666         });
13667
13668         this.value = value.join(',');
13669
13670         if(this.hiddenField){
13671             this.hiddenField.dom.value = this.value;
13672         }
13673         
13674         this.store.fireEvent("datachanged", this.store);
13675     },
13676     
13677     clearItem : function()
13678     {
13679         if(!this.multiple){
13680             return;
13681         }
13682         
13683         this.item = [];
13684         
13685         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13686            c.remove();
13687         });
13688         
13689         this.syncValue();
13690         
13691         this.validate();
13692         
13693         if(this.tickable && !Roo.isTouch){
13694             this.view.refresh();
13695         }
13696     },
13697     
13698     inputEl: function ()
13699     {
13700         if(Roo.isTouch && this.mobileTouchView){
13701             return this.el.select('input.form-control',true).first();
13702         }
13703         
13704         if(this.tickable){
13705             return this.searchField;
13706         }
13707         
13708         return this.el.select('input.form-control',true).first();
13709     },
13710     
13711     
13712     onTickableFooterButtonClick : function(e, btn, el)
13713     {
13714         e.preventDefault();
13715         
13716         this.lastItem = Roo.apply([], this.item);
13717         
13718         if(btn && btn.name == 'cancel'){
13719             this.tickItems = Roo.apply([], this.item);
13720             this.collapse();
13721             return;
13722         }
13723         
13724         this.clearItem();
13725         
13726         var _this = this;
13727         
13728         Roo.each(this.tickItems, function(o){
13729             _this.addItem(o);
13730         });
13731         
13732         this.collapse();
13733         
13734     },
13735     
13736     validate : function()
13737     {
13738         var v = this.getRawValue();
13739         
13740         if(this.multiple){
13741             v = this.getValue();
13742         }
13743         
13744         if(this.disabled || this.allowBlank || v.length){
13745             this.markValid();
13746             return true;
13747         }
13748         
13749         this.markInvalid();
13750         return false;
13751     },
13752     
13753     tickableInputEl : function()
13754     {
13755         if(!this.tickable || !this.editable){
13756             return this.inputEl();
13757         }
13758         
13759         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13760     },
13761     
13762     
13763     getAutoCreateTouchView : function()
13764     {
13765         var id = Roo.id();
13766         
13767         var cfg = {
13768             cls: 'form-group' //input-group
13769         };
13770         
13771         var input =  {
13772             tag: 'input',
13773             id : id,
13774             type : this.inputType,
13775             cls : 'form-control x-combo-noedit',
13776             autocomplete: 'new-password',
13777             placeholder : this.placeholder || '',
13778             readonly : true
13779         };
13780         
13781         if (this.name) {
13782             input.name = this.name;
13783         }
13784         
13785         if (this.size) {
13786             input.cls += ' input-' + this.size;
13787         }
13788         
13789         if (this.disabled) {
13790             input.disabled = true;
13791         }
13792         
13793         var inputblock = {
13794             cls : '',
13795             cn : [
13796                 input
13797             ]
13798         };
13799         
13800         if(this.before){
13801             inputblock.cls += ' input-group';
13802             
13803             inputblock.cn.unshift({
13804                 tag :'span',
13805                 cls : 'input-group-addon',
13806                 html : this.before
13807             });
13808         }
13809         
13810         if(this.removable && !this.multiple){
13811             inputblock.cls += ' roo-removable';
13812             
13813             inputblock.cn.push({
13814                 tag: 'button',
13815                 html : 'x',
13816                 cls : 'roo-combo-removable-btn close'
13817             });
13818         }
13819
13820         if(this.hasFeedback && !this.allowBlank){
13821             
13822             inputblock.cls += ' has-feedback';
13823             
13824             inputblock.cn.push({
13825                 tag: 'span',
13826                 cls: 'glyphicon form-control-feedback'
13827             });
13828             
13829         }
13830         
13831         if (this.after) {
13832             
13833             inputblock.cls += (this.before) ? '' : ' input-group';
13834             
13835             inputblock.cn.push({
13836                 tag :'span',
13837                 cls : 'input-group-addon',
13838                 html : this.after
13839             });
13840         }
13841
13842         var box = {
13843             tag: 'div',
13844             cn: [
13845                 {
13846                     tag: 'input',
13847                     type : 'hidden',
13848                     cls: 'form-hidden-field'
13849                 },
13850                 inputblock
13851             ]
13852             
13853         };
13854         
13855         if(this.multiple){
13856             box = {
13857                 tag: 'div',
13858                 cn: [
13859                     {
13860                         tag: 'input',
13861                         type : 'hidden',
13862                         cls: 'form-hidden-field'
13863                     },
13864                     {
13865                         tag: 'ul',
13866                         cls: 'roo-select2-choices',
13867                         cn:[
13868                             {
13869                                 tag: 'li',
13870                                 cls: 'roo-select2-search-field',
13871                                 cn: [
13872
13873                                     inputblock
13874                                 ]
13875                             }
13876                         ]
13877                     }
13878                 ]
13879             }
13880         };
13881         
13882         var combobox = {
13883             cls: 'roo-select2-container input-group roo-touchview-combobox ',
13884             cn: [
13885                 box
13886             ]
13887         };
13888         
13889         if(!this.multiple && this.showToggleBtn){
13890             
13891             var caret = {
13892                         tag: 'span',
13893                         cls: 'caret'
13894             };
13895             
13896             if (this.caret != false) {
13897                 caret = {
13898                      tag: 'i',
13899                      cls: 'fa fa-' + this.caret
13900                 };
13901                 
13902             }
13903             
13904             combobox.cn.push({
13905                 tag :'span',
13906                 cls : 'input-group-addon btn dropdown-toggle',
13907                 cn : [
13908                     caret,
13909                     {
13910                         tag: 'span',
13911                         cls: 'combobox-clear',
13912                         cn  : [
13913                             {
13914                                 tag : 'i',
13915                                 cls: 'icon-remove'
13916                             }
13917                         ]
13918                     }
13919                 ]
13920
13921             })
13922         }
13923         
13924         if(this.multiple){
13925             combobox.cls += ' roo-select2-container-multi';
13926         }
13927         
13928         var align = this.labelAlign || this.parentLabelAlign();
13929         
13930         cfg.cn = combobox;
13931         
13932         if(this.fieldLabel.length && this.labelWidth){
13933             
13934             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13935             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13936             
13937             cfg.cn = [
13938                 {
13939                     tag: 'label',
13940                     cls : 'control-label ' + lw,
13941                     html : this.fieldLabel
13942
13943                 },
13944                 {
13945                     cls : cw, 
13946                     cn: [
13947                         combobox
13948                     ]
13949                 }
13950             ];
13951         }
13952         
13953         var settings = this;
13954         
13955         ['xs','sm','md','lg'].map(function(size){
13956             if (settings[size]) {
13957                 cfg.cls += ' col-' + size + '-' + settings[size];
13958             }
13959         });
13960         
13961         return cfg;
13962     },
13963     
13964     initTouchView : function()
13965     {
13966         this.renderTouchView();
13967         
13968         this.touchViewEl.on('scroll', function(){
13969             this.el.dom.scrollTop = 0;
13970         }, this);
13971         
13972         this.originalValue = this.getValue();
13973         
13974         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
13975         
13976         this.inputEl().on("click", this.showTouchView, this);
13977         this.triggerEl.on("click", this.showTouchView, this);
13978         
13979         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13980         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13981         
13982         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13983         
13984         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13985         this.store.on('load', this.onTouchViewLoad, this);
13986         this.store.on('loadexception', this.onTouchViewLoadException, this);
13987         
13988         if(this.hiddenName){
13989             
13990             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13991             
13992             this.hiddenField.dom.value =
13993                 this.hiddenValue !== undefined ? this.hiddenValue :
13994                 this.value !== undefined ? this.value : '';
13995         
13996             this.el.dom.removeAttribute('name');
13997             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13998         }
13999         
14000         if(this.multiple){
14001             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14002             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14003         }
14004         
14005         if(this.removable && !this.multiple){
14006             var close = this.closeTriggerEl();
14007             if(close){
14008                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14009                 close.on('click', this.removeBtnClick, this, close);
14010             }
14011         }
14012         /*
14013          * fix the bug in Safari iOS8
14014          */
14015         this.inputEl().on("focus", function(e){
14016             document.activeElement.blur();
14017         }, this);
14018         
14019         return;
14020         
14021         
14022     },
14023     
14024     renderTouchView : function()
14025     {
14026         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14027         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14028         
14029         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14030         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14031         
14032         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14033         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14034         this.touchViewBodyEl.setStyle('overflow', 'auto');
14035         
14036         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14037         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14038         
14039         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14040         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14041         
14042     },
14043     
14044     showTouchView : function()
14045     {
14046         if(this.disabled){
14047             return;
14048         }
14049         
14050         this.touchViewHeaderEl.hide();
14051
14052         if(this.modalTitle.length){
14053             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14054             this.touchViewHeaderEl.show();
14055         }
14056
14057         this.touchViewEl.show();
14058
14059         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14060         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14061                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14062
14063         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14064
14065         if(this.modalTitle.length){
14066             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14067         }
14068         
14069         this.touchViewBodyEl.setHeight(bodyHeight);
14070
14071         if(this.animate){
14072             var _this = this;
14073             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14074         }else{
14075             this.touchViewEl.addClass('in');
14076         }
14077
14078         this.doTouchViewQuery();
14079         
14080     },
14081     
14082     hideTouchView : function()
14083     {
14084         this.touchViewEl.removeClass('in');
14085
14086         if(this.animate){
14087             var _this = this;
14088             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14089         }else{
14090             this.touchViewEl.setStyle('display', 'none');
14091         }
14092         
14093     },
14094     
14095     setTouchViewValue : function()
14096     {
14097         if(this.multiple){
14098             this.clearItem();
14099         
14100             var _this = this;
14101
14102             Roo.each(this.tickItems, function(o){
14103                 this.addItem(o);
14104             }, this);
14105         }
14106         
14107         this.hideTouchView();
14108     },
14109     
14110     doTouchViewQuery : function()
14111     {
14112         var qe = {
14113             query: '',
14114             forceAll: true,
14115             combo: this,
14116             cancel:false
14117         };
14118         
14119         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14120             return false;
14121         }
14122         
14123         if(!this.alwaysQuery || this.mode == 'local'){
14124             this.onTouchViewLoad();
14125             return;
14126         }
14127         
14128         this.store.load();
14129     },
14130     
14131     onTouchViewBeforeLoad : function(combo,opts)
14132     {
14133         return;
14134     },
14135
14136     // private
14137     onTouchViewLoad : function()
14138     {
14139         if(this.store.getCount() < 1){
14140             this.onTouchViewEmptyResults();
14141             return;
14142         }
14143         
14144         this.clearTouchView();
14145         
14146         var rawValue = this.getRawValue();
14147         
14148         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14149         
14150         this.tickItems = [];
14151         
14152         this.store.data.each(function(d, rowIndex){
14153             var row = this.touchViewListGroup.createChild(template);
14154             
14155             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14156                 row.addClass(d.data.cls);
14157             }
14158             
14159             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14160                 var cfg = {
14161                     data : d.data,
14162                     html : d.data[this.displayField]
14163                 };
14164                 
14165                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14166                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14167                 }
14168             }
14169             row.removeClass('selected');
14170             if(!this.multiple && this.valueField &&
14171                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14172             {
14173                 // radio buttons..
14174                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14175                 row.addClass('selected');
14176             }
14177             
14178             if(this.multiple && this.valueField &&
14179                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14180             {
14181                 
14182                 // checkboxes...
14183                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14184                 this.tickItems.push(d.data);
14185             }
14186             
14187             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14188             
14189         }, this);
14190         
14191         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14192         
14193         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14194
14195         if(this.modalTitle.length){
14196             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14197         }
14198
14199         var listHeight = this.touchViewListGroup.getHeight();
14200         
14201         var _this = this;
14202         
14203         if(firstChecked && listHeight > bodyHeight){
14204             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14205         }
14206         
14207     },
14208     
14209     onTouchViewLoadException : function()
14210     {
14211         this.hideTouchView();
14212     },
14213     
14214     onTouchViewEmptyResults : function()
14215     {
14216         this.clearTouchView();
14217         
14218         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14219         
14220         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14221         
14222     },
14223     
14224     clearTouchView : function()
14225     {
14226         this.touchViewListGroup.dom.innerHTML = '';
14227     },
14228     
14229     onTouchViewClick : function(e, el, o)
14230     {
14231         e.preventDefault();
14232         
14233         var row = o.row;
14234         var rowIndex = o.rowIndex;
14235         
14236         var r = this.store.getAt(rowIndex);
14237         
14238         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14239             
14240             if(!this.multiple){
14241                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14242                     c.dom.removeAttribute('checked');
14243                 }, this);
14244
14245                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14246
14247                 this.setFromData(r.data);
14248
14249                 var close = this.closeTriggerEl();
14250
14251                 if(close){
14252                     close.show();
14253                 }
14254
14255                 this.hideTouchView();
14256
14257                 this.fireEvent('select', this, r, rowIndex);
14258
14259                 return;
14260             }
14261
14262             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14263                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14264                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14265                 return;
14266             }
14267
14268             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14269             this.addItem(r.data);
14270             this.tickItems.push(r.data);
14271         }
14272     }
14273     
14274
14275     /** 
14276     * @cfg {Boolean} grow 
14277     * @hide 
14278     */
14279     /** 
14280     * @cfg {Number} growMin 
14281     * @hide 
14282     */
14283     /** 
14284     * @cfg {Number} growMax 
14285     * @hide 
14286     */
14287     /**
14288      * @hide
14289      * @method autoSize
14290      */
14291 });
14292
14293 Roo.apply(Roo.bootstrap.ComboBox,  {
14294     
14295     header : {
14296         tag: 'div',
14297         cls: 'modal-header',
14298         cn: [
14299             {
14300                 tag: 'h4',
14301                 cls: 'modal-title'
14302             }
14303         ]
14304     },
14305     
14306     body : {
14307         tag: 'div',
14308         cls: 'modal-body',
14309         cn: [
14310             {
14311                 tag: 'ul',
14312                 cls: 'list-group'
14313             }
14314         ]
14315     },
14316     
14317     listItemRadio : {
14318         tag: 'li',
14319         cls: 'list-group-item',
14320         cn: [
14321             {
14322                 tag: 'span',
14323                 cls: 'roo-combobox-list-group-item-value'
14324             },
14325             {
14326                 tag: 'div',
14327                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14328                 cn: [
14329                     {
14330                         tag: 'input',
14331                         type: 'radio'
14332                     },
14333                     {
14334                         tag: 'label'
14335                     }
14336                 ]
14337             }
14338         ]
14339     },
14340     
14341     listItemCheckbox : {
14342         tag: 'li',
14343         cls: 'list-group-item',
14344         cn: [
14345             {
14346                 tag: 'span',
14347                 cls: 'roo-combobox-list-group-item-value'
14348             },
14349             {
14350                 tag: 'div',
14351                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14352                 cn: [
14353                     {
14354                         tag: 'input',
14355                         type: 'checkbox'
14356                     },
14357                     {
14358                         tag: 'label'
14359                     }
14360                 ]
14361             }
14362         ]
14363     },
14364     
14365     emptyResult : {
14366         tag: 'div',
14367         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14368     },
14369     
14370     footer : {
14371         tag: 'div',
14372         cls: 'modal-footer',
14373         cn: [
14374             {
14375                 tag: 'div',
14376                 cls: 'row',
14377                 cn: [
14378                     {
14379                         tag: 'div',
14380                         cls: 'col-xs-6 text-left',
14381                         cn: {
14382                             tag: 'button',
14383                             cls: 'btn btn-danger roo-touch-view-cancel',
14384                             html: 'Cancel'
14385                         }
14386                     },
14387                     {
14388                         tag: 'div',
14389                         cls: 'col-xs-6 text-right',
14390                         cn: {
14391                             tag: 'button',
14392                             cls: 'btn btn-success roo-touch-view-ok',
14393                             html: 'OK'
14394                         }
14395                     }
14396                 ]
14397             }
14398         ]
14399         
14400     }
14401 });
14402
14403 Roo.apply(Roo.bootstrap.ComboBox,  {
14404     
14405     touchViewTemplate : {
14406         tag: 'div',
14407         cls: 'modal fade roo-combobox-touch-view',
14408         cn: [
14409             {
14410                 tag: 'div',
14411                 cls: 'modal-dialog',
14412                 style : 'position:fixed', // we have to fix position....
14413                 cn: [
14414                     {
14415                         tag: 'div',
14416                         cls: 'modal-content',
14417                         cn: [
14418                             Roo.bootstrap.ComboBox.header,
14419                             Roo.bootstrap.ComboBox.body,
14420                             Roo.bootstrap.ComboBox.footer
14421                         ]
14422                     }
14423                 ]
14424             }
14425         ]
14426     }
14427 });/*
14428  * Based on:
14429  * Ext JS Library 1.1.1
14430  * Copyright(c) 2006-2007, Ext JS, LLC.
14431  *
14432  * Originally Released Under LGPL - original licence link has changed is not relivant.
14433  *
14434  * Fork - LGPL
14435  * <script type="text/javascript">
14436  */
14437
14438 /**
14439  * @class Roo.View
14440  * @extends Roo.util.Observable
14441  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14442  * This class also supports single and multi selection modes. <br>
14443  * Create a data model bound view:
14444  <pre><code>
14445  var store = new Roo.data.Store(...);
14446
14447  var view = new Roo.View({
14448     el : "my-element",
14449     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14450  
14451     singleSelect: true,
14452     selectedClass: "ydataview-selected",
14453     store: store
14454  });
14455
14456  // listen for node click?
14457  view.on("click", function(vw, index, node, e){
14458  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14459  });
14460
14461  // load XML data
14462  dataModel.load("foobar.xml");
14463  </code></pre>
14464  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14465  * <br><br>
14466  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14467  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14468  * 
14469  * Note: old style constructor is still suported (container, template, config)
14470  * 
14471  * @constructor
14472  * Create a new View
14473  * @param {Object} config The config object
14474  * 
14475  */
14476 Roo.View = function(config, depreciated_tpl, depreciated_config){
14477     
14478     this.parent = false;
14479     
14480     if (typeof(depreciated_tpl) == 'undefined') {
14481         // new way.. - universal constructor.
14482         Roo.apply(this, config);
14483         this.el  = Roo.get(this.el);
14484     } else {
14485         // old format..
14486         this.el  = Roo.get(config);
14487         this.tpl = depreciated_tpl;
14488         Roo.apply(this, depreciated_config);
14489     }
14490     this.wrapEl  = this.el.wrap().wrap();
14491     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14492     
14493     
14494     if(typeof(this.tpl) == "string"){
14495         this.tpl = new Roo.Template(this.tpl);
14496     } else {
14497         // support xtype ctors..
14498         this.tpl = new Roo.factory(this.tpl, Roo);
14499     }
14500     
14501     
14502     this.tpl.compile();
14503     
14504     /** @private */
14505     this.addEvents({
14506         /**
14507          * @event beforeclick
14508          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
14515         /**
14516          * @event click
14517          * Fires when a template node is 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             "click" : true,
14524         /**
14525          * @event dblclick
14526          * Fires when a template node is double 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             "dblclick" : true,
14533         /**
14534          * @event contextmenu
14535          * Fires when a template node is right clicked.
14536          * @param {Roo.View} this
14537          * @param {Number} index The index of the target node
14538          * @param {HTMLElement} node The target node
14539          * @param {Roo.EventObject} e The raw event object
14540          */
14541             "contextmenu" : true,
14542         /**
14543          * @event selectionchange
14544          * Fires when the selected nodes change.
14545          * @param {Roo.View} this
14546          * @param {Array} selections Array of the selected nodes
14547          */
14548             "selectionchange" : true,
14549     
14550         /**
14551          * @event beforeselect
14552          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14553          * @param {Roo.View} this
14554          * @param {HTMLElement} node The node to be selected
14555          * @param {Array} selections Array of currently selected nodes
14556          */
14557             "beforeselect" : true,
14558         /**
14559          * @event preparedata
14560          * Fires on every row to render, to allow you to change the data.
14561          * @param {Roo.View} this
14562          * @param {Object} data to be rendered (change this)
14563          */
14564           "preparedata" : true
14565           
14566           
14567         });
14568
14569
14570
14571     this.el.on({
14572         "click": this.onClick,
14573         "dblclick": this.onDblClick,
14574         "contextmenu": this.onContextMenu,
14575         scope:this
14576     });
14577
14578     this.selections = [];
14579     this.nodes = [];
14580     this.cmp = new Roo.CompositeElementLite([]);
14581     if(this.store){
14582         this.store = Roo.factory(this.store, Roo.data);
14583         this.setStore(this.store, true);
14584     }
14585     
14586     if ( this.footer && this.footer.xtype) {
14587            
14588          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14589         
14590         this.footer.dataSource = this.store;
14591         this.footer.container = fctr;
14592         this.footer = Roo.factory(this.footer, Roo);
14593         fctr.insertFirst(this.el);
14594         
14595         // this is a bit insane - as the paging toolbar seems to detach the el..
14596 //        dom.parentNode.parentNode.parentNode
14597          // they get detached?
14598     }
14599     
14600     
14601     Roo.View.superclass.constructor.call(this);
14602     
14603     
14604 };
14605
14606 Roo.extend(Roo.View, Roo.util.Observable, {
14607     
14608      /**
14609      * @cfg {Roo.data.Store} store Data store to load data from.
14610      */
14611     store : false,
14612     
14613     /**
14614      * @cfg {String|Roo.Element} el The container element.
14615      */
14616     el : '',
14617     
14618     /**
14619      * @cfg {String|Roo.Template} tpl The template used by this View 
14620      */
14621     tpl : false,
14622     /**
14623      * @cfg {String} dataName the named area of the template to use as the data area
14624      *                          Works with domtemplates roo-name="name"
14625      */
14626     dataName: false,
14627     /**
14628      * @cfg {String} selectedClass The css class to add to selected nodes
14629      */
14630     selectedClass : "x-view-selected",
14631      /**
14632      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14633      */
14634     emptyText : "",
14635     
14636     /**
14637      * @cfg {String} text to display on mask (default Loading)
14638      */
14639     mask : false,
14640     /**
14641      * @cfg {Boolean} multiSelect Allow multiple selection
14642      */
14643     multiSelect : false,
14644     /**
14645      * @cfg {Boolean} singleSelect Allow single selection
14646      */
14647     singleSelect:  false,
14648     
14649     /**
14650      * @cfg {Boolean} toggleSelect - selecting 
14651      */
14652     toggleSelect : false,
14653     
14654     /**
14655      * @cfg {Boolean} tickable - selecting 
14656      */
14657     tickable : false,
14658     
14659     /**
14660      * Returns the element this view is bound to.
14661      * @return {Roo.Element}
14662      */
14663     getEl : function(){
14664         return this.wrapEl;
14665     },
14666     
14667     
14668
14669     /**
14670      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14671      */
14672     refresh : function(){
14673         //Roo.log('refresh');
14674         var t = this.tpl;
14675         
14676         // if we are using something like 'domtemplate', then
14677         // the what gets used is:
14678         // t.applySubtemplate(NAME, data, wrapping data..)
14679         // the outer template then get' applied with
14680         //     the store 'extra data'
14681         // and the body get's added to the
14682         //      roo-name="data" node?
14683         //      <span class='roo-tpl-{name}'></span> ?????
14684         
14685         
14686         
14687         this.clearSelections();
14688         this.el.update("");
14689         var html = [];
14690         var records = this.store.getRange();
14691         if(records.length < 1) {
14692             
14693             // is this valid??  = should it render a template??
14694             
14695             this.el.update(this.emptyText);
14696             return;
14697         }
14698         var el = this.el;
14699         if (this.dataName) {
14700             this.el.update(t.apply(this.store.meta)); //????
14701             el = this.el.child('.roo-tpl-' + this.dataName);
14702         }
14703         
14704         for(var i = 0, len = records.length; i < len; i++){
14705             var data = this.prepareData(records[i].data, i, records[i]);
14706             this.fireEvent("preparedata", this, data, i, records[i]);
14707             
14708             var d = Roo.apply({}, data);
14709             
14710             if(this.tickable){
14711                 Roo.apply(d, {'roo-id' : Roo.id()});
14712                 
14713                 var _this = this;
14714             
14715                 Roo.each(this.parent.item, function(item){
14716                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14717                         return;
14718                     }
14719                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14720                 });
14721             }
14722             
14723             html[html.length] = Roo.util.Format.trim(
14724                 this.dataName ?
14725                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14726                     t.apply(d)
14727             );
14728         }
14729         
14730         
14731         
14732         el.update(html.join(""));
14733         this.nodes = el.dom.childNodes;
14734         this.updateIndexes(0);
14735     },
14736     
14737
14738     /**
14739      * Function to override to reformat the data that is sent to
14740      * the template for each node.
14741      * DEPRICATED - use the preparedata event handler.
14742      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14743      * a JSON object for an UpdateManager bound view).
14744      */
14745     prepareData : function(data, index, record)
14746     {
14747         this.fireEvent("preparedata", this, data, index, record);
14748         return data;
14749     },
14750
14751     onUpdate : function(ds, record){
14752         // Roo.log('on update');   
14753         this.clearSelections();
14754         var index = this.store.indexOf(record);
14755         var n = this.nodes[index];
14756         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14757         n.parentNode.removeChild(n);
14758         this.updateIndexes(index, index);
14759     },
14760
14761     
14762     
14763 // --------- FIXME     
14764     onAdd : function(ds, records, index)
14765     {
14766         //Roo.log(['on Add', ds, records, index] );        
14767         this.clearSelections();
14768         if(this.nodes.length == 0){
14769             this.refresh();
14770             return;
14771         }
14772         var n = this.nodes[index];
14773         for(var i = 0, len = records.length; i < len; i++){
14774             var d = this.prepareData(records[i].data, i, records[i]);
14775             if(n){
14776                 this.tpl.insertBefore(n, d);
14777             }else{
14778                 
14779                 this.tpl.append(this.el, d);
14780             }
14781         }
14782         this.updateIndexes(index);
14783     },
14784
14785     onRemove : function(ds, record, index){
14786        // Roo.log('onRemove');
14787         this.clearSelections();
14788         var el = this.dataName  ?
14789             this.el.child('.roo-tpl-' + this.dataName) :
14790             this.el; 
14791         
14792         el.dom.removeChild(this.nodes[index]);
14793         this.updateIndexes(index);
14794     },
14795
14796     /**
14797      * Refresh an individual node.
14798      * @param {Number} index
14799      */
14800     refreshNode : function(index){
14801         this.onUpdate(this.store, this.store.getAt(index));
14802     },
14803
14804     updateIndexes : function(startIndex, endIndex){
14805         var ns = this.nodes;
14806         startIndex = startIndex || 0;
14807         endIndex = endIndex || ns.length - 1;
14808         for(var i = startIndex; i <= endIndex; i++){
14809             ns[i].nodeIndex = i;
14810         }
14811     },
14812
14813     /**
14814      * Changes the data store this view uses and refresh the view.
14815      * @param {Store} store
14816      */
14817     setStore : function(store, initial){
14818         if(!initial && this.store){
14819             this.store.un("datachanged", this.refresh);
14820             this.store.un("add", this.onAdd);
14821             this.store.un("remove", this.onRemove);
14822             this.store.un("update", this.onUpdate);
14823             this.store.un("clear", this.refresh);
14824             this.store.un("beforeload", this.onBeforeLoad);
14825             this.store.un("load", this.onLoad);
14826             this.store.un("loadexception", this.onLoad);
14827         }
14828         if(store){
14829           
14830             store.on("datachanged", this.refresh, this);
14831             store.on("add", this.onAdd, this);
14832             store.on("remove", this.onRemove, this);
14833             store.on("update", this.onUpdate, this);
14834             store.on("clear", this.refresh, this);
14835             store.on("beforeload", this.onBeforeLoad, this);
14836             store.on("load", this.onLoad, this);
14837             store.on("loadexception", this.onLoad, this);
14838         }
14839         
14840         if(store){
14841             this.refresh();
14842         }
14843     },
14844     /**
14845      * onbeforeLoad - masks the loading area.
14846      *
14847      */
14848     onBeforeLoad : function(store,opts)
14849     {
14850          //Roo.log('onBeforeLoad');   
14851         if (!opts.add) {
14852             this.el.update("");
14853         }
14854         this.el.mask(this.mask ? this.mask : "Loading" ); 
14855     },
14856     onLoad : function ()
14857     {
14858         this.el.unmask();
14859     },
14860     
14861
14862     /**
14863      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14864      * @param {HTMLElement} node
14865      * @return {HTMLElement} The template node
14866      */
14867     findItemFromChild : function(node){
14868         var el = this.dataName  ?
14869             this.el.child('.roo-tpl-' + this.dataName,true) :
14870             this.el.dom; 
14871         
14872         if(!node || node.parentNode == el){
14873                     return node;
14874             }
14875             var p = node.parentNode;
14876             while(p && p != el){
14877             if(p.parentNode == el){
14878                 return p;
14879             }
14880             p = p.parentNode;
14881         }
14882             return null;
14883     },
14884
14885     /** @ignore */
14886     onClick : function(e){
14887         var item = this.findItemFromChild(e.getTarget());
14888         if(item){
14889             var index = this.indexOf(item);
14890             if(this.onItemClick(item, index, e) !== false){
14891                 this.fireEvent("click", this, index, item, e);
14892             }
14893         }else{
14894             this.clearSelections();
14895         }
14896     },
14897
14898     /** @ignore */
14899     onContextMenu : function(e){
14900         var item = this.findItemFromChild(e.getTarget());
14901         if(item){
14902             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14903         }
14904     },
14905
14906     /** @ignore */
14907     onDblClick : function(e){
14908         var item = this.findItemFromChild(e.getTarget());
14909         if(item){
14910             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14911         }
14912     },
14913
14914     onItemClick : function(item, index, e)
14915     {
14916         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14917             return false;
14918         }
14919         if (this.toggleSelect) {
14920             var m = this.isSelected(item) ? 'unselect' : 'select';
14921             //Roo.log(m);
14922             var _t = this;
14923             _t[m](item, true, false);
14924             return true;
14925         }
14926         if(this.multiSelect || this.singleSelect){
14927             if(this.multiSelect && e.shiftKey && this.lastSelection){
14928                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14929             }else{
14930                 this.select(item, this.multiSelect && e.ctrlKey);
14931                 this.lastSelection = item;
14932             }
14933             
14934             if(!this.tickable){
14935                 e.preventDefault();
14936             }
14937             
14938         }
14939         return true;
14940     },
14941
14942     /**
14943      * Get the number of selected nodes.
14944      * @return {Number}
14945      */
14946     getSelectionCount : function(){
14947         return this.selections.length;
14948     },
14949
14950     /**
14951      * Get the currently selected nodes.
14952      * @return {Array} An array of HTMLElements
14953      */
14954     getSelectedNodes : function(){
14955         return this.selections;
14956     },
14957
14958     /**
14959      * Get the indexes of the selected nodes.
14960      * @return {Array}
14961      */
14962     getSelectedIndexes : function(){
14963         var indexes = [], s = this.selections;
14964         for(var i = 0, len = s.length; i < len; i++){
14965             indexes.push(s[i].nodeIndex);
14966         }
14967         return indexes;
14968     },
14969
14970     /**
14971      * Clear all selections
14972      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14973      */
14974     clearSelections : function(suppressEvent){
14975         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14976             this.cmp.elements = this.selections;
14977             this.cmp.removeClass(this.selectedClass);
14978             this.selections = [];
14979             if(!suppressEvent){
14980                 this.fireEvent("selectionchange", this, this.selections);
14981             }
14982         }
14983     },
14984
14985     /**
14986      * Returns true if the passed node is selected
14987      * @param {HTMLElement/Number} node The node or node index
14988      * @return {Boolean}
14989      */
14990     isSelected : function(node){
14991         var s = this.selections;
14992         if(s.length < 1){
14993             return false;
14994         }
14995         node = this.getNode(node);
14996         return s.indexOf(node) !== -1;
14997     },
14998
14999     /**
15000      * Selects nodes.
15001      * @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
15002      * @param {Boolean} keepExisting (optional) true to keep existing selections
15003      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15004      */
15005     select : function(nodeInfo, keepExisting, suppressEvent){
15006         if(nodeInfo instanceof Array){
15007             if(!keepExisting){
15008                 this.clearSelections(true);
15009             }
15010             for(var i = 0, len = nodeInfo.length; i < len; i++){
15011                 this.select(nodeInfo[i], true, true);
15012             }
15013             return;
15014         } 
15015         var node = this.getNode(nodeInfo);
15016         if(!node || this.isSelected(node)){
15017             return; // already selected.
15018         }
15019         if(!keepExisting){
15020             this.clearSelections(true);
15021         }
15022         
15023         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15024             Roo.fly(node).addClass(this.selectedClass);
15025             this.selections.push(node);
15026             if(!suppressEvent){
15027                 this.fireEvent("selectionchange", this, this.selections);
15028             }
15029         }
15030         
15031         
15032     },
15033       /**
15034      * Unselects nodes.
15035      * @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
15036      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15037      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15038      */
15039     unselect : function(nodeInfo, keepExisting, suppressEvent)
15040     {
15041         if(nodeInfo instanceof Array){
15042             Roo.each(this.selections, function(s) {
15043                 this.unselect(s, nodeInfo);
15044             }, this);
15045             return;
15046         }
15047         var node = this.getNode(nodeInfo);
15048         if(!node || !this.isSelected(node)){
15049             //Roo.log("not selected");
15050             return; // not selected.
15051         }
15052         // fireevent???
15053         var ns = [];
15054         Roo.each(this.selections, function(s) {
15055             if (s == node ) {
15056                 Roo.fly(node).removeClass(this.selectedClass);
15057
15058                 return;
15059             }
15060             ns.push(s);
15061         },this);
15062         
15063         this.selections= ns;
15064         this.fireEvent("selectionchange", this, this.selections);
15065     },
15066
15067     /**
15068      * Gets a template node.
15069      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15070      * @return {HTMLElement} The node or null if it wasn't found
15071      */
15072     getNode : function(nodeInfo){
15073         if(typeof nodeInfo == "string"){
15074             return document.getElementById(nodeInfo);
15075         }else if(typeof nodeInfo == "number"){
15076             return this.nodes[nodeInfo];
15077         }
15078         return nodeInfo;
15079     },
15080
15081     /**
15082      * Gets a range template nodes.
15083      * @param {Number} startIndex
15084      * @param {Number} endIndex
15085      * @return {Array} An array of nodes
15086      */
15087     getNodes : function(start, end){
15088         var ns = this.nodes;
15089         start = start || 0;
15090         end = typeof end == "undefined" ? ns.length - 1 : end;
15091         var nodes = [];
15092         if(start <= end){
15093             for(var i = start; i <= end; i++){
15094                 nodes.push(ns[i]);
15095             }
15096         } else{
15097             for(var i = start; i >= end; i--){
15098                 nodes.push(ns[i]);
15099             }
15100         }
15101         return nodes;
15102     },
15103
15104     /**
15105      * Finds the index of the passed node
15106      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15107      * @return {Number} The index of the node or -1
15108      */
15109     indexOf : function(node){
15110         node = this.getNode(node);
15111         if(typeof node.nodeIndex == "number"){
15112             return node.nodeIndex;
15113         }
15114         var ns = this.nodes;
15115         for(var i = 0, len = ns.length; i < len; i++){
15116             if(ns[i] == node){
15117                 return i;
15118             }
15119         }
15120         return -1;
15121     }
15122 });
15123 /*
15124  * - LGPL
15125  *
15126  * based on jquery fullcalendar
15127  * 
15128  */
15129
15130 Roo.bootstrap = Roo.bootstrap || {};
15131 /**
15132  * @class Roo.bootstrap.Calendar
15133  * @extends Roo.bootstrap.Component
15134  * Bootstrap Calendar class
15135  * @cfg {Boolean} loadMask (true|false) default false
15136  * @cfg {Object} header generate the user specific header of the calendar, default false
15137
15138  * @constructor
15139  * Create a new Container
15140  * @param {Object} config The config object
15141  */
15142
15143
15144
15145 Roo.bootstrap.Calendar = function(config){
15146     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15147      this.addEvents({
15148         /**
15149              * @event select
15150              * Fires when a date is selected
15151              * @param {DatePicker} this
15152              * @param {Date} date The selected date
15153              */
15154         'select': true,
15155         /**
15156              * @event monthchange
15157              * Fires when the displayed month changes 
15158              * @param {DatePicker} this
15159              * @param {Date} date The selected month
15160              */
15161         'monthchange': true,
15162         /**
15163              * @event evententer
15164              * Fires when mouse over an event
15165              * @param {Calendar} this
15166              * @param {event} Event
15167              */
15168         'evententer': true,
15169         /**
15170              * @event eventleave
15171              * Fires when the mouse leaves an
15172              * @param {Calendar} this
15173              * @param {event}
15174              */
15175         'eventleave': true,
15176         /**
15177              * @event eventclick
15178              * Fires when the mouse click an
15179              * @param {Calendar} this
15180              * @param {event}
15181              */
15182         'eventclick': true
15183         
15184     });
15185
15186 };
15187
15188 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15189     
15190      /**
15191      * @cfg {Number} startDay
15192      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15193      */
15194     startDay : 0,
15195     
15196     loadMask : false,
15197     
15198     header : false,
15199       
15200     getAutoCreate : function(){
15201         
15202         
15203         var fc_button = function(name, corner, style, content ) {
15204             return Roo.apply({},{
15205                 tag : 'span',
15206                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15207                          (corner.length ?
15208                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15209                             ''
15210                         ),
15211                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15212                 unselectable: 'on'
15213             });
15214         };
15215         
15216         var header = {};
15217         
15218         if(!this.header){
15219             header = {
15220                 tag : 'table',
15221                 cls : 'fc-header',
15222                 style : 'width:100%',
15223                 cn : [
15224                     {
15225                         tag: 'tr',
15226                         cn : [
15227                             {
15228                                 tag : 'td',
15229                                 cls : 'fc-header-left',
15230                                 cn : [
15231                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15232                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15233                                     { tag: 'span', cls: 'fc-header-space' },
15234                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15235
15236
15237                                 ]
15238                             },
15239
15240                             {
15241                                 tag : 'td',
15242                                 cls : 'fc-header-center',
15243                                 cn : [
15244                                     {
15245                                         tag: 'span',
15246                                         cls: 'fc-header-title',
15247                                         cn : {
15248                                             tag: 'H2',
15249                                             html : 'month / year'
15250                                         }
15251                                     }
15252
15253                                 ]
15254                             },
15255                             {
15256                                 tag : 'td',
15257                                 cls : 'fc-header-right',
15258                                 cn : [
15259                               /*      fc_button('month', 'left', '', 'month' ),
15260                                     fc_button('week', '', '', 'week' ),
15261                                     fc_button('day', 'right', '', 'day' )
15262                                 */    
15263
15264                                 ]
15265                             }
15266
15267                         ]
15268                     }
15269                 ]
15270             };
15271         }
15272         
15273         header = this.header;
15274         
15275        
15276         var cal_heads = function() {
15277             var ret = [];
15278             // fixme - handle this.
15279             
15280             for (var i =0; i < Date.dayNames.length; i++) {
15281                 var d = Date.dayNames[i];
15282                 ret.push({
15283                     tag: 'th',
15284                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15285                     html : d.substring(0,3)
15286                 });
15287                 
15288             }
15289             ret[0].cls += ' fc-first';
15290             ret[6].cls += ' fc-last';
15291             return ret;
15292         };
15293         var cal_cell = function(n) {
15294             return  {
15295                 tag: 'td',
15296                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15297                 cn : [
15298                     {
15299                         cn : [
15300                             {
15301                                 cls: 'fc-day-number',
15302                                 html: 'D'
15303                             },
15304                             {
15305                                 cls: 'fc-day-content',
15306                              
15307                                 cn : [
15308                                      {
15309                                         style: 'position: relative;' // height: 17px;
15310                                     }
15311                                 ]
15312                             }
15313                             
15314                             
15315                         ]
15316                     }
15317                 ]
15318                 
15319             }
15320         };
15321         var cal_rows = function() {
15322             
15323             var ret = [];
15324             for (var r = 0; r < 6; r++) {
15325                 var row= {
15326                     tag : 'tr',
15327                     cls : 'fc-week',
15328                     cn : []
15329                 };
15330                 
15331                 for (var i =0; i < Date.dayNames.length; i++) {
15332                     var d = Date.dayNames[i];
15333                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15334
15335                 }
15336                 row.cn[0].cls+=' fc-first';
15337                 row.cn[0].cn[0].style = 'min-height:90px';
15338                 row.cn[6].cls+=' fc-last';
15339                 ret.push(row);
15340                 
15341             }
15342             ret[0].cls += ' fc-first';
15343             ret[4].cls += ' fc-prev-last';
15344             ret[5].cls += ' fc-last';
15345             return ret;
15346             
15347         };
15348         
15349         var cal_table = {
15350             tag: 'table',
15351             cls: 'fc-border-separate',
15352             style : 'width:100%',
15353             cellspacing  : 0,
15354             cn : [
15355                 { 
15356                     tag: 'thead',
15357                     cn : [
15358                         { 
15359                             tag: 'tr',
15360                             cls : 'fc-first fc-last',
15361                             cn : cal_heads()
15362                         }
15363                     ]
15364                 },
15365                 { 
15366                     tag: 'tbody',
15367                     cn : cal_rows()
15368                 }
15369                   
15370             ]
15371         };
15372          
15373          var cfg = {
15374             cls : 'fc fc-ltr',
15375             cn : [
15376                 header,
15377                 {
15378                     cls : 'fc-content',
15379                     style : "position: relative;",
15380                     cn : [
15381                         {
15382                             cls : 'fc-view fc-view-month fc-grid',
15383                             style : 'position: relative',
15384                             unselectable : 'on',
15385                             cn : [
15386                                 {
15387                                     cls : 'fc-event-container',
15388                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15389                                 },
15390                                 cal_table
15391                             ]
15392                         }
15393                     ]
15394     
15395                 }
15396            ] 
15397             
15398         };
15399         
15400          
15401         
15402         return cfg;
15403     },
15404     
15405     
15406     initEvents : function()
15407     {
15408         if(!this.store){
15409             throw "can not find store for calendar";
15410         }
15411         
15412         var mark = {
15413             tag: "div",
15414             cls:"x-dlg-mask",
15415             style: "text-align:center",
15416             cn: [
15417                 {
15418                     tag: "div",
15419                     style: "background-color:white;width:50%;margin:250 auto",
15420                     cn: [
15421                         {
15422                             tag: "img",
15423                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15424                         },
15425                         {
15426                             tag: "span",
15427                             html: "Loading"
15428                         }
15429                         
15430                     ]
15431                 }
15432             ]
15433         };
15434         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15435         
15436         var size = this.el.select('.fc-content', true).first().getSize();
15437         this.maskEl.setSize(size.width, size.height);
15438         this.maskEl.enableDisplayMode("block");
15439         if(!this.loadMask){
15440             this.maskEl.hide();
15441         }
15442         
15443         this.store = Roo.factory(this.store, Roo.data);
15444         this.store.on('load', this.onLoad, this);
15445         this.store.on('beforeload', this.onBeforeLoad, this);
15446         
15447         this.resize();
15448         
15449         this.cells = this.el.select('.fc-day',true);
15450         //Roo.log(this.cells);
15451         this.textNodes = this.el.query('.fc-day-number');
15452         this.cells.addClassOnOver('fc-state-hover');
15453         
15454         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15455         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15456         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15457         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15458         
15459         this.on('monthchange', this.onMonthChange, this);
15460         
15461         this.update(new Date().clearTime());
15462     },
15463     
15464     resize : function() {
15465         var sz  = this.el.getSize();
15466         
15467         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15468         this.el.select('.fc-day-content div',true).setHeight(34);
15469     },
15470     
15471     
15472     // private
15473     showPrevMonth : function(e){
15474         this.update(this.activeDate.add("mo", -1));
15475     },
15476     showToday : function(e){
15477         this.update(new Date().clearTime());
15478     },
15479     // private
15480     showNextMonth : function(e){
15481         this.update(this.activeDate.add("mo", 1));
15482     },
15483
15484     // private
15485     showPrevYear : function(){
15486         this.update(this.activeDate.add("y", -1));
15487     },
15488
15489     // private
15490     showNextYear : function(){
15491         this.update(this.activeDate.add("y", 1));
15492     },
15493
15494     
15495    // private
15496     update : function(date)
15497     {
15498         var vd = this.activeDate;
15499         this.activeDate = date;
15500 //        if(vd && this.el){
15501 //            var t = date.getTime();
15502 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15503 //                Roo.log('using add remove');
15504 //                
15505 //                this.fireEvent('monthchange', this, date);
15506 //                
15507 //                this.cells.removeClass("fc-state-highlight");
15508 //                this.cells.each(function(c){
15509 //                   if(c.dateValue == t){
15510 //                       c.addClass("fc-state-highlight");
15511 //                       setTimeout(function(){
15512 //                            try{c.dom.firstChild.focus();}catch(e){}
15513 //                       }, 50);
15514 //                       return false;
15515 //                   }
15516 //                   return true;
15517 //                });
15518 //                return;
15519 //            }
15520 //        }
15521         
15522         var days = date.getDaysInMonth();
15523         
15524         var firstOfMonth = date.getFirstDateOfMonth();
15525         var startingPos = firstOfMonth.getDay()-this.startDay;
15526         
15527         if(startingPos < this.startDay){
15528             startingPos += 7;
15529         }
15530         
15531         var pm = date.add(Date.MONTH, -1);
15532         var prevStart = pm.getDaysInMonth()-startingPos;
15533 //        
15534         this.cells = this.el.select('.fc-day',true);
15535         this.textNodes = this.el.query('.fc-day-number');
15536         this.cells.addClassOnOver('fc-state-hover');
15537         
15538         var cells = this.cells.elements;
15539         var textEls = this.textNodes;
15540         
15541         Roo.each(cells, function(cell){
15542             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15543         });
15544         
15545         days += startingPos;
15546
15547         // convert everything to numbers so it's fast
15548         var day = 86400000;
15549         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15550         //Roo.log(d);
15551         //Roo.log(pm);
15552         //Roo.log(prevStart);
15553         
15554         var today = new Date().clearTime().getTime();
15555         var sel = date.clearTime().getTime();
15556         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15557         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15558         var ddMatch = this.disabledDatesRE;
15559         var ddText = this.disabledDatesText;
15560         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15561         var ddaysText = this.disabledDaysText;
15562         var format = this.format;
15563         
15564         var setCellClass = function(cal, cell){
15565             cell.row = 0;
15566             cell.events = [];
15567             cell.more = [];
15568             //Roo.log('set Cell Class');
15569             cell.title = "";
15570             var t = d.getTime();
15571             
15572             //Roo.log(d);
15573             
15574             cell.dateValue = t;
15575             if(t == today){
15576                 cell.className += " fc-today";
15577                 cell.className += " fc-state-highlight";
15578                 cell.title = cal.todayText;
15579             }
15580             if(t == sel){
15581                 // disable highlight in other month..
15582                 //cell.className += " fc-state-highlight";
15583                 
15584             }
15585             // disabling
15586             if(t < min) {
15587                 cell.className = " fc-state-disabled";
15588                 cell.title = cal.minText;
15589                 return;
15590             }
15591             if(t > max) {
15592                 cell.className = " fc-state-disabled";
15593                 cell.title = cal.maxText;
15594                 return;
15595             }
15596             if(ddays){
15597                 if(ddays.indexOf(d.getDay()) != -1){
15598                     cell.title = ddaysText;
15599                     cell.className = " fc-state-disabled";
15600                 }
15601             }
15602             if(ddMatch && format){
15603                 var fvalue = d.dateFormat(format);
15604                 if(ddMatch.test(fvalue)){
15605                     cell.title = ddText.replace("%0", fvalue);
15606                     cell.className = " fc-state-disabled";
15607                 }
15608             }
15609             
15610             if (!cell.initialClassName) {
15611                 cell.initialClassName = cell.dom.className;
15612             }
15613             
15614             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15615         };
15616
15617         var i = 0;
15618         
15619         for(; i < startingPos; i++) {
15620             textEls[i].innerHTML = (++prevStart);
15621             d.setDate(d.getDate()+1);
15622             
15623             cells[i].className = "fc-past fc-other-month";
15624             setCellClass(this, cells[i]);
15625         }
15626         
15627         var intDay = 0;
15628         
15629         for(; i < days; i++){
15630             intDay = i - startingPos + 1;
15631             textEls[i].innerHTML = (intDay);
15632             d.setDate(d.getDate()+1);
15633             
15634             cells[i].className = ''; // "x-date-active";
15635             setCellClass(this, cells[i]);
15636         }
15637         var extraDays = 0;
15638         
15639         for(; i < 42; i++) {
15640             textEls[i].innerHTML = (++extraDays);
15641             d.setDate(d.getDate()+1);
15642             
15643             cells[i].className = "fc-future fc-other-month";
15644             setCellClass(this, cells[i]);
15645         }
15646         
15647         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15648         
15649         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15650         
15651         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15652         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15653         
15654         if(totalRows != 6){
15655             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15656             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15657         }
15658         
15659         this.fireEvent('monthchange', this, date);
15660         
15661         
15662         /*
15663         if(!this.internalRender){
15664             var main = this.el.dom.firstChild;
15665             var w = main.offsetWidth;
15666             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15667             Roo.fly(main).setWidth(w);
15668             this.internalRender = true;
15669             // opera does not respect the auto grow header center column
15670             // then, after it gets a width opera refuses to recalculate
15671             // without a second pass
15672             if(Roo.isOpera && !this.secondPass){
15673                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15674                 this.secondPass = true;
15675                 this.update.defer(10, this, [date]);
15676             }
15677         }
15678         */
15679         
15680     },
15681     
15682     findCell : function(dt) {
15683         dt = dt.clearTime().getTime();
15684         var ret = false;
15685         this.cells.each(function(c){
15686             //Roo.log("check " +c.dateValue + '?=' + dt);
15687             if(c.dateValue == dt){
15688                 ret = c;
15689                 return false;
15690             }
15691             return true;
15692         });
15693         
15694         return ret;
15695     },
15696     
15697     findCells : function(ev) {
15698         var s = ev.start.clone().clearTime().getTime();
15699        // Roo.log(s);
15700         var e= ev.end.clone().clearTime().getTime();
15701        // Roo.log(e);
15702         var ret = [];
15703         this.cells.each(function(c){
15704              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15705             
15706             if(c.dateValue > e){
15707                 return ;
15708             }
15709             if(c.dateValue < s){
15710                 return ;
15711             }
15712             ret.push(c);
15713         });
15714         
15715         return ret;    
15716     },
15717     
15718 //    findBestRow: function(cells)
15719 //    {
15720 //        var ret = 0;
15721 //        
15722 //        for (var i =0 ; i < cells.length;i++) {
15723 //            ret  = Math.max(cells[i].rows || 0,ret);
15724 //        }
15725 //        return ret;
15726 //        
15727 //    },
15728     
15729     
15730     addItem : function(ev)
15731     {
15732         // look for vertical location slot in
15733         var cells = this.findCells(ev);
15734         
15735 //        ev.row = this.findBestRow(cells);
15736         
15737         // work out the location.
15738         
15739         var crow = false;
15740         var rows = [];
15741         for(var i =0; i < cells.length; i++) {
15742             
15743             cells[i].row = cells[0].row;
15744             
15745             if(i == 0){
15746                 cells[i].row = cells[i].row + 1;
15747             }
15748             
15749             if (!crow) {
15750                 crow = {
15751                     start : cells[i],
15752                     end :  cells[i]
15753                 };
15754                 continue;
15755             }
15756             if (crow.start.getY() == cells[i].getY()) {
15757                 // on same row.
15758                 crow.end = cells[i];
15759                 continue;
15760             }
15761             // different row.
15762             rows.push(crow);
15763             crow = {
15764                 start: cells[i],
15765                 end : cells[i]
15766             };
15767             
15768         }
15769         
15770         rows.push(crow);
15771         ev.els = [];
15772         ev.rows = rows;
15773         ev.cells = cells;
15774         
15775         cells[0].events.push(ev);
15776         
15777         this.calevents.push(ev);
15778     },
15779     
15780     clearEvents: function() {
15781         
15782         if(!this.calevents){
15783             return;
15784         }
15785         
15786         Roo.each(this.cells.elements, function(c){
15787             c.row = 0;
15788             c.events = [];
15789             c.more = [];
15790         });
15791         
15792         Roo.each(this.calevents, function(e) {
15793             Roo.each(e.els, function(el) {
15794                 el.un('mouseenter' ,this.onEventEnter, this);
15795                 el.un('mouseleave' ,this.onEventLeave, this);
15796                 el.remove();
15797             },this);
15798         },this);
15799         
15800         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15801             e.remove();
15802         });
15803         
15804     },
15805     
15806     renderEvents: function()
15807     {   
15808         var _this = this;
15809         
15810         this.cells.each(function(c) {
15811             
15812             if(c.row < 5){
15813                 return;
15814             }
15815             
15816             var ev = c.events;
15817             
15818             var r = 4;
15819             if(c.row != c.events.length){
15820                 r = 4 - (4 - (c.row - c.events.length));
15821             }
15822             
15823             c.events = ev.slice(0, r);
15824             c.more = ev.slice(r);
15825             
15826             if(c.more.length && c.more.length == 1){
15827                 c.events.push(c.more.pop());
15828             }
15829             
15830             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15831             
15832         });
15833             
15834         this.cells.each(function(c) {
15835             
15836             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15837             
15838             
15839             for (var e = 0; e < c.events.length; e++){
15840                 var ev = c.events[e];
15841                 var rows = ev.rows;
15842                 
15843                 for(var i = 0; i < rows.length; i++) {
15844                 
15845                     // how many rows should it span..
15846
15847                     var  cfg = {
15848                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15849                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15850
15851                         unselectable : "on",
15852                         cn : [
15853                             {
15854                                 cls: 'fc-event-inner',
15855                                 cn : [
15856     //                                {
15857     //                                  tag:'span',
15858     //                                  cls: 'fc-event-time',
15859     //                                  html : cells.length > 1 ? '' : ev.time
15860     //                                },
15861                                     {
15862                                       tag:'span',
15863                                       cls: 'fc-event-title',
15864                                       html : String.format('{0}', ev.title)
15865                                     }
15866
15867
15868                                 ]
15869                             },
15870                             {
15871                                 cls: 'ui-resizable-handle ui-resizable-e',
15872                                 html : '&nbsp;&nbsp;&nbsp'
15873                             }
15874
15875                         ]
15876                     };
15877
15878                     if (i == 0) {
15879                         cfg.cls += ' fc-event-start';
15880                     }
15881                     if ((i+1) == rows.length) {
15882                         cfg.cls += ' fc-event-end';
15883                     }
15884
15885                     var ctr = _this.el.select('.fc-event-container',true).first();
15886                     var cg = ctr.createChild(cfg);
15887
15888                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15889                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15890
15891                     var r = (c.more.length) ? 1 : 0;
15892                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15893                     cg.setWidth(ebox.right - sbox.x -2);
15894
15895                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15896                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15897                     cg.on('click', _this.onEventClick, _this, ev);
15898
15899                     ev.els.push(cg);
15900                     
15901                 }
15902                 
15903             }
15904             
15905             
15906             if(c.more.length){
15907                 var  cfg = {
15908                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15909                     style : 'position: absolute',
15910                     unselectable : "on",
15911                     cn : [
15912                         {
15913                             cls: 'fc-event-inner',
15914                             cn : [
15915                                 {
15916                                   tag:'span',
15917                                   cls: 'fc-event-title',
15918                                   html : 'More'
15919                                 }
15920
15921
15922                             ]
15923                         },
15924                         {
15925                             cls: 'ui-resizable-handle ui-resizable-e',
15926                             html : '&nbsp;&nbsp;&nbsp'
15927                         }
15928
15929                     ]
15930                 };
15931
15932                 var ctr = _this.el.select('.fc-event-container',true).first();
15933                 var cg = ctr.createChild(cfg);
15934
15935                 var sbox = c.select('.fc-day-content',true).first().getBox();
15936                 var ebox = c.select('.fc-day-content',true).first().getBox();
15937                 //Roo.log(cg);
15938                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15939                 cg.setWidth(ebox.right - sbox.x -2);
15940
15941                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15942                 
15943             }
15944             
15945         });
15946         
15947         
15948         
15949     },
15950     
15951     onEventEnter: function (e, el,event,d) {
15952         this.fireEvent('evententer', this, el, event);
15953     },
15954     
15955     onEventLeave: function (e, el,event,d) {
15956         this.fireEvent('eventleave', this, el, event);
15957     },
15958     
15959     onEventClick: function (e, el,event,d) {
15960         this.fireEvent('eventclick', this, el, event);
15961     },
15962     
15963     onMonthChange: function () {
15964         this.store.load();
15965     },
15966     
15967     onMoreEventClick: function(e, el, more)
15968     {
15969         var _this = this;
15970         
15971         this.calpopover.placement = 'right';
15972         this.calpopover.setTitle('More');
15973         
15974         this.calpopover.setContent('');
15975         
15976         var ctr = this.calpopover.el.select('.popover-content', true).first();
15977         
15978         Roo.each(more, function(m){
15979             var cfg = {
15980                 cls : 'fc-event-hori fc-event-draggable',
15981                 html : m.title
15982             };
15983             var cg = ctr.createChild(cfg);
15984             
15985             cg.on('click', _this.onEventClick, _this, m);
15986         });
15987         
15988         this.calpopover.show(el);
15989         
15990         
15991     },
15992     
15993     onLoad: function () 
15994     {   
15995         this.calevents = [];
15996         var cal = this;
15997         
15998         if(this.store.getCount() > 0){
15999             this.store.data.each(function(d){
16000                cal.addItem({
16001                     id : d.data.id,
16002                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16003                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16004                     time : d.data.start_time,
16005                     title : d.data.title,
16006                     description : d.data.description,
16007                     venue : d.data.venue
16008                 });
16009             });
16010         }
16011         
16012         this.renderEvents();
16013         
16014         if(this.calevents.length && this.loadMask){
16015             this.maskEl.hide();
16016         }
16017     },
16018     
16019     onBeforeLoad: function()
16020     {
16021         this.clearEvents();
16022         if(this.loadMask){
16023             this.maskEl.show();
16024         }
16025     }
16026 });
16027
16028  
16029  /*
16030  * - LGPL
16031  *
16032  * element
16033  * 
16034  */
16035
16036 /**
16037  * @class Roo.bootstrap.Popover
16038  * @extends Roo.bootstrap.Component
16039  * Bootstrap Popover class
16040  * @cfg {String} html contents of the popover   (or false to use children..)
16041  * @cfg {String} title of popover (or false to hide)
16042  * @cfg {String} placement how it is placed
16043  * @cfg {String} trigger click || hover (or false to trigger manually)
16044  * @cfg {String} over what (parent or false to trigger manually.)
16045  * @cfg {Number} delay - delay before showing
16046  
16047  * @constructor
16048  * Create a new Popover
16049  * @param {Object} config The config object
16050  */
16051
16052 Roo.bootstrap.Popover = function(config){
16053     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16054     
16055     this.addEvents({
16056         // raw events
16057          /**
16058          * @event show
16059          * After the popover show
16060          * 
16061          * @param {Roo.bootstrap.Popover} this
16062          */
16063         "show" : true,
16064         /**
16065          * @event hide
16066          * After the popover hide
16067          * 
16068          * @param {Roo.bootstrap.Popover} this
16069          */
16070         "hide" : true
16071     });
16072 };
16073
16074 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16075     
16076     title: 'Fill in a title',
16077     html: false,
16078     
16079     placement : 'right',
16080     trigger : 'hover', // hover
16081     
16082     delay : 0,
16083     
16084     over: 'parent',
16085     
16086     can_build_overlaid : false,
16087     
16088     getChildContainer : function()
16089     {
16090         return this.el.select('.popover-content',true).first();
16091     },
16092     
16093     getAutoCreate : function(){
16094          
16095         var cfg = {
16096            cls : 'popover roo-dynamic',
16097            style: 'display:block',
16098            cn : [
16099                 {
16100                     cls : 'arrow'
16101                 },
16102                 {
16103                     cls : 'popover-inner',
16104                     cn : [
16105                         {
16106                             tag: 'h3',
16107                             cls: 'popover-title',
16108                             html : this.title
16109                         },
16110                         {
16111                             cls : 'popover-content',
16112                             html : this.html
16113                         }
16114                     ]
16115                     
16116                 }
16117            ]
16118         };
16119         
16120         return cfg;
16121     },
16122     setTitle: function(str)
16123     {
16124         this.title = str;
16125         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16126     },
16127     setContent: function(str)
16128     {
16129         this.html = str;
16130         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16131     },
16132     // as it get's added to the bottom of the page.
16133     onRender : function(ct, position)
16134     {
16135         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16136         if(!this.el){
16137             var cfg = Roo.apply({},  this.getAutoCreate());
16138             cfg.id = Roo.id();
16139             
16140             if (this.cls) {
16141                 cfg.cls += ' ' + this.cls;
16142             }
16143             if (this.style) {
16144                 cfg.style = this.style;
16145             }
16146             //Roo.log("adding to ");
16147             this.el = Roo.get(document.body).createChild(cfg, position);
16148 //            Roo.log(this.el);
16149         }
16150         this.initEvents();
16151     },
16152     
16153     initEvents : function()
16154     {
16155         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16156         this.el.enableDisplayMode('block');
16157         this.el.hide();
16158         if (this.over === false) {
16159             return; 
16160         }
16161         if (this.triggers === false) {
16162             return;
16163         }
16164         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16165         var triggers = this.trigger ? this.trigger.split(' ') : [];
16166         Roo.each(triggers, function(trigger) {
16167         
16168             if (trigger == 'click') {
16169                 on_el.on('click', this.toggle, this);
16170             } else if (trigger != 'manual') {
16171                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16172                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16173       
16174                 on_el.on(eventIn  ,this.enter, this);
16175                 on_el.on(eventOut, this.leave, this);
16176             }
16177         }, this);
16178         
16179     },
16180     
16181     
16182     // private
16183     timeout : null,
16184     hoverState : null,
16185     
16186     toggle : function () {
16187         this.hoverState == 'in' ? this.leave() : this.enter();
16188     },
16189     
16190     enter : function () {
16191         
16192         clearTimeout(this.timeout);
16193     
16194         this.hoverState = 'in';
16195     
16196         if (!this.delay || !this.delay.show) {
16197             this.show();
16198             return;
16199         }
16200         var _t = this;
16201         this.timeout = setTimeout(function () {
16202             if (_t.hoverState == 'in') {
16203                 _t.show();
16204             }
16205         }, this.delay.show)
16206     },
16207     
16208     leave : function() {
16209         clearTimeout(this.timeout);
16210     
16211         this.hoverState = 'out';
16212     
16213         if (!this.delay || !this.delay.hide) {
16214             this.hide();
16215             return;
16216         }
16217         var _t = this;
16218         this.timeout = setTimeout(function () {
16219             if (_t.hoverState == 'out') {
16220                 _t.hide();
16221             }
16222         }, this.delay.hide)
16223     },
16224     
16225     show : function (on_el)
16226     {
16227         if (!on_el) {
16228             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16229         }
16230         
16231         // set content.
16232         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16233         if (this.html !== false) {
16234             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16235         }
16236         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16237         if (!this.title.length) {
16238             this.el.select('.popover-title',true).hide();
16239         }
16240         
16241         var placement = typeof this.placement == 'function' ?
16242             this.placement.call(this, this.el, on_el) :
16243             this.placement;
16244             
16245         var autoToken = /\s?auto?\s?/i;
16246         var autoPlace = autoToken.test(placement);
16247         if (autoPlace) {
16248             placement = placement.replace(autoToken, '') || 'top';
16249         }
16250         
16251         //this.el.detach()
16252         //this.el.setXY([0,0]);
16253         this.el.show();
16254         this.el.dom.style.display='block';
16255         this.el.addClass(placement);
16256         
16257         //this.el.appendTo(on_el);
16258         
16259         var p = this.getPosition();
16260         var box = this.el.getBox();
16261         
16262         if (autoPlace) {
16263             // fixme..
16264         }
16265         var align = Roo.bootstrap.Popover.alignment[placement];
16266         this.el.alignTo(on_el, align[0],align[1]);
16267         //var arrow = this.el.select('.arrow',true).first();
16268         //arrow.set(align[2], 
16269         
16270         this.el.addClass('in');
16271         
16272         
16273         if (this.el.hasClass('fade')) {
16274             // fade it?
16275         }
16276         
16277         this.hoverState = 'in';
16278         
16279         this.fireEvent('show', this);
16280         
16281     },
16282     hide : function()
16283     {
16284         this.el.setXY([0,0]);
16285         this.el.removeClass('in');
16286         this.el.hide();
16287         this.hoverState = null;
16288         
16289         this.fireEvent('hide', this);
16290     }
16291     
16292 });
16293
16294 Roo.bootstrap.Popover.alignment = {
16295     'left' : ['r-l', [-10,0], 'right'],
16296     'right' : ['l-r', [10,0], 'left'],
16297     'bottom' : ['t-b', [0,10], 'top'],
16298     'top' : [ 'b-t', [0,-10], 'bottom']
16299 };
16300
16301  /*
16302  * - LGPL
16303  *
16304  * Progress
16305  * 
16306  */
16307
16308 /**
16309  * @class Roo.bootstrap.Progress
16310  * @extends Roo.bootstrap.Component
16311  * Bootstrap Progress class
16312  * @cfg {Boolean} striped striped of the progress bar
16313  * @cfg {Boolean} active animated of the progress bar
16314  * 
16315  * 
16316  * @constructor
16317  * Create a new Progress
16318  * @param {Object} config The config object
16319  */
16320
16321 Roo.bootstrap.Progress = function(config){
16322     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16323 };
16324
16325 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16326     
16327     striped : false,
16328     active: false,
16329     
16330     getAutoCreate : function(){
16331         var cfg = {
16332             tag: 'div',
16333             cls: 'progress'
16334         };
16335         
16336         
16337         if(this.striped){
16338             cfg.cls += ' progress-striped';
16339         }
16340       
16341         if(this.active){
16342             cfg.cls += ' active';
16343         }
16344         
16345         
16346         return cfg;
16347     }
16348    
16349 });
16350
16351  
16352
16353  /*
16354  * - LGPL
16355  *
16356  * ProgressBar
16357  * 
16358  */
16359
16360 /**
16361  * @class Roo.bootstrap.ProgressBar
16362  * @extends Roo.bootstrap.Component
16363  * Bootstrap ProgressBar class
16364  * @cfg {Number} aria_valuenow aria-value now
16365  * @cfg {Number} aria_valuemin aria-value min
16366  * @cfg {Number} aria_valuemax aria-value max
16367  * @cfg {String} label label for the progress bar
16368  * @cfg {String} panel (success | info | warning | danger )
16369  * @cfg {String} role role of the progress bar
16370  * @cfg {String} sr_only text
16371  * 
16372  * 
16373  * @constructor
16374  * Create a new ProgressBar
16375  * @param {Object} config The config object
16376  */
16377
16378 Roo.bootstrap.ProgressBar = function(config){
16379     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16380 };
16381
16382 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16383     
16384     aria_valuenow : 0,
16385     aria_valuemin : 0,
16386     aria_valuemax : 100,
16387     label : false,
16388     panel : false,
16389     role : false,
16390     sr_only: false,
16391     
16392     getAutoCreate : function()
16393     {
16394         
16395         var cfg = {
16396             tag: 'div',
16397             cls: 'progress-bar',
16398             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16399         };
16400         
16401         if(this.sr_only){
16402             cfg.cn = {
16403                 tag: 'span',
16404                 cls: 'sr-only',
16405                 html: this.sr_only
16406             }
16407         }
16408         
16409         if(this.role){
16410             cfg.role = this.role;
16411         }
16412         
16413         if(this.aria_valuenow){
16414             cfg['aria-valuenow'] = this.aria_valuenow;
16415         }
16416         
16417         if(this.aria_valuemin){
16418             cfg['aria-valuemin'] = this.aria_valuemin;
16419         }
16420         
16421         if(this.aria_valuemax){
16422             cfg['aria-valuemax'] = this.aria_valuemax;
16423         }
16424         
16425         if(this.label && !this.sr_only){
16426             cfg.html = this.label;
16427         }
16428         
16429         if(this.panel){
16430             cfg.cls += ' progress-bar-' + this.panel;
16431         }
16432         
16433         return cfg;
16434     },
16435     
16436     update : function(aria_valuenow)
16437     {
16438         this.aria_valuenow = aria_valuenow;
16439         
16440         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16441     }
16442    
16443 });
16444
16445  
16446
16447  /*
16448  * - LGPL
16449  *
16450  * column
16451  * 
16452  */
16453
16454 /**
16455  * @class Roo.bootstrap.TabGroup
16456  * @extends Roo.bootstrap.Column
16457  * Bootstrap Column class
16458  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16459  * @cfg {Boolean} carousel true to make the group behave like a carousel
16460  * @cfg {Boolean} bullets show bullets for the panels
16461  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16462  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16463  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16464  * @cfg {Boolean} showarrow (true|false) show arrow default true
16465  * 
16466  * @constructor
16467  * Create a new TabGroup
16468  * @param {Object} config The config object
16469  */
16470
16471 Roo.bootstrap.TabGroup = function(config){
16472     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16473     if (!this.navId) {
16474         this.navId = Roo.id();
16475     }
16476     this.tabs = [];
16477     Roo.bootstrap.TabGroup.register(this);
16478     
16479 };
16480
16481 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16482     
16483     carousel : false,
16484     transition : false,
16485     bullets : 0,
16486     timer : 0,
16487     autoslide : false,
16488     slideFn : false,
16489     slideOnTouch : false,
16490     showarrow : true,
16491     
16492     getAutoCreate : function()
16493     {
16494         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16495         
16496         cfg.cls += ' tab-content';
16497         
16498         if (this.carousel) {
16499             cfg.cls += ' carousel slide';
16500             
16501             cfg.cn = [{
16502                cls : 'carousel-inner',
16503                cn : []
16504             }];
16505         
16506             if(this.bullets  && !Roo.isTouch){
16507                 
16508                 var bullets = {
16509                     cls : 'carousel-bullets',
16510                     cn : []
16511                 };
16512                
16513                 if(this.bullets_cls){
16514                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16515                 }
16516                 
16517                 bullets.cn.push({
16518                     cls : 'clear'
16519                 });
16520                 
16521                 cfg.cn[0].cn.push(bullets);
16522             }
16523             
16524             if(this.showarrow){
16525                 cfg.cn[0].cn.push({
16526                     tag : 'div',
16527                     class : 'carousel-arrow',
16528                     cn : [
16529                         {
16530                             tag : 'div',
16531                             class : 'carousel-prev',
16532                             cn : [
16533                                 {
16534                                     tag : 'i',
16535                                     class : 'fa fa-chevron-left'
16536                                 }
16537                             ]
16538                         },
16539                         {
16540                             tag : 'div',
16541                             class : 'carousel-next',
16542                             cn : [
16543                                 {
16544                                     tag : 'i',
16545                                     class : 'fa fa-chevron-right'
16546                                 }
16547                             ]
16548                         }
16549                     ]
16550                 });
16551             }
16552             
16553         }
16554         
16555         return cfg;
16556     },
16557     
16558     initEvents:  function()
16559     {
16560         if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16561             this.el.on("touchstart", this.onTouchStart, this);
16562         }
16563         
16564         if(this.autoslide){
16565             var _this = this;
16566             
16567             this.slideFn = window.setInterval(function() {
16568                 _this.showPanelNext();
16569             }, this.timer);
16570         }
16571         
16572         if(this.showarrow){
16573             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16574             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16575         }
16576         
16577         
16578     },
16579     
16580     onTouchStart : function(e, el, o)
16581     {
16582         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16583             return;
16584         }
16585         
16586         this.showPanelNext();
16587     },
16588     
16589     getChildContainer : function()
16590     {
16591         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16592     },
16593     
16594     /**
16595     * register a Navigation item
16596     * @param {Roo.bootstrap.NavItem} the navitem to add
16597     */
16598     register : function(item)
16599     {
16600         this.tabs.push( item);
16601         item.navId = this.navId; // not really needed..
16602         this.addBullet();
16603     
16604     },
16605     
16606     getActivePanel : function()
16607     {
16608         var r = false;
16609         Roo.each(this.tabs, function(t) {
16610             if (t.active) {
16611                 r = t;
16612                 return false;
16613             }
16614             return null;
16615         });
16616         return r;
16617         
16618     },
16619     getPanelByName : function(n)
16620     {
16621         var r = false;
16622         Roo.each(this.tabs, function(t) {
16623             if (t.tabId == n) {
16624                 r = t;
16625                 return false;
16626             }
16627             return null;
16628         });
16629         return r;
16630     },
16631     indexOfPanel : function(p)
16632     {
16633         var r = false;
16634         Roo.each(this.tabs, function(t,i) {
16635             if (t.tabId == p.tabId) {
16636                 r = i;
16637                 return false;
16638             }
16639             return null;
16640         });
16641         return r;
16642     },
16643     /**
16644      * show a specific panel
16645      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16646      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16647      */
16648     showPanel : function (pan)
16649     {
16650         if(this.transition || typeof(pan) == 'undefined'){
16651             Roo.log("waiting for the transitionend");
16652             return;
16653         }
16654         
16655         if (typeof(pan) == 'number') {
16656             pan = this.tabs[pan];
16657         }
16658         
16659         if (typeof(pan) == 'string') {
16660             pan = this.getPanelByName(pan);
16661         }
16662         
16663         var cur = this.getActivePanel();
16664         
16665         if(!pan || !cur){
16666             Roo.log('pan or acitve pan is undefined');
16667             return false;
16668         }
16669         
16670         if (pan.tabId == this.getActivePanel().tabId) {
16671             return true;
16672         }
16673         
16674         if (false === cur.fireEvent('beforedeactivate')) {
16675             return false;
16676         }
16677         
16678         if(this.bullets > 0 && !Roo.isTouch){
16679             this.setActiveBullet(this.indexOfPanel(pan));
16680         }
16681         
16682         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16683             
16684             this.transition = true;
16685             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16686             var lr = dir == 'next' ? 'left' : 'right';
16687             pan.el.addClass(dir); // or prev
16688             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16689             cur.el.addClass(lr); // or right
16690             pan.el.addClass(lr);
16691             
16692             var _this = this;
16693             cur.el.on('transitionend', function() {
16694                 Roo.log("trans end?");
16695                 
16696                 pan.el.removeClass([lr,dir]);
16697                 pan.setActive(true);
16698                 
16699                 cur.el.removeClass([lr]);
16700                 cur.setActive(false);
16701                 
16702                 _this.transition = false;
16703                 
16704             }, this, { single:  true } );
16705             
16706             return true;
16707         }
16708         
16709         cur.setActive(false);
16710         pan.setActive(true);
16711         
16712         return true;
16713         
16714     },
16715     showPanelNext : function()
16716     {
16717         var i = this.indexOfPanel(this.getActivePanel());
16718         
16719         if (i >= this.tabs.length - 1 && !this.autoslide) {
16720             return;
16721         }
16722         
16723         if (i >= this.tabs.length - 1 && this.autoslide) {
16724             i = -1;
16725         }
16726         
16727         this.showPanel(this.tabs[i+1]);
16728     },
16729     
16730     showPanelPrev : function()
16731     {
16732         var i = this.indexOfPanel(this.getActivePanel());
16733         
16734         if (i  < 1 && !this.autoslide) {
16735             return;
16736         }
16737         
16738         if (i < 1 && this.autoslide) {
16739             i = this.tabs.length;
16740         }
16741         
16742         this.showPanel(this.tabs[i-1]);
16743     },
16744     
16745     
16746     addBullet: function()
16747     {
16748         if(!this.bullets || Roo.isTouch){
16749             return;
16750         }
16751         var ctr = this.el.select('.carousel-bullets',true).first();
16752         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16753         var bullet = ctr.createChild({
16754             cls : 'bullet bullet-' + i
16755         },ctr.dom.lastChild);
16756         
16757         
16758         var _this = this;
16759         
16760         bullet.on('click', (function(e, el, o, ii, t){
16761
16762             e.preventDefault();
16763
16764             this.showPanel(ii);
16765
16766             if(this.autoslide && this.slideFn){
16767                 clearInterval(this.slideFn);
16768                 this.slideFn = window.setInterval(function() {
16769                     _this.showPanelNext();
16770                 }, this.timer);
16771             }
16772
16773         }).createDelegate(this, [i, bullet], true));
16774                 
16775         
16776     },
16777      
16778     setActiveBullet : function(i)
16779     {
16780         if(Roo.isTouch){
16781             return;
16782         }
16783         
16784         Roo.each(this.el.select('.bullet', true).elements, function(el){
16785             el.removeClass('selected');
16786         });
16787
16788         var bullet = this.el.select('.bullet-' + i, true).first();
16789         
16790         if(!bullet){
16791             return;
16792         }
16793         
16794         bullet.addClass('selected');
16795     }
16796     
16797     
16798   
16799 });
16800
16801  
16802
16803  
16804  
16805 Roo.apply(Roo.bootstrap.TabGroup, {
16806     
16807     groups: {},
16808      /**
16809     * register a Navigation Group
16810     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16811     */
16812     register : function(navgrp)
16813     {
16814         this.groups[navgrp.navId] = navgrp;
16815         
16816     },
16817     /**
16818     * fetch a Navigation Group based on the navigation ID
16819     * if one does not exist , it will get created.
16820     * @param {string} the navgroup to add
16821     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16822     */
16823     get: function(navId) {
16824         if (typeof(this.groups[navId]) == 'undefined') {
16825             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16826         }
16827         return this.groups[navId] ;
16828     }
16829     
16830     
16831     
16832 });
16833
16834  /*
16835  * - LGPL
16836  *
16837  * TabPanel
16838  * 
16839  */
16840
16841 /**
16842  * @class Roo.bootstrap.TabPanel
16843  * @extends Roo.bootstrap.Component
16844  * Bootstrap TabPanel class
16845  * @cfg {Boolean} active panel active
16846  * @cfg {String} html panel content
16847  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16848  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16849  * @cfg {String} href click to link..
16850  * 
16851  * 
16852  * @constructor
16853  * Create a new TabPanel
16854  * @param {Object} config The config object
16855  */
16856
16857 Roo.bootstrap.TabPanel = function(config){
16858     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16859     this.addEvents({
16860         /**
16861              * @event changed
16862              * Fires when the active status changes
16863              * @param {Roo.bootstrap.TabPanel} this
16864              * @param {Boolean} state the new state
16865             
16866          */
16867         'changed': true,
16868         /**
16869              * @event beforedeactivate
16870              * Fires before a tab is de-activated - can be used to do validation on a form.
16871              * @param {Roo.bootstrap.TabPanel} this
16872              * @return {Boolean} false if there is an error
16873             
16874          */
16875         'beforedeactivate': true
16876      });
16877     
16878     this.tabId = this.tabId || Roo.id();
16879   
16880 };
16881
16882 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16883     
16884     active: false,
16885     html: false,
16886     tabId: false,
16887     navId : false,
16888     href : '',
16889     
16890     getAutoCreate : function(){
16891         var cfg = {
16892             tag: 'div',
16893             // item is needed for carousel - not sure if it has any effect otherwise
16894             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
16895             html: this.html || ''
16896         };
16897         
16898         if(this.active){
16899             cfg.cls += ' active';
16900         }
16901         
16902         if(this.tabId){
16903             cfg.tabId = this.tabId;
16904         }
16905         
16906         
16907         return cfg;
16908     },
16909     
16910     initEvents:  function()
16911     {
16912         var p = this.parent();
16913         this.navId = this.navId || p.navId;
16914         
16915         if (typeof(this.navId) != 'undefined') {
16916             // not really needed.. but just in case.. parent should be a NavGroup.
16917             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16918             
16919             tg.register(this);
16920             
16921             var i = tg.tabs.length - 1;
16922             
16923             if(this.active && tg.bullets > 0 && i < tg.bullets){
16924                 tg.setActiveBullet(i);
16925             }
16926         }
16927         
16928         if(this.href.length){
16929             this.el.on('click', this.onClick, this);
16930         }
16931         
16932     },
16933     
16934     onRender : function(ct, position)
16935     {
16936        // Roo.log("Call onRender: " + this.xtype);
16937         
16938         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16939         
16940         
16941         
16942         
16943         
16944     },
16945     
16946     setActive: function(state)
16947     {
16948         Roo.log("panel - set active " + this.tabId + "=" + state);
16949         
16950         this.active = state;
16951         if (!state) {
16952             this.el.removeClass('active');
16953             
16954         } else  if (!this.el.hasClass('active')) {
16955             this.el.addClass('active');
16956         }
16957         
16958         this.fireEvent('changed', this, state);
16959     },
16960     
16961     onClick: function(e)
16962     {
16963         e.preventDefault();
16964         
16965         window.location.href = this.href;
16966     }
16967     
16968     
16969 });
16970  
16971
16972  
16973
16974  /*
16975  * - LGPL
16976  *
16977  * DateField
16978  * 
16979  */
16980
16981 /**
16982  * @class Roo.bootstrap.DateField
16983  * @extends Roo.bootstrap.Input
16984  * Bootstrap DateField class
16985  * @cfg {Number} weekStart default 0
16986  * @cfg {String} viewMode default empty, (months|years)
16987  * @cfg {String} minViewMode default empty, (months|years)
16988  * @cfg {Number} startDate default -Infinity
16989  * @cfg {Number} endDate default Infinity
16990  * @cfg {Boolean} todayHighlight default false
16991  * @cfg {Boolean} todayBtn default false
16992  * @cfg {Boolean} calendarWeeks default false
16993  * @cfg {Object} daysOfWeekDisabled default empty
16994  * @cfg {Boolean} singleMode default false (true | false)
16995  * 
16996  * @cfg {Boolean} keyboardNavigation default true
16997  * @cfg {String} language default en
16998  * 
16999  * @constructor
17000  * Create a new DateField
17001  * @param {Object} config The config object
17002  */
17003
17004 Roo.bootstrap.DateField = function(config){
17005     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17006      this.addEvents({
17007             /**
17008              * @event show
17009              * Fires when this field show.
17010              * @param {Roo.bootstrap.DateField} this
17011              * @param {Mixed} date The date value
17012              */
17013             show : true,
17014             /**
17015              * @event show
17016              * Fires when this field hide.
17017              * @param {Roo.bootstrap.DateField} this
17018              * @param {Mixed} date The date value
17019              */
17020             hide : true,
17021             /**
17022              * @event select
17023              * Fires when select a date.
17024              * @param {Roo.bootstrap.DateField} this
17025              * @param {Mixed} date The date value
17026              */
17027             select : true,
17028             /**
17029              * @event beforeselect
17030              * Fires when before select a date.
17031              * @param {Roo.bootstrap.DateField} this
17032              * @param {Mixed} date The date value
17033              */
17034             beforeselect : true
17035         });
17036 };
17037
17038 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17039     
17040     /**
17041      * @cfg {String} format
17042      * The default date format string which can be overriden for localization support.  The format must be
17043      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17044      */
17045     format : "m/d/y",
17046     /**
17047      * @cfg {String} altFormats
17048      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17049      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17050      */
17051     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17052     
17053     weekStart : 0,
17054     
17055     viewMode : '',
17056     
17057     minViewMode : '',
17058     
17059     todayHighlight : false,
17060     
17061     todayBtn: false,
17062     
17063     language: 'en',
17064     
17065     keyboardNavigation: true,
17066     
17067     calendarWeeks: false,
17068     
17069     startDate: -Infinity,
17070     
17071     endDate: Infinity,
17072     
17073     daysOfWeekDisabled: [],
17074     
17075     _events: [],
17076     
17077     singleMode : false,
17078     
17079     UTCDate: function()
17080     {
17081         return new Date(Date.UTC.apply(Date, arguments));
17082     },
17083     
17084     UTCToday: function()
17085     {
17086         var today = new Date();
17087         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17088     },
17089     
17090     getDate: function() {
17091             var d = this.getUTCDate();
17092             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17093     },
17094     
17095     getUTCDate: function() {
17096             return this.date;
17097     },
17098     
17099     setDate: function(d) {
17100             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17101     },
17102     
17103     setUTCDate: function(d) {
17104             this.date = d;
17105             this.setValue(this.formatDate(this.date));
17106     },
17107         
17108     onRender: function(ct, position)
17109     {
17110         
17111         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17112         
17113         this.language = this.language || 'en';
17114         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17115         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17116         
17117         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17118         this.format = this.format || 'm/d/y';
17119         this.isInline = false;
17120         this.isInput = true;
17121         this.component = this.el.select('.add-on', true).first() || false;
17122         this.component = (this.component && this.component.length === 0) ? false : this.component;
17123         this.hasInput = this.component && this.inputEL().length;
17124         
17125         if (typeof(this.minViewMode === 'string')) {
17126             switch (this.minViewMode) {
17127                 case 'months':
17128                     this.minViewMode = 1;
17129                     break;
17130                 case 'years':
17131                     this.minViewMode = 2;
17132                     break;
17133                 default:
17134                     this.minViewMode = 0;
17135                     break;
17136             }
17137         }
17138         
17139         if (typeof(this.viewMode === 'string')) {
17140             switch (this.viewMode) {
17141                 case 'months':
17142                     this.viewMode = 1;
17143                     break;
17144                 case 'years':
17145                     this.viewMode = 2;
17146                     break;
17147                 default:
17148                     this.viewMode = 0;
17149                     break;
17150             }
17151         }
17152                 
17153         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17154         
17155 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17156         
17157         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17158         
17159         this.picker().on('mousedown', this.onMousedown, this);
17160         this.picker().on('click', this.onClick, this);
17161         
17162         this.picker().addClass('datepicker-dropdown');
17163         
17164         this.startViewMode = this.viewMode;
17165         
17166         if(this.singleMode){
17167             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17168                 v.setVisibilityMode(Roo.Element.DISPLAY);
17169                 v.hide();
17170             });
17171             
17172             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17173                 v.setStyle('width', '189px');
17174             });
17175         }
17176         
17177         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17178             if(!this.calendarWeeks){
17179                 v.remove();
17180                 return;
17181             }
17182             
17183             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17184             v.attr('colspan', function(i, val){
17185                 return parseInt(val) + 1;
17186             });
17187         });
17188                         
17189         
17190         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17191         
17192         this.setStartDate(this.startDate);
17193         this.setEndDate(this.endDate);
17194         
17195         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17196         
17197         this.fillDow();
17198         this.fillMonths();
17199         this.update();
17200         this.showMode();
17201         
17202         if(this.isInline) {
17203             this.show();
17204         }
17205     },
17206     
17207     picker : function()
17208     {
17209         return this.pickerEl;
17210 //        return this.el.select('.datepicker', true).first();
17211     },
17212     
17213     fillDow: function()
17214     {
17215         var dowCnt = this.weekStart;
17216         
17217         var dow = {
17218             tag: 'tr',
17219             cn: [
17220                 
17221             ]
17222         };
17223         
17224         if(this.calendarWeeks){
17225             dow.cn.push({
17226                 tag: 'th',
17227                 cls: 'cw',
17228                 html: '&nbsp;'
17229             })
17230         }
17231         
17232         while (dowCnt < this.weekStart + 7) {
17233             dow.cn.push({
17234                 tag: 'th',
17235                 cls: 'dow',
17236                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17237             });
17238         }
17239         
17240         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17241     },
17242     
17243     fillMonths: function()
17244     {    
17245         var i = 0;
17246         var months = this.picker().select('>.datepicker-months td', true).first();
17247         
17248         months.dom.innerHTML = '';
17249         
17250         while (i < 12) {
17251             var month = {
17252                 tag: 'span',
17253                 cls: 'month',
17254                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17255             };
17256             
17257             months.createChild(month);
17258         }
17259         
17260     },
17261     
17262     update: function()
17263     {
17264         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;
17265         
17266         if (this.date < this.startDate) {
17267             this.viewDate = new Date(this.startDate);
17268         } else if (this.date > this.endDate) {
17269             this.viewDate = new Date(this.endDate);
17270         } else {
17271             this.viewDate = new Date(this.date);
17272         }
17273         
17274         this.fill();
17275     },
17276     
17277     fill: function() 
17278     {
17279         var d = new Date(this.viewDate),
17280                 year = d.getUTCFullYear(),
17281                 month = d.getUTCMonth(),
17282                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17283                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17284                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17285                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17286                 currentDate = this.date && this.date.valueOf(),
17287                 today = this.UTCToday();
17288         
17289         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17290         
17291 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17292         
17293 //        this.picker.select('>tfoot th.today').
17294 //                                              .text(dates[this.language].today)
17295 //                                              .toggle(this.todayBtn !== false);
17296     
17297         this.updateNavArrows();
17298         this.fillMonths();
17299                                                 
17300         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17301         
17302         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17303          
17304         prevMonth.setUTCDate(day);
17305         
17306         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17307         
17308         var nextMonth = new Date(prevMonth);
17309         
17310         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17311         
17312         nextMonth = nextMonth.valueOf();
17313         
17314         var fillMonths = false;
17315         
17316         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17317         
17318         while(prevMonth.valueOf() < nextMonth) {
17319             var clsName = '';
17320             
17321             if (prevMonth.getUTCDay() === this.weekStart) {
17322                 if(fillMonths){
17323                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17324                 }
17325                     
17326                 fillMonths = {
17327                     tag: 'tr',
17328                     cn: []
17329                 };
17330                 
17331                 if(this.calendarWeeks){
17332                     // ISO 8601: First week contains first thursday.
17333                     // ISO also states week starts on Monday, but we can be more abstract here.
17334                     var
17335                     // Start of current week: based on weekstart/current date
17336                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17337                     // Thursday of this week
17338                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17339                     // First Thursday of year, year from thursday
17340                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17341                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17342                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17343                     
17344                     fillMonths.cn.push({
17345                         tag: 'td',
17346                         cls: 'cw',
17347                         html: calWeek
17348                     });
17349                 }
17350             }
17351             
17352             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17353                 clsName += ' old';
17354             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17355                 clsName += ' new';
17356             }
17357             if (this.todayHighlight &&
17358                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17359                 prevMonth.getUTCMonth() == today.getMonth() &&
17360                 prevMonth.getUTCDate() == today.getDate()) {
17361                 clsName += ' today';
17362             }
17363             
17364             if (currentDate && prevMonth.valueOf() === currentDate) {
17365                 clsName += ' active';
17366             }
17367             
17368             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17369                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17370                     clsName += ' disabled';
17371             }
17372             
17373             fillMonths.cn.push({
17374                 tag: 'td',
17375                 cls: 'day ' + clsName,
17376                 html: prevMonth.getDate()
17377             });
17378             
17379             prevMonth.setDate(prevMonth.getDate()+1);
17380         }
17381           
17382         var currentYear = this.date && this.date.getUTCFullYear();
17383         var currentMonth = this.date && this.date.getUTCMonth();
17384         
17385         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17386         
17387         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17388             v.removeClass('active');
17389             
17390             if(currentYear === year && k === currentMonth){
17391                 v.addClass('active');
17392             }
17393             
17394             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17395                 v.addClass('disabled');
17396             }
17397             
17398         });
17399         
17400         
17401         year = parseInt(year/10, 10) * 10;
17402         
17403         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17404         
17405         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17406         
17407         year -= 1;
17408         for (var i = -1; i < 11; i++) {
17409             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17410                 tag: 'span',
17411                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17412                 html: year
17413             });
17414             
17415             year += 1;
17416         }
17417     },
17418     
17419     showMode: function(dir) 
17420     {
17421         if (dir) {
17422             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17423         }
17424         
17425         Roo.each(this.picker().select('>div',true).elements, function(v){
17426             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17427             v.hide();
17428         });
17429         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17430     },
17431     
17432     place: function()
17433     {
17434         if(this.isInline) {
17435             return;
17436         }
17437         
17438         this.picker().removeClass(['bottom', 'top']);
17439         
17440         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17441             /*
17442              * place to the top of element!
17443              *
17444              */
17445             
17446             this.picker().addClass('top');
17447             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17448             
17449             return;
17450         }
17451         
17452         this.picker().addClass('bottom');
17453         
17454         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17455     },
17456     
17457     parseDate : function(value)
17458     {
17459         if(!value || value instanceof Date){
17460             return value;
17461         }
17462         var v = Date.parseDate(value, this.format);
17463         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17464             v = Date.parseDate(value, 'Y-m-d');
17465         }
17466         if(!v && this.altFormats){
17467             if(!this.altFormatsArray){
17468                 this.altFormatsArray = this.altFormats.split("|");
17469             }
17470             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17471                 v = Date.parseDate(value, this.altFormatsArray[i]);
17472             }
17473         }
17474         return v;
17475     },
17476     
17477     formatDate : function(date, fmt)
17478     {   
17479         return (!date || !(date instanceof Date)) ?
17480         date : date.dateFormat(fmt || this.format);
17481     },
17482     
17483     onFocus : function()
17484     {
17485         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17486         this.show();
17487     },
17488     
17489     onBlur : function()
17490     {
17491         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17492         
17493         var d = this.inputEl().getValue();
17494         
17495         this.setValue(d);
17496                 
17497         this.hide();
17498     },
17499     
17500     show : function()
17501     {
17502         this.picker().show();
17503         this.update();
17504         this.place();
17505         
17506         this.fireEvent('show', this, this.date);
17507     },
17508     
17509     hide : function()
17510     {
17511         if(this.isInline) {
17512             return;
17513         }
17514         this.picker().hide();
17515         this.viewMode = this.startViewMode;
17516         this.showMode();
17517         
17518         this.fireEvent('hide', this, this.date);
17519         
17520     },
17521     
17522     onMousedown: function(e)
17523     {
17524         e.stopPropagation();
17525         e.preventDefault();
17526     },
17527     
17528     keyup: function(e)
17529     {
17530         Roo.bootstrap.DateField.superclass.keyup.call(this);
17531         this.update();
17532     },
17533
17534     setValue: function(v)
17535     {
17536         if(this.fireEvent('beforeselect', this, v) !== false){
17537             var d = new Date(this.parseDate(v) ).clearTime();
17538         
17539             if(isNaN(d.getTime())){
17540                 this.date = this.viewDate = '';
17541                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17542                 return;
17543             }
17544
17545             v = this.formatDate(d);
17546
17547             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17548
17549             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17550
17551             this.update();
17552
17553             this.fireEvent('select', this, this.date);
17554         }
17555     },
17556     
17557     getValue: function()
17558     {
17559         return this.formatDate(this.date);
17560     },
17561     
17562     fireKey: function(e)
17563     {
17564         if (!this.picker().isVisible()){
17565             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17566                 this.show();
17567             }
17568             return;
17569         }
17570         
17571         var dateChanged = false,
17572         dir, day, month,
17573         newDate, newViewDate;
17574         
17575         switch(e.keyCode){
17576             case 27: // escape
17577                 this.hide();
17578                 e.preventDefault();
17579                 break;
17580             case 37: // left
17581             case 39: // right
17582                 if (!this.keyboardNavigation) {
17583                     break;
17584                 }
17585                 dir = e.keyCode == 37 ? -1 : 1;
17586                 
17587                 if (e.ctrlKey){
17588                     newDate = this.moveYear(this.date, dir);
17589                     newViewDate = this.moveYear(this.viewDate, dir);
17590                 } else if (e.shiftKey){
17591                     newDate = this.moveMonth(this.date, dir);
17592                     newViewDate = this.moveMonth(this.viewDate, dir);
17593                 } else {
17594                     newDate = new Date(this.date);
17595                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17596                     newViewDate = new Date(this.viewDate);
17597                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17598                 }
17599                 if (this.dateWithinRange(newDate)){
17600                     this.date = newDate;
17601                     this.viewDate = newViewDate;
17602                     this.setValue(this.formatDate(this.date));
17603 //                    this.update();
17604                     e.preventDefault();
17605                     dateChanged = true;
17606                 }
17607                 break;
17608             case 38: // up
17609             case 40: // down
17610                 if (!this.keyboardNavigation) {
17611                     break;
17612                 }
17613                 dir = e.keyCode == 38 ? -1 : 1;
17614                 if (e.ctrlKey){
17615                     newDate = this.moveYear(this.date, dir);
17616                     newViewDate = this.moveYear(this.viewDate, dir);
17617                 } else if (e.shiftKey){
17618                     newDate = this.moveMonth(this.date, dir);
17619                     newViewDate = this.moveMonth(this.viewDate, dir);
17620                 } else {
17621                     newDate = new Date(this.date);
17622                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17623                     newViewDate = new Date(this.viewDate);
17624                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17625                 }
17626                 if (this.dateWithinRange(newDate)){
17627                     this.date = newDate;
17628                     this.viewDate = newViewDate;
17629                     this.setValue(this.formatDate(this.date));
17630 //                    this.update();
17631                     e.preventDefault();
17632                     dateChanged = true;
17633                 }
17634                 break;
17635             case 13: // enter
17636                 this.setValue(this.formatDate(this.date));
17637                 this.hide();
17638                 e.preventDefault();
17639                 break;
17640             case 9: // tab
17641                 this.setValue(this.formatDate(this.date));
17642                 this.hide();
17643                 break;
17644             case 16: // shift
17645             case 17: // ctrl
17646             case 18: // alt
17647                 break;
17648             default :
17649                 this.hide();
17650                 
17651         }
17652     },
17653     
17654     
17655     onClick: function(e) 
17656     {
17657         e.stopPropagation();
17658         e.preventDefault();
17659         
17660         var target = e.getTarget();
17661         
17662         if(target.nodeName.toLowerCase() === 'i'){
17663             target = Roo.get(target).dom.parentNode;
17664         }
17665         
17666         var nodeName = target.nodeName;
17667         var className = target.className;
17668         var html = target.innerHTML;
17669         //Roo.log(nodeName);
17670         
17671         switch(nodeName.toLowerCase()) {
17672             case 'th':
17673                 switch(className) {
17674                     case 'switch':
17675                         this.showMode(1);
17676                         break;
17677                     case 'prev':
17678                     case 'next':
17679                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17680                         switch(this.viewMode){
17681                                 case 0:
17682                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17683                                         break;
17684                                 case 1:
17685                                 case 2:
17686                                         this.viewDate = this.moveYear(this.viewDate, dir);
17687                                         break;
17688                         }
17689                         this.fill();
17690                         break;
17691                     case 'today':
17692                         var date = new Date();
17693                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17694 //                        this.fill()
17695                         this.setValue(this.formatDate(this.date));
17696                         
17697                         this.hide();
17698                         break;
17699                 }
17700                 break;
17701             case 'span':
17702                 if (className.indexOf('disabled') < 0) {
17703                     this.viewDate.setUTCDate(1);
17704                     if (className.indexOf('month') > -1) {
17705                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17706                     } else {
17707                         var year = parseInt(html, 10) || 0;
17708                         this.viewDate.setUTCFullYear(year);
17709                         
17710                     }
17711                     
17712                     if(this.singleMode){
17713                         this.setValue(this.formatDate(this.viewDate));
17714                         this.hide();
17715                         return;
17716                     }
17717                     
17718                     this.showMode(-1);
17719                     this.fill();
17720                 }
17721                 break;
17722                 
17723             case 'td':
17724                 //Roo.log(className);
17725                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17726                     var day = parseInt(html, 10) || 1;
17727                     var year = this.viewDate.getUTCFullYear(),
17728                         month = this.viewDate.getUTCMonth();
17729
17730                     if (className.indexOf('old') > -1) {
17731                         if(month === 0 ){
17732                             month = 11;
17733                             year -= 1;
17734                         }else{
17735                             month -= 1;
17736                         }
17737                     } else if (className.indexOf('new') > -1) {
17738                         if (month == 11) {
17739                             month = 0;
17740                             year += 1;
17741                         } else {
17742                             month += 1;
17743                         }
17744                     }
17745                     //Roo.log([year,month,day]);
17746                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17747                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17748 //                    this.fill();
17749                     //Roo.log(this.formatDate(this.date));
17750                     this.setValue(this.formatDate(this.date));
17751                     this.hide();
17752                 }
17753                 break;
17754         }
17755     },
17756     
17757     setStartDate: function(startDate)
17758     {
17759         this.startDate = startDate || -Infinity;
17760         if (this.startDate !== -Infinity) {
17761             this.startDate = this.parseDate(this.startDate);
17762         }
17763         this.update();
17764         this.updateNavArrows();
17765     },
17766
17767     setEndDate: function(endDate)
17768     {
17769         this.endDate = endDate || Infinity;
17770         if (this.endDate !== Infinity) {
17771             this.endDate = this.parseDate(this.endDate);
17772         }
17773         this.update();
17774         this.updateNavArrows();
17775     },
17776     
17777     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17778     {
17779         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17780         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17781             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17782         }
17783         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17784             return parseInt(d, 10);
17785         });
17786         this.update();
17787         this.updateNavArrows();
17788     },
17789     
17790     updateNavArrows: function() 
17791     {
17792         if(this.singleMode){
17793             return;
17794         }
17795         
17796         var d = new Date(this.viewDate),
17797         year = d.getUTCFullYear(),
17798         month = d.getUTCMonth();
17799         
17800         Roo.each(this.picker().select('.prev', true).elements, function(v){
17801             v.show();
17802             switch (this.viewMode) {
17803                 case 0:
17804
17805                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17806                         v.hide();
17807                     }
17808                     break;
17809                 case 1:
17810                 case 2:
17811                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17812                         v.hide();
17813                     }
17814                     break;
17815             }
17816         });
17817         
17818         Roo.each(this.picker().select('.next', true).elements, function(v){
17819             v.show();
17820             switch (this.viewMode) {
17821                 case 0:
17822
17823                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17824                         v.hide();
17825                     }
17826                     break;
17827                 case 1:
17828                 case 2:
17829                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17830                         v.hide();
17831                     }
17832                     break;
17833             }
17834         })
17835     },
17836     
17837     moveMonth: function(date, dir)
17838     {
17839         if (!dir) {
17840             return date;
17841         }
17842         var new_date = new Date(date.valueOf()),
17843         day = new_date.getUTCDate(),
17844         month = new_date.getUTCMonth(),
17845         mag = Math.abs(dir),
17846         new_month, test;
17847         dir = dir > 0 ? 1 : -1;
17848         if (mag == 1){
17849             test = dir == -1
17850             // If going back one month, make sure month is not current month
17851             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17852             ? function(){
17853                 return new_date.getUTCMonth() == month;
17854             }
17855             // If going forward one month, make sure month is as expected
17856             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17857             : function(){
17858                 return new_date.getUTCMonth() != new_month;
17859             };
17860             new_month = month + dir;
17861             new_date.setUTCMonth(new_month);
17862             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17863             if (new_month < 0 || new_month > 11) {
17864                 new_month = (new_month + 12) % 12;
17865             }
17866         } else {
17867             // For magnitudes >1, move one month at a time...
17868             for (var i=0; i<mag; i++) {
17869                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17870                 new_date = this.moveMonth(new_date, dir);
17871             }
17872             // ...then reset the day, keeping it in the new month
17873             new_month = new_date.getUTCMonth();
17874             new_date.setUTCDate(day);
17875             test = function(){
17876                 return new_month != new_date.getUTCMonth();
17877             };
17878         }
17879         // Common date-resetting loop -- if date is beyond end of month, make it
17880         // end of month
17881         while (test()){
17882             new_date.setUTCDate(--day);
17883             new_date.setUTCMonth(new_month);
17884         }
17885         return new_date;
17886     },
17887
17888     moveYear: function(date, dir)
17889     {
17890         return this.moveMonth(date, dir*12);
17891     },
17892
17893     dateWithinRange: function(date)
17894     {
17895         return date >= this.startDate && date <= this.endDate;
17896     },
17897
17898     
17899     remove: function() 
17900     {
17901         this.picker().remove();
17902     }
17903    
17904 });
17905
17906 Roo.apply(Roo.bootstrap.DateField,  {
17907     
17908     head : {
17909         tag: 'thead',
17910         cn: [
17911         {
17912             tag: 'tr',
17913             cn: [
17914             {
17915                 tag: 'th',
17916                 cls: 'prev',
17917                 html: '<i class="fa fa-arrow-left"/>'
17918             },
17919             {
17920                 tag: 'th',
17921                 cls: 'switch',
17922                 colspan: '5'
17923             },
17924             {
17925                 tag: 'th',
17926                 cls: 'next',
17927                 html: '<i class="fa fa-arrow-right"/>'
17928             }
17929
17930             ]
17931         }
17932         ]
17933     },
17934     
17935     content : {
17936         tag: 'tbody',
17937         cn: [
17938         {
17939             tag: 'tr',
17940             cn: [
17941             {
17942                 tag: 'td',
17943                 colspan: '7'
17944             }
17945             ]
17946         }
17947         ]
17948     },
17949     
17950     footer : {
17951         tag: 'tfoot',
17952         cn: [
17953         {
17954             tag: 'tr',
17955             cn: [
17956             {
17957                 tag: 'th',
17958                 colspan: '7',
17959                 cls: 'today'
17960             }
17961                     
17962             ]
17963         }
17964         ]
17965     },
17966     
17967     dates:{
17968         en: {
17969             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17970             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17971             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17972             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17973             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17974             today: "Today"
17975         }
17976     },
17977     
17978     modes: [
17979     {
17980         clsName: 'days',
17981         navFnc: 'Month',
17982         navStep: 1
17983     },
17984     {
17985         clsName: 'months',
17986         navFnc: 'FullYear',
17987         navStep: 1
17988     },
17989     {
17990         clsName: 'years',
17991         navFnc: 'FullYear',
17992         navStep: 10
17993     }]
17994 });
17995
17996 Roo.apply(Roo.bootstrap.DateField,  {
17997   
17998     template : {
17999         tag: 'div',
18000         cls: 'datepicker dropdown-menu roo-dynamic',
18001         cn: [
18002         {
18003             tag: 'div',
18004             cls: 'datepicker-days',
18005             cn: [
18006             {
18007                 tag: 'table',
18008                 cls: 'table-condensed',
18009                 cn:[
18010                 Roo.bootstrap.DateField.head,
18011                 {
18012                     tag: 'tbody'
18013                 },
18014                 Roo.bootstrap.DateField.footer
18015                 ]
18016             }
18017             ]
18018         },
18019         {
18020             tag: 'div',
18021             cls: 'datepicker-months',
18022             cn: [
18023             {
18024                 tag: 'table',
18025                 cls: 'table-condensed',
18026                 cn:[
18027                 Roo.bootstrap.DateField.head,
18028                 Roo.bootstrap.DateField.content,
18029                 Roo.bootstrap.DateField.footer
18030                 ]
18031             }
18032             ]
18033         },
18034         {
18035             tag: 'div',
18036             cls: 'datepicker-years',
18037             cn: [
18038             {
18039                 tag: 'table',
18040                 cls: 'table-condensed',
18041                 cn:[
18042                 Roo.bootstrap.DateField.head,
18043                 Roo.bootstrap.DateField.content,
18044                 Roo.bootstrap.DateField.footer
18045                 ]
18046             }
18047             ]
18048         }
18049         ]
18050     }
18051 });
18052
18053  
18054
18055  /*
18056  * - LGPL
18057  *
18058  * TimeField
18059  * 
18060  */
18061
18062 /**
18063  * @class Roo.bootstrap.TimeField
18064  * @extends Roo.bootstrap.Input
18065  * Bootstrap DateField class
18066  * 
18067  * 
18068  * @constructor
18069  * Create a new TimeField
18070  * @param {Object} config The config object
18071  */
18072
18073 Roo.bootstrap.TimeField = function(config){
18074     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18075     this.addEvents({
18076             /**
18077              * @event show
18078              * Fires when this field show.
18079              * @param {Roo.bootstrap.DateField} thisthis
18080              * @param {Mixed} date The date value
18081              */
18082             show : true,
18083             /**
18084              * @event show
18085              * Fires when this field hide.
18086              * @param {Roo.bootstrap.DateField} this
18087              * @param {Mixed} date The date value
18088              */
18089             hide : true,
18090             /**
18091              * @event select
18092              * Fires when select a date.
18093              * @param {Roo.bootstrap.DateField} this
18094              * @param {Mixed} date The date value
18095              */
18096             select : true
18097         });
18098 };
18099
18100 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18101     
18102     /**
18103      * @cfg {String} format
18104      * The default time format string which can be overriden for localization support.  The format must be
18105      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18106      */
18107     format : "H:i",
18108        
18109     onRender: function(ct, position)
18110     {
18111         
18112         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18113                 
18114         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18115         
18116         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18117         
18118         this.pop = this.picker().select('>.datepicker-time',true).first();
18119         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18120         
18121         this.picker().on('mousedown', this.onMousedown, this);
18122         this.picker().on('click', this.onClick, this);
18123         
18124         this.picker().addClass('datepicker-dropdown');
18125     
18126         this.fillTime();
18127         this.update();
18128             
18129         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18130         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18131         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18132         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18133         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18134         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18135
18136     },
18137     
18138     fireKey: function(e){
18139         if (!this.picker().isVisible()){
18140             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18141                 this.show();
18142             }
18143             return;
18144         }
18145
18146         e.preventDefault();
18147         
18148         switch(e.keyCode){
18149             case 27: // escape
18150                 this.hide();
18151                 break;
18152             case 37: // left
18153             case 39: // right
18154                 this.onTogglePeriod();
18155                 break;
18156             case 38: // up
18157                 this.onIncrementMinutes();
18158                 break;
18159             case 40: // down
18160                 this.onDecrementMinutes();
18161                 break;
18162             case 13: // enter
18163             case 9: // tab
18164                 this.setTime();
18165                 break;
18166         }
18167     },
18168     
18169     onClick: function(e) {
18170         e.stopPropagation();
18171         e.preventDefault();
18172     },
18173     
18174     picker : function()
18175     {
18176         return this.el.select('.datepicker', true).first();
18177     },
18178     
18179     fillTime: function()
18180     {    
18181         var time = this.pop.select('tbody', true).first();
18182         
18183         time.dom.innerHTML = '';
18184         
18185         time.createChild({
18186             tag: 'tr',
18187             cn: [
18188                 {
18189                     tag: 'td',
18190                     cn: [
18191                         {
18192                             tag: 'a',
18193                             href: '#',
18194                             cls: 'btn',
18195                             cn: [
18196                                 {
18197                                     tag: 'span',
18198                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18199                                 }
18200                             ]
18201                         } 
18202                     ]
18203                 },
18204                 {
18205                     tag: 'td',
18206                     cls: 'separator'
18207                 },
18208                 {
18209                     tag: 'td',
18210                     cn: [
18211                         {
18212                             tag: 'a',
18213                             href: '#',
18214                             cls: 'btn',
18215                             cn: [
18216                                 {
18217                                     tag: 'span',
18218                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18219                                 }
18220                             ]
18221                         }
18222                     ]
18223                 },
18224                 {
18225                     tag: 'td',
18226                     cls: 'separator'
18227                 }
18228             ]
18229         });
18230         
18231         time.createChild({
18232             tag: 'tr',
18233             cn: [
18234                 {
18235                     tag: 'td',
18236                     cn: [
18237                         {
18238                             tag: 'span',
18239                             cls: 'timepicker-hour',
18240                             html: '00'
18241                         }  
18242                     ]
18243                 },
18244                 {
18245                     tag: 'td',
18246                     cls: 'separator',
18247                     html: ':'
18248                 },
18249                 {
18250                     tag: 'td',
18251                     cn: [
18252                         {
18253                             tag: 'span',
18254                             cls: 'timepicker-minute',
18255                             html: '00'
18256                         }  
18257                     ]
18258                 },
18259                 {
18260                     tag: 'td',
18261                     cls: 'separator'
18262                 },
18263                 {
18264                     tag: 'td',
18265                     cn: [
18266                         {
18267                             tag: 'button',
18268                             type: 'button',
18269                             cls: 'btn btn-primary period',
18270                             html: 'AM'
18271                             
18272                         }
18273                     ]
18274                 }
18275             ]
18276         });
18277         
18278         time.createChild({
18279             tag: 'tr',
18280             cn: [
18281                 {
18282                     tag: 'td',
18283                     cn: [
18284                         {
18285                             tag: 'a',
18286                             href: '#',
18287                             cls: 'btn',
18288                             cn: [
18289                                 {
18290                                     tag: 'span',
18291                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18292                                 }
18293                             ]
18294                         }
18295                     ]
18296                 },
18297                 {
18298                     tag: 'td',
18299                     cls: 'separator'
18300                 },
18301                 {
18302                     tag: 'td',
18303                     cn: [
18304                         {
18305                             tag: 'a',
18306                             href: '#',
18307                             cls: 'btn',
18308                             cn: [
18309                                 {
18310                                     tag: 'span',
18311                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18312                                 }
18313                             ]
18314                         }
18315                     ]
18316                 },
18317                 {
18318                     tag: 'td',
18319                     cls: 'separator'
18320                 }
18321             ]
18322         });
18323         
18324     },
18325     
18326     update: function()
18327     {
18328         
18329         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18330         
18331         this.fill();
18332     },
18333     
18334     fill: function() 
18335     {
18336         var hours = this.time.getHours();
18337         var minutes = this.time.getMinutes();
18338         var period = 'AM';
18339         
18340         if(hours > 11){
18341             period = 'PM';
18342         }
18343         
18344         if(hours == 0){
18345             hours = 12;
18346         }
18347         
18348         
18349         if(hours > 12){
18350             hours = hours - 12;
18351         }
18352         
18353         if(hours < 10){
18354             hours = '0' + hours;
18355         }
18356         
18357         if(minutes < 10){
18358             minutes = '0' + minutes;
18359         }
18360         
18361         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18362         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18363         this.pop.select('button', true).first().dom.innerHTML = period;
18364         
18365     },
18366     
18367     place: function()
18368     {   
18369         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18370         
18371         var cls = ['bottom'];
18372         
18373         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18374             cls.pop();
18375             cls.push('top');
18376         }
18377         
18378         cls.push('right');
18379         
18380         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18381             cls.pop();
18382             cls.push('left');
18383         }
18384         
18385         this.picker().addClass(cls.join('-'));
18386         
18387         var _this = this;
18388         
18389         Roo.each(cls, function(c){
18390             if(c == 'bottom'){
18391                 _this.picker().setTop(_this.inputEl().getHeight());
18392                 return;
18393             }
18394             if(c == 'top'){
18395                 _this.picker().setTop(0 - _this.picker().getHeight());
18396                 return;
18397             }
18398             
18399             if(c == 'left'){
18400                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18401                 return;
18402             }
18403             if(c == 'right'){
18404                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18405                 return;
18406             }
18407         });
18408         
18409     },
18410   
18411     onFocus : function()
18412     {
18413         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18414         this.show();
18415     },
18416     
18417     onBlur : function()
18418     {
18419         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18420         this.hide();
18421     },
18422     
18423     show : function()
18424     {
18425         this.picker().show();
18426         this.pop.show();
18427         this.update();
18428         this.place();
18429         
18430         this.fireEvent('show', this, this.date);
18431     },
18432     
18433     hide : function()
18434     {
18435         this.picker().hide();
18436         this.pop.hide();
18437         
18438         this.fireEvent('hide', this, this.date);
18439     },
18440     
18441     setTime : function()
18442     {
18443         this.hide();
18444         this.setValue(this.time.format(this.format));
18445         
18446         this.fireEvent('select', this, this.date);
18447         
18448         
18449     },
18450     
18451     onMousedown: function(e){
18452         e.stopPropagation();
18453         e.preventDefault();
18454     },
18455     
18456     onIncrementHours: function()
18457     {
18458         Roo.log('onIncrementHours');
18459         this.time = this.time.add(Date.HOUR, 1);
18460         this.update();
18461         
18462     },
18463     
18464     onDecrementHours: function()
18465     {
18466         Roo.log('onDecrementHours');
18467         this.time = this.time.add(Date.HOUR, -1);
18468         this.update();
18469     },
18470     
18471     onIncrementMinutes: function()
18472     {
18473         Roo.log('onIncrementMinutes');
18474         this.time = this.time.add(Date.MINUTE, 1);
18475         this.update();
18476     },
18477     
18478     onDecrementMinutes: function()
18479     {
18480         Roo.log('onDecrementMinutes');
18481         this.time = this.time.add(Date.MINUTE, -1);
18482         this.update();
18483     },
18484     
18485     onTogglePeriod: function()
18486     {
18487         Roo.log('onTogglePeriod');
18488         this.time = this.time.add(Date.HOUR, 12);
18489         this.update();
18490     }
18491     
18492    
18493 });
18494
18495 Roo.apply(Roo.bootstrap.TimeField,  {
18496     
18497     content : {
18498         tag: 'tbody',
18499         cn: [
18500             {
18501                 tag: 'tr',
18502                 cn: [
18503                 {
18504                     tag: 'td',
18505                     colspan: '7'
18506                 }
18507                 ]
18508             }
18509         ]
18510     },
18511     
18512     footer : {
18513         tag: 'tfoot',
18514         cn: [
18515             {
18516                 tag: 'tr',
18517                 cn: [
18518                 {
18519                     tag: 'th',
18520                     colspan: '7',
18521                     cls: '',
18522                     cn: [
18523                         {
18524                             tag: 'button',
18525                             cls: 'btn btn-info ok',
18526                             html: 'OK'
18527                         }
18528                     ]
18529                 }
18530
18531                 ]
18532             }
18533         ]
18534     }
18535 });
18536
18537 Roo.apply(Roo.bootstrap.TimeField,  {
18538   
18539     template : {
18540         tag: 'div',
18541         cls: 'datepicker dropdown-menu',
18542         cn: [
18543             {
18544                 tag: 'div',
18545                 cls: 'datepicker-time',
18546                 cn: [
18547                 {
18548                     tag: 'table',
18549                     cls: 'table-condensed',
18550                     cn:[
18551                     Roo.bootstrap.TimeField.content,
18552                     Roo.bootstrap.TimeField.footer
18553                     ]
18554                 }
18555                 ]
18556             }
18557         ]
18558     }
18559 });
18560
18561  
18562
18563  /*
18564  * - LGPL
18565  *
18566  * MonthField
18567  * 
18568  */
18569
18570 /**
18571  * @class Roo.bootstrap.MonthField
18572  * @extends Roo.bootstrap.Input
18573  * Bootstrap MonthField class
18574  * 
18575  * @cfg {String} language default en
18576  * 
18577  * @constructor
18578  * Create a new MonthField
18579  * @param {Object} config The config object
18580  */
18581
18582 Roo.bootstrap.MonthField = function(config){
18583     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18584     
18585     this.addEvents({
18586         /**
18587          * @event show
18588          * Fires when this field show.
18589          * @param {Roo.bootstrap.MonthField} this
18590          * @param {Mixed} date The date value
18591          */
18592         show : true,
18593         /**
18594          * @event show
18595          * Fires when this field hide.
18596          * @param {Roo.bootstrap.MonthField} this
18597          * @param {Mixed} date The date value
18598          */
18599         hide : true,
18600         /**
18601          * @event select
18602          * Fires when select a date.
18603          * @param {Roo.bootstrap.MonthField} this
18604          * @param {String} oldvalue The old value
18605          * @param {String} newvalue The new value
18606          */
18607         select : true
18608     });
18609 };
18610
18611 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18612     
18613     onRender: function(ct, position)
18614     {
18615         
18616         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18617         
18618         this.language = this.language || 'en';
18619         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18620         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18621         
18622         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18623         this.isInline = false;
18624         this.isInput = true;
18625         this.component = this.el.select('.add-on', true).first() || false;
18626         this.component = (this.component && this.component.length === 0) ? false : this.component;
18627         this.hasInput = this.component && this.inputEL().length;
18628         
18629         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18630         
18631         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18632         
18633         this.picker().on('mousedown', this.onMousedown, this);
18634         this.picker().on('click', this.onClick, this);
18635         
18636         this.picker().addClass('datepicker-dropdown');
18637         
18638         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18639             v.setStyle('width', '189px');
18640         });
18641         
18642         this.fillMonths();
18643         
18644         this.update();
18645         
18646         if(this.isInline) {
18647             this.show();
18648         }
18649         
18650     },
18651     
18652     setValue: function(v, suppressEvent)
18653     {   
18654         var o = this.getValue();
18655         
18656         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18657         
18658         this.update();
18659
18660         if(suppressEvent !== true){
18661             this.fireEvent('select', this, o, v);
18662         }
18663         
18664     },
18665     
18666     getValue: function()
18667     {
18668         return this.value;
18669     },
18670     
18671     onClick: function(e) 
18672     {
18673         e.stopPropagation();
18674         e.preventDefault();
18675         
18676         var target = e.getTarget();
18677         
18678         if(target.nodeName.toLowerCase() === 'i'){
18679             target = Roo.get(target).dom.parentNode;
18680         }
18681         
18682         var nodeName = target.nodeName;
18683         var className = target.className;
18684         var html = target.innerHTML;
18685         
18686         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18687             return;
18688         }
18689         
18690         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18691         
18692         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18693         
18694         this.hide();
18695                         
18696     },
18697     
18698     picker : function()
18699     {
18700         return this.pickerEl;
18701     },
18702     
18703     fillMonths: function()
18704     {    
18705         var i = 0;
18706         var months = this.picker().select('>.datepicker-months td', true).first();
18707         
18708         months.dom.innerHTML = '';
18709         
18710         while (i < 12) {
18711             var month = {
18712                 tag: 'span',
18713                 cls: 'month',
18714                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18715             };
18716             
18717             months.createChild(month);
18718         }
18719         
18720     },
18721     
18722     update: function()
18723     {
18724         var _this = this;
18725         
18726         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18727             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18728         }
18729         
18730         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18731             e.removeClass('active');
18732             
18733             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18734                 e.addClass('active');
18735             }
18736         })
18737     },
18738     
18739     place: function()
18740     {
18741         if(this.isInline) {
18742             return;
18743         }
18744         
18745         this.picker().removeClass(['bottom', 'top']);
18746         
18747         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18748             /*
18749              * place to the top of element!
18750              *
18751              */
18752             
18753             this.picker().addClass('top');
18754             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18755             
18756             return;
18757         }
18758         
18759         this.picker().addClass('bottom');
18760         
18761         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18762     },
18763     
18764     onFocus : function()
18765     {
18766         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18767         this.show();
18768     },
18769     
18770     onBlur : function()
18771     {
18772         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18773         
18774         var d = this.inputEl().getValue();
18775         
18776         this.setValue(d);
18777                 
18778         this.hide();
18779     },
18780     
18781     show : function()
18782     {
18783         this.picker().show();
18784         this.picker().select('>.datepicker-months', true).first().show();
18785         this.update();
18786         this.place();
18787         
18788         this.fireEvent('show', this, this.date);
18789     },
18790     
18791     hide : function()
18792     {
18793         if(this.isInline) {
18794             return;
18795         }
18796         this.picker().hide();
18797         this.fireEvent('hide', this, this.date);
18798         
18799     },
18800     
18801     onMousedown: function(e)
18802     {
18803         e.stopPropagation();
18804         e.preventDefault();
18805     },
18806     
18807     keyup: function(e)
18808     {
18809         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18810         this.update();
18811     },
18812
18813     fireKey: function(e)
18814     {
18815         if (!this.picker().isVisible()){
18816             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18817                 this.show();
18818             }
18819             return;
18820         }
18821         
18822         var dir;
18823         
18824         switch(e.keyCode){
18825             case 27: // escape
18826                 this.hide();
18827                 e.preventDefault();
18828                 break;
18829             case 37: // left
18830             case 39: // right
18831                 dir = e.keyCode == 37 ? -1 : 1;
18832                 
18833                 this.vIndex = this.vIndex + dir;
18834                 
18835                 if(this.vIndex < 0){
18836                     this.vIndex = 0;
18837                 }
18838                 
18839                 if(this.vIndex > 11){
18840                     this.vIndex = 11;
18841                 }
18842                 
18843                 if(isNaN(this.vIndex)){
18844                     this.vIndex = 0;
18845                 }
18846                 
18847                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18848                 
18849                 break;
18850             case 38: // up
18851             case 40: // down
18852                 
18853                 dir = e.keyCode == 38 ? -1 : 1;
18854                 
18855                 this.vIndex = this.vIndex + dir * 4;
18856                 
18857                 if(this.vIndex < 0){
18858                     this.vIndex = 0;
18859                 }
18860                 
18861                 if(this.vIndex > 11){
18862                     this.vIndex = 11;
18863                 }
18864                 
18865                 if(isNaN(this.vIndex)){
18866                     this.vIndex = 0;
18867                 }
18868                 
18869                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18870                 break;
18871                 
18872             case 13: // enter
18873                 
18874                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18875                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18876                 }
18877                 
18878                 this.hide();
18879                 e.preventDefault();
18880                 break;
18881             case 9: // tab
18882                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18883                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18884                 }
18885                 this.hide();
18886                 break;
18887             case 16: // shift
18888             case 17: // ctrl
18889             case 18: // alt
18890                 break;
18891             default :
18892                 this.hide();
18893                 
18894         }
18895     },
18896     
18897     remove: function() 
18898     {
18899         this.picker().remove();
18900     }
18901    
18902 });
18903
18904 Roo.apply(Roo.bootstrap.MonthField,  {
18905     
18906     content : {
18907         tag: 'tbody',
18908         cn: [
18909         {
18910             tag: 'tr',
18911             cn: [
18912             {
18913                 tag: 'td',
18914                 colspan: '7'
18915             }
18916             ]
18917         }
18918         ]
18919     },
18920     
18921     dates:{
18922         en: {
18923             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18924             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18925         }
18926     }
18927 });
18928
18929 Roo.apply(Roo.bootstrap.MonthField,  {
18930   
18931     template : {
18932         tag: 'div',
18933         cls: 'datepicker dropdown-menu roo-dynamic',
18934         cn: [
18935             {
18936                 tag: 'div',
18937                 cls: 'datepicker-months',
18938                 cn: [
18939                 {
18940                     tag: 'table',
18941                     cls: 'table-condensed',
18942                     cn:[
18943                         Roo.bootstrap.DateField.content
18944                     ]
18945                 }
18946                 ]
18947             }
18948         ]
18949     }
18950 });
18951
18952  
18953
18954  
18955  /*
18956  * - LGPL
18957  *
18958  * CheckBox
18959  * 
18960  */
18961
18962 /**
18963  * @class Roo.bootstrap.CheckBox
18964  * @extends Roo.bootstrap.Input
18965  * Bootstrap CheckBox class
18966  * 
18967  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18968  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18969  * @cfg {String} boxLabel The text that appears beside the checkbox
18970  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18971  * @cfg {Boolean} checked initnal the element
18972  * @cfg {Boolean} inline inline the element (default false)
18973  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18974  * 
18975  * @constructor
18976  * Create a new CheckBox
18977  * @param {Object} config The config object
18978  */
18979
18980 Roo.bootstrap.CheckBox = function(config){
18981     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18982    
18983     this.addEvents({
18984         /**
18985         * @event check
18986         * Fires when the element is checked or unchecked.
18987         * @param {Roo.bootstrap.CheckBox} this This input
18988         * @param {Boolean} checked The new checked value
18989         */
18990        check : true
18991     });
18992     
18993 };
18994
18995 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18996   
18997     inputType: 'checkbox',
18998     inputValue: 1,
18999     valueOff: 0,
19000     boxLabel: false,
19001     checked: false,
19002     weight : false,
19003     inline: false,
19004     
19005     getAutoCreate : function()
19006     {
19007         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19008         
19009         var id = Roo.id();
19010         
19011         var cfg = {};
19012         
19013         cfg.cls = 'form-group ' + this.inputType; //input-group
19014         
19015         if(this.inline){
19016             cfg.cls += ' ' + this.inputType + '-inline';
19017         }
19018         
19019         var input =  {
19020             tag: 'input',
19021             id : id,
19022             type : this.inputType,
19023             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19024             cls : 'roo-' + this.inputType, //'form-box',
19025             placeholder : this.placeholder || ''
19026             
19027         };
19028         
19029         if (this.weight) { // Validity check?
19030             cfg.cls += " " + this.inputType + "-" + this.weight;
19031         }
19032         
19033         if (this.disabled) {
19034             input.disabled=true;
19035         }
19036         
19037         if(this.checked){
19038             input.checked = this.checked;
19039         }
19040         
19041         if (this.name) {
19042             input.name = this.name;
19043         }
19044         
19045         if (this.size) {
19046             input.cls += ' input-' + this.size;
19047         }
19048         
19049         var settings=this;
19050         
19051         ['xs','sm','md','lg'].map(function(size){
19052             if (settings[size]) {
19053                 cfg.cls += ' col-' + size + '-' + settings[size];
19054             }
19055         });
19056         
19057         var inputblock = input;
19058          
19059         if (this.before || this.after) {
19060             
19061             inputblock = {
19062                 cls : 'input-group',
19063                 cn :  [] 
19064             };
19065             
19066             if (this.before) {
19067                 inputblock.cn.push({
19068                     tag :'span',
19069                     cls : 'input-group-addon',
19070                     html : this.before
19071                 });
19072             }
19073             
19074             inputblock.cn.push(input);
19075             
19076             if (this.after) {
19077                 inputblock.cn.push({
19078                     tag :'span',
19079                     cls : 'input-group-addon',
19080                     html : this.after
19081                 });
19082             }
19083             
19084         }
19085         
19086         if (align ==='left' && this.fieldLabel.length) {
19087 //                Roo.log("left and has label");
19088                 cfg.cn = [
19089                     
19090                     {
19091                         tag: 'label',
19092                         'for' :  id,
19093                         cls : 'control-label col-md-' + this.labelWidth,
19094                         html : this.fieldLabel
19095                         
19096                     },
19097                     {
19098                         cls : "col-md-" + (12 - this.labelWidth), 
19099                         cn: [
19100                             inputblock
19101                         ]
19102                     }
19103                     
19104                 ];
19105         } else if ( this.fieldLabel.length) {
19106 //                Roo.log(" label");
19107                 cfg.cn = [
19108                    
19109                     {
19110                         tag: this.boxLabel ? 'span' : 'label',
19111                         'for': id,
19112                         cls: 'control-label box-input-label',
19113                         //cls : 'input-group-addon',
19114                         html : this.fieldLabel
19115                         
19116                     },
19117                     
19118                     inputblock
19119                     
19120                 ];
19121
19122         } else {
19123             
19124 //                Roo.log(" no label && no align");
19125                 cfg.cn = [  inputblock ] ;
19126                 
19127                 
19128         }
19129         
19130         if(this.boxLabel){
19131              var boxLabelCfg = {
19132                 tag: 'label',
19133                 //'for': id, // box label is handled by onclick - so no for...
19134                 cls: 'box-label',
19135                 html: this.boxLabel
19136             };
19137             
19138             if(this.tooltip){
19139                 boxLabelCfg.tooltip = this.tooltip;
19140             }
19141              
19142             cfg.cn.push(boxLabelCfg);
19143         }
19144         
19145         
19146        
19147         return cfg;
19148         
19149     },
19150     
19151     /**
19152      * return the real input element.
19153      */
19154     inputEl: function ()
19155     {
19156         return this.el.select('input.roo-' + this.inputType,true).first();
19157     },
19158     
19159     labelEl: function()
19160     {
19161         return this.el.select('label.control-label',true).first();
19162     },
19163     /* depricated... */
19164     
19165     label: function()
19166     {
19167         return this.labelEl();
19168     },
19169     
19170     boxLabelEl: function()
19171     {
19172         return this.el.select('label.box-label',true).first();
19173     },
19174     
19175     initEvents : function()
19176     {
19177 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19178         
19179         this.inputEl().on('click', this.onClick,  this);
19180         
19181         if (this.boxLabel) { 
19182             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19183         }
19184         
19185         this.startValue = this.getValue();
19186         
19187         if(this.groupId){
19188             Roo.bootstrap.CheckBox.register(this);
19189         }
19190     },
19191     
19192     onClick : function()
19193     {   
19194         this.setChecked(!this.checked);
19195     },
19196     
19197     setChecked : function(state,suppressEvent)
19198     {
19199         this.startValue = this.getValue();
19200         
19201         if(this.inputType == 'radio'){
19202             
19203             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19204                 e.dom.checked = false;
19205             });
19206             
19207             this.inputEl().dom.checked = true;
19208             
19209             this.inputEl().dom.value = this.inputValue;
19210             
19211             if(suppressEvent !== true){
19212                 this.fireEvent('check', this, true);
19213             }
19214             
19215             this.validate();
19216             
19217             return;
19218         }
19219         
19220         this.checked = state;
19221         
19222         this.inputEl().dom.checked = state;
19223         
19224         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19225         
19226         if(suppressEvent !== true){
19227             this.fireEvent('check', this, state);
19228         }
19229         
19230         this.validate();
19231     },
19232     
19233     getValue : function()
19234     {
19235         if(this.inputType == 'radio'){
19236             return this.getGroupValue();
19237         }
19238         
19239         return this.inputEl().getValue();
19240         
19241     },
19242     
19243     getGroupValue : function()
19244     {
19245         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19246             return '';
19247         }
19248         
19249         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19250     },
19251     
19252     setValue : function(v,suppressEvent)
19253     {
19254         if(this.inputType == 'radio'){
19255             this.setGroupValue(v, suppressEvent);
19256             return;
19257         }
19258         
19259         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19260         
19261         this.validate();
19262     },
19263     
19264     setGroupValue : function(v, suppressEvent)
19265     {
19266         this.startValue = this.getValue();
19267         
19268         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19269             e.dom.checked = false;
19270             
19271             if(e.dom.value == v){
19272                 e.dom.checked = true;
19273             }
19274         });
19275         
19276         if(suppressEvent !== true){
19277             this.fireEvent('check', this, true);
19278         }
19279
19280         this.validate();
19281         
19282         return;
19283     },
19284     
19285     validate : function()
19286     {
19287         if(
19288                 this.disabled || 
19289                 (this.inputType == 'radio' && this.validateRadio()) ||
19290                 (this.inputType == 'checkbox' && this.validateCheckbox())
19291         ){
19292             this.markValid();
19293             return true;
19294         }
19295         
19296         this.markInvalid();
19297         return false;
19298     },
19299     
19300     validateRadio : function()
19301     {
19302         var valid = false;
19303         
19304         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19305             if(!e.dom.checked){
19306                 return;
19307             }
19308             
19309             valid = true;
19310             
19311             return false;
19312         });
19313         
19314         return valid;
19315     },
19316     
19317     validateCheckbox : function()
19318     {
19319         if(!this.groupId){
19320             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19321         }
19322         
19323         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19324         
19325         if(!group){
19326             return false;
19327         }
19328         
19329         var r = false;
19330         
19331         for(var i in group){
19332             if(r){
19333                 break;
19334             }
19335             
19336             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19337         }
19338         
19339         return r;
19340     },
19341     
19342     /**
19343      * Mark this field as valid
19344      */
19345     markValid : function()
19346     {
19347         if(this.allowBlank){
19348             return;
19349         }
19350         
19351         var _this = this;
19352         
19353         this.fireEvent('valid', this);
19354         
19355         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19356         
19357         if(this.groupId){
19358             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19359         }
19360         
19361         if(label){
19362             label.markValid();
19363         }
19364         
19365         if(this.inputType == 'radio'){
19366             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19367                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19368                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19369             });
19370             
19371             return;
19372         }
19373         
19374         if(!this.groupId){
19375             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19376             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19377             return;
19378         }
19379         
19380         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19381             
19382         if(!group){
19383             return;
19384         }
19385         
19386         for(var i in group){
19387             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19388             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19389         }
19390     },
19391     
19392      /**
19393      * Mark this field as invalid
19394      * @param {String} msg The validation message
19395      */
19396     markInvalid : function(msg)
19397     {
19398         if(this.allowBlank){
19399             return;
19400         }
19401         
19402         var _this = this;
19403         
19404         this.fireEvent('invalid', this, msg);
19405         
19406         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19407         
19408         if(this.groupId){
19409             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19410         }
19411         
19412         if(label){
19413             label.markInvalid();
19414         }
19415             
19416         if(this.inputType == 'radio'){
19417             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19418                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19419                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19420             });
19421             
19422             return;
19423         }
19424         
19425         if(!this.groupId){
19426             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19427             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19428             return;
19429         }
19430         
19431         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19432         
19433         if(!group){
19434             return;
19435         }
19436         
19437         for(var i in group){
19438             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19439             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19440         }
19441         
19442     }
19443     
19444 });
19445
19446 Roo.apply(Roo.bootstrap.CheckBox, {
19447     
19448     groups: {},
19449     
19450      /**
19451     * register a CheckBox Group
19452     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19453     */
19454     register : function(checkbox)
19455     {
19456         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19457             this.groups[checkbox.groupId] = {};
19458         }
19459         
19460         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19461             return;
19462         }
19463         
19464         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19465         
19466     },
19467     /**
19468     * fetch a CheckBox Group based on the group ID
19469     * @param {string} the group ID
19470     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19471     */
19472     get: function(groupId) {
19473         if (typeof(this.groups[groupId]) == 'undefined') {
19474             return false;
19475         }
19476         
19477         return this.groups[groupId] ;
19478     }
19479     
19480     
19481 });
19482 /*
19483  * - LGPL
19484  *
19485  * Radio
19486  *
19487  *
19488  * not inline
19489  *<div class="radio">
19490   <label>
19491     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19492     Option one is this and that&mdash;be sure to include why it's great
19493   </label>
19494 </div>
19495  *
19496  *
19497  *inline
19498  *<span>
19499  *<label class="radio-inline">fieldLabel</label>
19500  *<label class="radio-inline">
19501   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19502 </label>
19503 <span>
19504  * 
19505  * 
19506  */
19507
19508 /**
19509  * @class Roo.bootstrap.Radio
19510  * @extends Roo.bootstrap.CheckBox
19511  * Bootstrap Radio class
19512
19513  * @constructor
19514  * Create a new Radio
19515  * @param {Object} config The config object
19516  */
19517
19518 Roo.bootstrap.Radio = function(config){
19519     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19520    
19521 };
19522
19523 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19524     
19525     inputType: 'radio',
19526     inputValue: '',
19527     valueOff: '',
19528     
19529     getAutoCreate : function()
19530     {
19531         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19532         align = align || 'left'; // default...
19533         
19534         
19535         
19536         var id = Roo.id();
19537         
19538         var cfg = {
19539                 tag : this.inline ? 'span' : 'div',
19540                 cls : '',
19541                 cn : []
19542         };
19543         
19544         var inline = this.inline ? ' radio-inline' : '';
19545         
19546         var lbl = {
19547                 tag: 'label' ,
19548                 // does not need for, as we wrap the input with it..
19549                 'for' : id,
19550                 cls : 'control-label box-label' + inline,
19551                 cn : []
19552         };
19553         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19554         
19555         var fieldLabel = {
19556             tag: 'label' ,
19557             //cls : 'control-label' + inline,
19558             html : this.fieldLabel,
19559             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19560         };
19561         
19562  
19563         
19564         
19565         var input =  {
19566             tag: 'input',
19567             id : id,
19568             type : this.inputType,
19569             //value : (!this.checked) ? this.valueOff : this.inputValue,
19570             value : this.inputValue,
19571             cls : 'roo-radio',
19572             placeholder : this.placeholder || '' // ?? needed????
19573             
19574         };
19575         if (this.weight) { // Validity check?
19576             input.cls += " radio-" + this.weight;
19577         }
19578         if (this.disabled) {
19579             input.disabled=true;
19580         }
19581         
19582         if(this.checked){
19583             input.checked = this.checked;
19584         }
19585         
19586         if (this.name) {
19587             input.name = this.name;
19588         }
19589         
19590         if (this.size) {
19591             input.cls += ' input-' + this.size;
19592         }
19593         
19594         //?? can span's inline have a width??
19595         
19596         var settings=this;
19597         ['xs','sm','md','lg'].map(function(size){
19598             if (settings[size]) {
19599                 cfg.cls += ' col-' + size + '-' + settings[size];
19600             }
19601         });
19602         
19603         var inputblock = input;
19604         
19605         if (this.before || this.after) {
19606             
19607             inputblock = {
19608                 cls : 'input-group',
19609                 tag : 'span',
19610                 cn :  [] 
19611             };
19612             if (this.before) {
19613                 inputblock.cn.push({
19614                     tag :'span',
19615                     cls : 'input-group-addon',
19616                     html : this.before
19617                 });
19618             }
19619             inputblock.cn.push(input);
19620             if (this.after) {
19621                 inputblock.cn.push({
19622                     tag :'span',
19623                     cls : 'input-group-addon',
19624                     html : this.after
19625                 });
19626             }
19627             
19628         };
19629         
19630         
19631         if (this.fieldLabel && this.fieldLabel.length) {
19632             cfg.cn.push(fieldLabel);
19633         }
19634        
19635         // normal bootstrap puts the input inside the label.
19636         // however with our styled version - it has to go after the input.
19637        
19638         //lbl.cn.push(inputblock);
19639         
19640         var lblwrap =  {
19641             tag: 'span',
19642             cls: 'radio' + inline,
19643             cn: [
19644                 inputblock,
19645                 lbl
19646             ]
19647         };
19648         
19649         cfg.cn.push( lblwrap);
19650         
19651         if(this.boxLabel){
19652             lbl.cn.push({
19653                 tag: 'span',
19654                 html: this.boxLabel
19655             })
19656         }
19657          
19658         
19659         return cfg;
19660         
19661     },
19662     
19663     initEvents : function()
19664     {
19665 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19666         
19667         this.inputEl().on('click', this.onClick,  this);
19668         if (this.boxLabel) {
19669             //Roo.log('find label');
19670             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19671         }
19672         
19673     },
19674     
19675     inputEl: function ()
19676     {
19677         return this.el.select('input.roo-radio',true).first();
19678     },
19679     onClick : function()
19680     {   
19681         Roo.log("click");
19682         this.setChecked(true);
19683     },
19684     
19685     setChecked : function(state,suppressEvent)
19686     {
19687         if(state){
19688             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19689                 v.dom.checked = false;
19690             });
19691         }
19692         Roo.log(this.inputEl().dom);
19693         this.checked = state;
19694         this.inputEl().dom.checked = state;
19695         
19696         if(suppressEvent !== true){
19697             this.fireEvent('check', this, state);
19698         }
19699         
19700         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19701         
19702     },
19703     
19704     getGroupValue : function()
19705     {
19706         var value = '';
19707         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19708             if(v.dom.checked == true){
19709                 value = v.dom.value;
19710             }
19711         });
19712         
19713         return value;
19714     },
19715     
19716     /**
19717      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19718      * @return {Mixed} value The field value
19719      */
19720     getValue : function(){
19721         return this.getGroupValue();
19722     }
19723     
19724 });
19725
19726  
19727 //<script type="text/javascript">
19728
19729 /*
19730  * Based  Ext JS Library 1.1.1
19731  * Copyright(c) 2006-2007, Ext JS, LLC.
19732  * LGPL
19733  *
19734  */
19735  
19736 /**
19737  * @class Roo.HtmlEditorCore
19738  * @extends Roo.Component
19739  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19740  *
19741  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19742  */
19743
19744 Roo.HtmlEditorCore = function(config){
19745     
19746     
19747     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19748     
19749     
19750     this.addEvents({
19751         /**
19752          * @event initialize
19753          * Fires when the editor is fully initialized (including the iframe)
19754          * @param {Roo.HtmlEditorCore} this
19755          */
19756         initialize: true,
19757         /**
19758          * @event activate
19759          * Fires when the editor is first receives the focus. Any insertion must wait
19760          * until after this event.
19761          * @param {Roo.HtmlEditorCore} this
19762          */
19763         activate: true,
19764          /**
19765          * @event beforesync
19766          * Fires before the textarea is updated with content from the editor iframe. Return false
19767          * to cancel the sync.
19768          * @param {Roo.HtmlEditorCore} this
19769          * @param {String} html
19770          */
19771         beforesync: true,
19772          /**
19773          * @event beforepush
19774          * Fires before the iframe editor is updated with content from the textarea. Return false
19775          * to cancel the push.
19776          * @param {Roo.HtmlEditorCore} this
19777          * @param {String} html
19778          */
19779         beforepush: true,
19780          /**
19781          * @event sync
19782          * Fires when the textarea is updated with content from the editor iframe.
19783          * @param {Roo.HtmlEditorCore} this
19784          * @param {String} html
19785          */
19786         sync: true,
19787          /**
19788          * @event push
19789          * Fires when the iframe editor is updated with content from the textarea.
19790          * @param {Roo.HtmlEditorCore} this
19791          * @param {String} html
19792          */
19793         push: true,
19794         
19795         /**
19796          * @event editorevent
19797          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19798          * @param {Roo.HtmlEditorCore} this
19799          */
19800         editorevent: true
19801         
19802     });
19803     
19804     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19805     
19806     // defaults : white / black...
19807     this.applyBlacklists();
19808     
19809     
19810     
19811 };
19812
19813
19814 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19815
19816
19817      /**
19818      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19819      */
19820     
19821     owner : false,
19822     
19823      /**
19824      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19825      *                        Roo.resizable.
19826      */
19827     resizable : false,
19828      /**
19829      * @cfg {Number} height (in pixels)
19830      */   
19831     height: 300,
19832    /**
19833      * @cfg {Number} width (in pixels)
19834      */   
19835     width: 500,
19836     
19837     /**
19838      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19839      * 
19840      */
19841     stylesheets: false,
19842     
19843     // id of frame..
19844     frameId: false,
19845     
19846     // private properties
19847     validationEvent : false,
19848     deferHeight: true,
19849     initialized : false,
19850     activated : false,
19851     sourceEditMode : false,
19852     onFocus : Roo.emptyFn,
19853     iframePad:3,
19854     hideMode:'offsets',
19855     
19856     clearUp: true,
19857     
19858     // blacklist + whitelisted elements..
19859     black: false,
19860     white: false,
19861      
19862     
19863
19864     /**
19865      * Protected method that will not generally be called directly. It
19866      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19867      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19868      */
19869     getDocMarkup : function(){
19870         // body styles..
19871         var st = '';
19872         
19873         // inherit styels from page...?? 
19874         if (this.stylesheets === false) {
19875             
19876             Roo.get(document.head).select('style').each(function(node) {
19877                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19878             });
19879             
19880             Roo.get(document.head).select('link').each(function(node) { 
19881                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19882             });
19883             
19884         } else if (!this.stylesheets.length) {
19885                 // simple..
19886                 st = '<style type="text/css">' +
19887                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19888                    '</style>';
19889         } else { 
19890             
19891         }
19892         
19893         st +=  '<style type="text/css">' +
19894             'IMG { cursor: pointer } ' +
19895         '</style>';
19896
19897         
19898         return '<html><head>' + st  +
19899             //<style type="text/css">' +
19900             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19901             //'</style>' +
19902             ' </head><body class="roo-htmleditor-body"></body></html>';
19903     },
19904
19905     // private
19906     onRender : function(ct, position)
19907     {
19908         var _t = this;
19909         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19910         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19911         
19912         
19913         this.el.dom.style.border = '0 none';
19914         this.el.dom.setAttribute('tabIndex', -1);
19915         this.el.addClass('x-hidden hide');
19916         
19917         
19918         
19919         if(Roo.isIE){ // fix IE 1px bogus margin
19920             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19921         }
19922        
19923         
19924         this.frameId = Roo.id();
19925         
19926          
19927         
19928         var iframe = this.owner.wrap.createChild({
19929             tag: 'iframe',
19930             cls: 'form-control', // bootstrap..
19931             id: this.frameId,
19932             name: this.frameId,
19933             frameBorder : 'no',
19934             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19935         }, this.el
19936         );
19937         
19938         
19939         this.iframe = iframe.dom;
19940
19941          this.assignDocWin();
19942         
19943         this.doc.designMode = 'on';
19944        
19945         this.doc.open();
19946         this.doc.write(this.getDocMarkup());
19947         this.doc.close();
19948
19949         
19950         var task = { // must defer to wait for browser to be ready
19951             run : function(){
19952                 //console.log("run task?" + this.doc.readyState);
19953                 this.assignDocWin();
19954                 if(this.doc.body || this.doc.readyState == 'complete'){
19955                     try {
19956                         this.doc.designMode="on";
19957                     } catch (e) {
19958                         return;
19959                     }
19960                     Roo.TaskMgr.stop(task);
19961                     this.initEditor.defer(10, this);
19962                 }
19963             },
19964             interval : 10,
19965             duration: 10000,
19966             scope: this
19967         };
19968         Roo.TaskMgr.start(task);
19969
19970     },
19971
19972     // private
19973     onResize : function(w, h)
19974     {
19975          Roo.log('resize: ' +w + ',' + h );
19976         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19977         if(!this.iframe){
19978             return;
19979         }
19980         if(typeof w == 'number'){
19981             
19982             this.iframe.style.width = w + 'px';
19983         }
19984         if(typeof h == 'number'){
19985             
19986             this.iframe.style.height = h + 'px';
19987             if(this.doc){
19988                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19989             }
19990         }
19991         
19992     },
19993
19994     /**
19995      * Toggles the editor between standard and source edit mode.
19996      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19997      */
19998     toggleSourceEdit : function(sourceEditMode){
19999         
20000         this.sourceEditMode = sourceEditMode === true;
20001         
20002         if(this.sourceEditMode){
20003  
20004             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20005             
20006         }else{
20007             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20008             //this.iframe.className = '';
20009             this.deferFocus();
20010         }
20011         //this.setSize(this.owner.wrap.getSize());
20012         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20013     },
20014
20015     
20016   
20017
20018     /**
20019      * Protected method that will not generally be called directly. If you need/want
20020      * custom HTML cleanup, this is the method you should override.
20021      * @param {String} html The HTML to be cleaned
20022      * return {String} The cleaned HTML
20023      */
20024     cleanHtml : function(html){
20025         html = String(html);
20026         if(html.length > 5){
20027             if(Roo.isSafari){ // strip safari nonsense
20028                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20029             }
20030         }
20031         if(html == '&nbsp;'){
20032             html = '';
20033         }
20034         return html;
20035     },
20036
20037     /**
20038      * HTML Editor -> Textarea
20039      * Protected method that will not generally be called directly. Syncs the contents
20040      * of the editor iframe with the textarea.
20041      */
20042     syncValue : function(){
20043         if(this.initialized){
20044             var bd = (this.doc.body || this.doc.documentElement);
20045             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20046             var html = bd.innerHTML;
20047             if(Roo.isSafari){
20048                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20049                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20050                 if(m && m[1]){
20051                     html = '<div style="'+m[0]+'">' + html + '</div>';
20052                 }
20053             }
20054             html = this.cleanHtml(html);
20055             // fix up the special chars.. normaly like back quotes in word...
20056             // however we do not want to do this with chinese..
20057             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20058                 var cc = b.charCodeAt();
20059                 if (
20060                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20061                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20062                     (cc >= 0xf900 && cc < 0xfb00 )
20063                 ) {
20064                         return b;
20065                 }
20066                 return "&#"+cc+";" 
20067             });
20068             if(this.owner.fireEvent('beforesync', this, html) !== false){
20069                 this.el.dom.value = html;
20070                 this.owner.fireEvent('sync', this, html);
20071             }
20072         }
20073     },
20074
20075     /**
20076      * Protected method that will not generally be called directly. Pushes the value of the textarea
20077      * into the iframe editor.
20078      */
20079     pushValue : function(){
20080         if(this.initialized){
20081             var v = this.el.dom.value.trim();
20082             
20083 //            if(v.length < 1){
20084 //                v = '&#160;';
20085 //            }
20086             
20087             if(this.owner.fireEvent('beforepush', this, v) !== false){
20088                 var d = (this.doc.body || this.doc.documentElement);
20089                 d.innerHTML = v;
20090                 this.cleanUpPaste();
20091                 this.el.dom.value = d.innerHTML;
20092                 this.owner.fireEvent('push', this, v);
20093             }
20094         }
20095     },
20096
20097     // private
20098     deferFocus : function(){
20099         this.focus.defer(10, this);
20100     },
20101
20102     // doc'ed in Field
20103     focus : function(){
20104         if(this.win && !this.sourceEditMode){
20105             this.win.focus();
20106         }else{
20107             this.el.focus();
20108         }
20109     },
20110     
20111     assignDocWin: function()
20112     {
20113         var iframe = this.iframe;
20114         
20115          if(Roo.isIE){
20116             this.doc = iframe.contentWindow.document;
20117             this.win = iframe.contentWindow;
20118         } else {
20119 //            if (!Roo.get(this.frameId)) {
20120 //                return;
20121 //            }
20122 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20123 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20124             
20125             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20126                 return;
20127             }
20128             
20129             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20130             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20131         }
20132     },
20133     
20134     // private
20135     initEditor : function(){
20136         //console.log("INIT EDITOR");
20137         this.assignDocWin();
20138         
20139         
20140         
20141         this.doc.designMode="on";
20142         this.doc.open();
20143         this.doc.write(this.getDocMarkup());
20144         this.doc.close();
20145         
20146         var dbody = (this.doc.body || this.doc.documentElement);
20147         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20148         // this copies styles from the containing element into thsi one..
20149         // not sure why we need all of this..
20150         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20151         
20152         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20153         //ss['background-attachment'] = 'fixed'; // w3c
20154         dbody.bgProperties = 'fixed'; // ie
20155         //Roo.DomHelper.applyStyles(dbody, ss);
20156         Roo.EventManager.on(this.doc, {
20157             //'mousedown': this.onEditorEvent,
20158             'mouseup': this.onEditorEvent,
20159             'dblclick': this.onEditorEvent,
20160             'click': this.onEditorEvent,
20161             'keyup': this.onEditorEvent,
20162             buffer:100,
20163             scope: this
20164         });
20165         if(Roo.isGecko){
20166             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20167         }
20168         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20169             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20170         }
20171         this.initialized = true;
20172
20173         this.owner.fireEvent('initialize', this);
20174         this.pushValue();
20175     },
20176
20177     // private
20178     onDestroy : function(){
20179         
20180         
20181         
20182         if(this.rendered){
20183             
20184             //for (var i =0; i < this.toolbars.length;i++) {
20185             //    // fixme - ask toolbars for heights?
20186             //    this.toolbars[i].onDestroy();
20187            // }
20188             
20189             //this.wrap.dom.innerHTML = '';
20190             //this.wrap.remove();
20191         }
20192     },
20193
20194     // private
20195     onFirstFocus : function(){
20196         
20197         this.assignDocWin();
20198         
20199         
20200         this.activated = true;
20201          
20202     
20203         if(Roo.isGecko){ // prevent silly gecko errors
20204             this.win.focus();
20205             var s = this.win.getSelection();
20206             if(!s.focusNode || s.focusNode.nodeType != 3){
20207                 var r = s.getRangeAt(0);
20208                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20209                 r.collapse(true);
20210                 this.deferFocus();
20211             }
20212             try{
20213                 this.execCmd('useCSS', true);
20214                 this.execCmd('styleWithCSS', false);
20215             }catch(e){}
20216         }
20217         this.owner.fireEvent('activate', this);
20218     },
20219
20220     // private
20221     adjustFont: function(btn){
20222         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20223         //if(Roo.isSafari){ // safari
20224         //    adjust *= 2;
20225        // }
20226         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20227         if(Roo.isSafari){ // safari
20228             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20229             v =  (v < 10) ? 10 : v;
20230             v =  (v > 48) ? 48 : v;
20231             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20232             
20233         }
20234         
20235         
20236         v = Math.max(1, v+adjust);
20237         
20238         this.execCmd('FontSize', v  );
20239     },
20240
20241     onEditorEvent : function(e)
20242     {
20243         this.owner.fireEvent('editorevent', this, e);
20244       //  this.updateToolbar();
20245         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20246     },
20247
20248     insertTag : function(tg)
20249     {
20250         // could be a bit smarter... -> wrap the current selected tRoo..
20251         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20252             
20253             range = this.createRange(this.getSelection());
20254             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20255             wrappingNode.appendChild(range.extractContents());
20256             range.insertNode(wrappingNode);
20257
20258             return;
20259             
20260             
20261             
20262         }
20263         this.execCmd("formatblock",   tg);
20264         
20265     },
20266     
20267     insertText : function(txt)
20268     {
20269         
20270         
20271         var range = this.createRange();
20272         range.deleteContents();
20273                //alert(Sender.getAttribute('label'));
20274                
20275         range.insertNode(this.doc.createTextNode(txt));
20276     } ,
20277     
20278      
20279
20280     /**
20281      * Executes a Midas editor command on the editor document and performs necessary focus and
20282      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20283      * @param {String} cmd The Midas command
20284      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20285      */
20286     relayCmd : function(cmd, value){
20287         this.win.focus();
20288         this.execCmd(cmd, value);
20289         this.owner.fireEvent('editorevent', this);
20290         //this.updateToolbar();
20291         this.owner.deferFocus();
20292     },
20293
20294     /**
20295      * Executes a Midas editor command directly on the editor document.
20296      * For visual commands, you should use {@link #relayCmd} instead.
20297      * <b>This should only be called after the editor is initialized.</b>
20298      * @param {String} cmd The Midas command
20299      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20300      */
20301     execCmd : function(cmd, value){
20302         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20303         this.syncValue();
20304     },
20305  
20306  
20307    
20308     /**
20309      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20310      * to insert tRoo.
20311      * @param {String} text | dom node.. 
20312      */
20313     insertAtCursor : function(text)
20314     {
20315         
20316         
20317         
20318         if(!this.activated){
20319             return;
20320         }
20321         /*
20322         if(Roo.isIE){
20323             this.win.focus();
20324             var r = this.doc.selection.createRange();
20325             if(r){
20326                 r.collapse(true);
20327                 r.pasteHTML(text);
20328                 this.syncValue();
20329                 this.deferFocus();
20330             
20331             }
20332             return;
20333         }
20334         */
20335         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20336             this.win.focus();
20337             
20338             
20339             // from jquery ui (MIT licenced)
20340             var range, node;
20341             var win = this.win;
20342             
20343             if (win.getSelection && win.getSelection().getRangeAt) {
20344                 range = win.getSelection().getRangeAt(0);
20345                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20346                 range.insertNode(node);
20347             } else if (win.document.selection && win.document.selection.createRange) {
20348                 // no firefox support
20349                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20350                 win.document.selection.createRange().pasteHTML(txt);
20351             } else {
20352                 // no firefox support
20353                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20354                 this.execCmd('InsertHTML', txt);
20355             } 
20356             
20357             this.syncValue();
20358             
20359             this.deferFocus();
20360         }
20361     },
20362  // private
20363     mozKeyPress : function(e){
20364         if(e.ctrlKey){
20365             var c = e.getCharCode(), cmd;
20366           
20367             if(c > 0){
20368                 c = String.fromCharCode(c).toLowerCase();
20369                 switch(c){
20370                     case 'b':
20371                         cmd = 'bold';
20372                         break;
20373                     case 'i':
20374                         cmd = 'italic';
20375                         break;
20376                     
20377                     case 'u':
20378                         cmd = 'underline';
20379                         break;
20380                     
20381                     case 'v':
20382                         this.cleanUpPaste.defer(100, this);
20383                         return;
20384                         
20385                 }
20386                 if(cmd){
20387                     this.win.focus();
20388                     this.execCmd(cmd);
20389                     this.deferFocus();
20390                     e.preventDefault();
20391                 }
20392                 
20393             }
20394         }
20395     },
20396
20397     // private
20398     fixKeys : function(){ // load time branching for fastest keydown performance
20399         if(Roo.isIE){
20400             return function(e){
20401                 var k = e.getKey(), r;
20402                 if(k == e.TAB){
20403                     e.stopEvent();
20404                     r = this.doc.selection.createRange();
20405                     if(r){
20406                         r.collapse(true);
20407                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20408                         this.deferFocus();
20409                     }
20410                     return;
20411                 }
20412                 
20413                 if(k == e.ENTER){
20414                     r = this.doc.selection.createRange();
20415                     if(r){
20416                         var target = r.parentElement();
20417                         if(!target || target.tagName.toLowerCase() != 'li'){
20418                             e.stopEvent();
20419                             r.pasteHTML('<br />');
20420                             r.collapse(false);
20421                             r.select();
20422                         }
20423                     }
20424                 }
20425                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20426                     this.cleanUpPaste.defer(100, this);
20427                     return;
20428                 }
20429                 
20430                 
20431             };
20432         }else if(Roo.isOpera){
20433             return function(e){
20434                 var k = e.getKey();
20435                 if(k == e.TAB){
20436                     e.stopEvent();
20437                     this.win.focus();
20438                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20439                     this.deferFocus();
20440                 }
20441                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20442                     this.cleanUpPaste.defer(100, this);
20443                     return;
20444                 }
20445                 
20446             };
20447         }else if(Roo.isSafari){
20448             return function(e){
20449                 var k = e.getKey();
20450                 
20451                 if(k == e.TAB){
20452                     e.stopEvent();
20453                     this.execCmd('InsertText','\t');
20454                     this.deferFocus();
20455                     return;
20456                 }
20457                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20458                     this.cleanUpPaste.defer(100, this);
20459                     return;
20460                 }
20461                 
20462              };
20463         }
20464     }(),
20465     
20466     getAllAncestors: function()
20467     {
20468         var p = this.getSelectedNode();
20469         var a = [];
20470         if (!p) {
20471             a.push(p); // push blank onto stack..
20472             p = this.getParentElement();
20473         }
20474         
20475         
20476         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20477             a.push(p);
20478             p = p.parentNode;
20479         }
20480         a.push(this.doc.body);
20481         return a;
20482     },
20483     lastSel : false,
20484     lastSelNode : false,
20485     
20486     
20487     getSelection : function() 
20488     {
20489         this.assignDocWin();
20490         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20491     },
20492     
20493     getSelectedNode: function() 
20494     {
20495         // this may only work on Gecko!!!
20496         
20497         // should we cache this!!!!
20498         
20499         
20500         
20501          
20502         var range = this.createRange(this.getSelection()).cloneRange();
20503         
20504         if (Roo.isIE) {
20505             var parent = range.parentElement();
20506             while (true) {
20507                 var testRange = range.duplicate();
20508                 testRange.moveToElementText(parent);
20509                 if (testRange.inRange(range)) {
20510                     break;
20511                 }
20512                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20513                     break;
20514                 }
20515                 parent = parent.parentElement;
20516             }
20517             return parent;
20518         }
20519         
20520         // is ancestor a text element.
20521         var ac =  range.commonAncestorContainer;
20522         if (ac.nodeType == 3) {
20523             ac = ac.parentNode;
20524         }
20525         
20526         var ar = ac.childNodes;
20527          
20528         var nodes = [];
20529         var other_nodes = [];
20530         var has_other_nodes = false;
20531         for (var i=0;i<ar.length;i++) {
20532             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20533                 continue;
20534             }
20535             // fullly contained node.
20536             
20537             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20538                 nodes.push(ar[i]);
20539                 continue;
20540             }
20541             
20542             // probably selected..
20543             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20544                 other_nodes.push(ar[i]);
20545                 continue;
20546             }
20547             // outer..
20548             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20549                 continue;
20550             }
20551             
20552             
20553             has_other_nodes = true;
20554         }
20555         if (!nodes.length && other_nodes.length) {
20556             nodes= other_nodes;
20557         }
20558         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20559             return false;
20560         }
20561         
20562         return nodes[0];
20563     },
20564     createRange: function(sel)
20565     {
20566         // this has strange effects when using with 
20567         // top toolbar - not sure if it's a great idea.
20568         //this.editor.contentWindow.focus();
20569         if (typeof sel != "undefined") {
20570             try {
20571                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20572             } catch(e) {
20573                 return this.doc.createRange();
20574             }
20575         } else {
20576             return this.doc.createRange();
20577         }
20578     },
20579     getParentElement: function()
20580     {
20581         
20582         this.assignDocWin();
20583         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20584         
20585         var range = this.createRange(sel);
20586          
20587         try {
20588             var p = range.commonAncestorContainer;
20589             while (p.nodeType == 3) { // text node
20590                 p = p.parentNode;
20591             }
20592             return p;
20593         } catch (e) {
20594             return null;
20595         }
20596     
20597     },
20598     /***
20599      *
20600      * Range intersection.. the hard stuff...
20601      *  '-1' = before
20602      *  '0' = hits..
20603      *  '1' = after.
20604      *         [ -- selected range --- ]
20605      *   [fail]                        [fail]
20606      *
20607      *    basically..
20608      *      if end is before start or  hits it. fail.
20609      *      if start is after end or hits it fail.
20610      *
20611      *   if either hits (but other is outside. - then it's not 
20612      *   
20613      *    
20614      **/
20615     
20616     
20617     // @see http://www.thismuchiknow.co.uk/?p=64.
20618     rangeIntersectsNode : function(range, node)
20619     {
20620         var nodeRange = node.ownerDocument.createRange();
20621         try {
20622             nodeRange.selectNode(node);
20623         } catch (e) {
20624             nodeRange.selectNodeContents(node);
20625         }
20626     
20627         var rangeStartRange = range.cloneRange();
20628         rangeStartRange.collapse(true);
20629     
20630         var rangeEndRange = range.cloneRange();
20631         rangeEndRange.collapse(false);
20632     
20633         var nodeStartRange = nodeRange.cloneRange();
20634         nodeStartRange.collapse(true);
20635     
20636         var nodeEndRange = nodeRange.cloneRange();
20637         nodeEndRange.collapse(false);
20638     
20639         return rangeStartRange.compareBoundaryPoints(
20640                  Range.START_TO_START, nodeEndRange) == -1 &&
20641                rangeEndRange.compareBoundaryPoints(
20642                  Range.START_TO_START, nodeStartRange) == 1;
20643         
20644          
20645     },
20646     rangeCompareNode : function(range, node)
20647     {
20648         var nodeRange = node.ownerDocument.createRange();
20649         try {
20650             nodeRange.selectNode(node);
20651         } catch (e) {
20652             nodeRange.selectNodeContents(node);
20653         }
20654         
20655         
20656         range.collapse(true);
20657     
20658         nodeRange.collapse(true);
20659      
20660         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20661         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20662          
20663         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20664         
20665         var nodeIsBefore   =  ss == 1;
20666         var nodeIsAfter    = ee == -1;
20667         
20668         if (nodeIsBefore && nodeIsAfter) {
20669             return 0; // outer
20670         }
20671         if (!nodeIsBefore && nodeIsAfter) {
20672             return 1; //right trailed.
20673         }
20674         
20675         if (nodeIsBefore && !nodeIsAfter) {
20676             return 2;  // left trailed.
20677         }
20678         // fully contined.
20679         return 3;
20680     },
20681
20682     // private? - in a new class?
20683     cleanUpPaste :  function()
20684     {
20685         // cleans up the whole document..
20686         Roo.log('cleanuppaste');
20687         
20688         this.cleanUpChildren(this.doc.body);
20689         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20690         if (clean != this.doc.body.innerHTML) {
20691             this.doc.body.innerHTML = clean;
20692         }
20693         
20694     },
20695     
20696     cleanWordChars : function(input) {// change the chars to hex code
20697         var he = Roo.HtmlEditorCore;
20698         
20699         var output = input;
20700         Roo.each(he.swapCodes, function(sw) { 
20701             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20702             
20703             output = output.replace(swapper, sw[1]);
20704         });
20705         
20706         return output;
20707     },
20708     
20709     
20710     cleanUpChildren : function (n)
20711     {
20712         if (!n.childNodes.length) {
20713             return;
20714         }
20715         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20716            this.cleanUpChild(n.childNodes[i]);
20717         }
20718     },
20719     
20720     
20721         
20722     
20723     cleanUpChild : function (node)
20724     {
20725         var ed = this;
20726         //console.log(node);
20727         if (node.nodeName == "#text") {
20728             // clean up silly Windows -- stuff?
20729             return; 
20730         }
20731         if (node.nodeName == "#comment") {
20732             node.parentNode.removeChild(node);
20733             // clean up silly Windows -- stuff?
20734             return; 
20735         }
20736         var lcname = node.tagName.toLowerCase();
20737         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20738         // whitelist of tags..
20739         
20740         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20741             // remove node.
20742             node.parentNode.removeChild(node);
20743             return;
20744             
20745         }
20746         
20747         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20748         
20749         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20750         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20751         
20752         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20753         //    remove_keep_children = true;
20754         //}
20755         
20756         if (remove_keep_children) {
20757             this.cleanUpChildren(node);
20758             // inserts everything just before this node...
20759             while (node.childNodes.length) {
20760                 var cn = node.childNodes[0];
20761                 node.removeChild(cn);
20762                 node.parentNode.insertBefore(cn, node);
20763             }
20764             node.parentNode.removeChild(node);
20765             return;
20766         }
20767         
20768         if (!node.attributes || !node.attributes.length) {
20769             this.cleanUpChildren(node);
20770             return;
20771         }
20772         
20773         function cleanAttr(n,v)
20774         {
20775             
20776             if (v.match(/^\./) || v.match(/^\//)) {
20777                 return;
20778             }
20779             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20780                 return;
20781             }
20782             if (v.match(/^#/)) {
20783                 return;
20784             }
20785 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20786             node.removeAttribute(n);
20787             
20788         }
20789         
20790         var cwhite = this.cwhite;
20791         var cblack = this.cblack;
20792             
20793         function cleanStyle(n,v)
20794         {
20795             if (v.match(/expression/)) { //XSS?? should we even bother..
20796                 node.removeAttribute(n);
20797                 return;
20798             }
20799             
20800             var parts = v.split(/;/);
20801             var clean = [];
20802             
20803             Roo.each(parts, function(p) {
20804                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20805                 if (!p.length) {
20806                     return true;
20807                 }
20808                 var l = p.split(':').shift().replace(/\s+/g,'');
20809                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20810                 
20811                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20812 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20813                     //node.removeAttribute(n);
20814                     return true;
20815                 }
20816                 //Roo.log()
20817                 // only allow 'c whitelisted system attributes'
20818                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20819 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20820                     //node.removeAttribute(n);
20821                     return true;
20822                 }
20823                 
20824                 
20825                  
20826                 
20827                 clean.push(p);
20828                 return true;
20829             });
20830             if (clean.length) { 
20831                 node.setAttribute(n, clean.join(';'));
20832             } else {
20833                 node.removeAttribute(n);
20834             }
20835             
20836         }
20837         
20838         
20839         for (var i = node.attributes.length-1; i > -1 ; i--) {
20840             var a = node.attributes[i];
20841             //console.log(a);
20842             
20843             if (a.name.toLowerCase().substr(0,2)=='on')  {
20844                 node.removeAttribute(a.name);
20845                 continue;
20846             }
20847             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20848                 node.removeAttribute(a.name);
20849                 continue;
20850             }
20851             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20852                 cleanAttr(a.name,a.value); // fixme..
20853                 continue;
20854             }
20855             if (a.name == 'style') {
20856                 cleanStyle(a.name,a.value);
20857                 continue;
20858             }
20859             /// clean up MS crap..
20860             // tecnically this should be a list of valid class'es..
20861             
20862             
20863             if (a.name == 'class') {
20864                 if (a.value.match(/^Mso/)) {
20865                     node.className = '';
20866                 }
20867                 
20868                 if (a.value.match(/body/)) {
20869                     node.className = '';
20870                 }
20871                 continue;
20872             }
20873             
20874             // style cleanup!?
20875             // class cleanup?
20876             
20877         }
20878         
20879         
20880         this.cleanUpChildren(node);
20881         
20882         
20883     },
20884     
20885     /**
20886      * Clean up MS wordisms...
20887      */
20888     cleanWord : function(node)
20889     {
20890         
20891         
20892         if (!node) {
20893             this.cleanWord(this.doc.body);
20894             return;
20895         }
20896         if (node.nodeName == "#text") {
20897             // clean up silly Windows -- stuff?
20898             return; 
20899         }
20900         if (node.nodeName == "#comment") {
20901             node.parentNode.removeChild(node);
20902             // clean up silly Windows -- stuff?
20903             return; 
20904         }
20905         
20906         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20907             node.parentNode.removeChild(node);
20908             return;
20909         }
20910         
20911         // remove - but keep children..
20912         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20913             while (node.childNodes.length) {
20914                 var cn = node.childNodes[0];
20915                 node.removeChild(cn);
20916                 node.parentNode.insertBefore(cn, node);
20917             }
20918             node.parentNode.removeChild(node);
20919             this.iterateChildren(node, this.cleanWord);
20920             return;
20921         }
20922         // clean styles
20923         if (node.className.length) {
20924             
20925             var cn = node.className.split(/\W+/);
20926             var cna = [];
20927             Roo.each(cn, function(cls) {
20928                 if (cls.match(/Mso[a-zA-Z]+/)) {
20929                     return;
20930                 }
20931                 cna.push(cls);
20932             });
20933             node.className = cna.length ? cna.join(' ') : '';
20934             if (!cna.length) {
20935                 node.removeAttribute("class");
20936             }
20937         }
20938         
20939         if (node.hasAttribute("lang")) {
20940             node.removeAttribute("lang");
20941         }
20942         
20943         if (node.hasAttribute("style")) {
20944             
20945             var styles = node.getAttribute("style").split(";");
20946             var nstyle = [];
20947             Roo.each(styles, function(s) {
20948                 if (!s.match(/:/)) {
20949                     return;
20950                 }
20951                 var kv = s.split(":");
20952                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20953                     return;
20954                 }
20955                 // what ever is left... we allow.
20956                 nstyle.push(s);
20957             });
20958             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20959             if (!nstyle.length) {
20960                 node.removeAttribute('style');
20961             }
20962         }
20963         this.iterateChildren(node, this.cleanWord);
20964         
20965         
20966         
20967     },
20968     /**
20969      * iterateChildren of a Node, calling fn each time, using this as the scole..
20970      * @param {DomNode} node node to iterate children of.
20971      * @param {Function} fn method of this class to call on each item.
20972      */
20973     iterateChildren : function(node, fn)
20974     {
20975         if (!node.childNodes.length) {
20976                 return;
20977         }
20978         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20979            fn.call(this, node.childNodes[i])
20980         }
20981     },
20982     
20983     
20984     /**
20985      * cleanTableWidths.
20986      *
20987      * Quite often pasting from word etc.. results in tables with column and widths.
20988      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20989      *
20990      */
20991     cleanTableWidths : function(node)
20992     {
20993          
20994          
20995         if (!node) {
20996             this.cleanTableWidths(this.doc.body);
20997             return;
20998         }
20999         
21000         // ignore list...
21001         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21002             return; 
21003         }
21004         Roo.log(node.tagName);
21005         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21006             this.iterateChildren(node, this.cleanTableWidths);
21007             return;
21008         }
21009         if (node.hasAttribute('width')) {
21010             node.removeAttribute('width');
21011         }
21012         
21013          
21014         if (node.hasAttribute("style")) {
21015             // pretty basic...
21016             
21017             var styles = node.getAttribute("style").split(";");
21018             var nstyle = [];
21019             Roo.each(styles, function(s) {
21020                 if (!s.match(/:/)) {
21021                     return;
21022                 }
21023                 var kv = s.split(":");
21024                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21025                     return;
21026                 }
21027                 // what ever is left... we allow.
21028                 nstyle.push(s);
21029             });
21030             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21031             if (!nstyle.length) {
21032                 node.removeAttribute('style');
21033             }
21034         }
21035         
21036         this.iterateChildren(node, this.cleanTableWidths);
21037         
21038         
21039     },
21040     
21041     
21042     
21043     
21044     domToHTML : function(currentElement, depth, nopadtext) {
21045         
21046         depth = depth || 0;
21047         nopadtext = nopadtext || false;
21048     
21049         if (!currentElement) {
21050             return this.domToHTML(this.doc.body);
21051         }
21052         
21053         //Roo.log(currentElement);
21054         var j;
21055         var allText = false;
21056         var nodeName = currentElement.nodeName;
21057         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21058         
21059         if  (nodeName == '#text') {
21060             
21061             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21062         }
21063         
21064         
21065         var ret = '';
21066         if (nodeName != 'BODY') {
21067              
21068             var i = 0;
21069             // Prints the node tagName, such as <A>, <IMG>, etc
21070             if (tagName) {
21071                 var attr = [];
21072                 for(i = 0; i < currentElement.attributes.length;i++) {
21073                     // quoting?
21074                     var aname = currentElement.attributes.item(i).name;
21075                     if (!currentElement.attributes.item(i).value.length) {
21076                         continue;
21077                     }
21078                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21079                 }
21080                 
21081                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21082             } 
21083             else {
21084                 
21085                 // eack
21086             }
21087         } else {
21088             tagName = false;
21089         }
21090         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21091             return ret;
21092         }
21093         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21094             nopadtext = true;
21095         }
21096         
21097         
21098         // Traverse the tree
21099         i = 0;
21100         var currentElementChild = currentElement.childNodes.item(i);
21101         var allText = true;
21102         var innerHTML  = '';
21103         lastnode = '';
21104         while (currentElementChild) {
21105             // Formatting code (indent the tree so it looks nice on the screen)
21106             var nopad = nopadtext;
21107             if (lastnode == 'SPAN') {
21108                 nopad  = true;
21109             }
21110             // text
21111             if  (currentElementChild.nodeName == '#text') {
21112                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21113                 toadd = nopadtext ? toadd : toadd.trim();
21114                 if (!nopad && toadd.length > 80) {
21115                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21116                 }
21117                 innerHTML  += toadd;
21118                 
21119                 i++;
21120                 currentElementChild = currentElement.childNodes.item(i);
21121                 lastNode = '';
21122                 continue;
21123             }
21124             allText = false;
21125             
21126             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21127                 
21128             // Recursively traverse the tree structure of the child node
21129             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21130             lastnode = currentElementChild.nodeName;
21131             i++;
21132             currentElementChild=currentElement.childNodes.item(i);
21133         }
21134         
21135         ret += innerHTML;
21136         
21137         if (!allText) {
21138                 // The remaining code is mostly for formatting the tree
21139             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21140         }
21141         
21142         
21143         if (tagName) {
21144             ret+= "</"+tagName+">";
21145         }
21146         return ret;
21147         
21148     },
21149         
21150     applyBlacklists : function()
21151     {
21152         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21153         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21154         
21155         this.white = [];
21156         this.black = [];
21157         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21158             if (b.indexOf(tag) > -1) {
21159                 return;
21160             }
21161             this.white.push(tag);
21162             
21163         }, this);
21164         
21165         Roo.each(w, function(tag) {
21166             if (b.indexOf(tag) > -1) {
21167                 return;
21168             }
21169             if (this.white.indexOf(tag) > -1) {
21170                 return;
21171             }
21172             this.white.push(tag);
21173             
21174         }, this);
21175         
21176         
21177         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21178             if (w.indexOf(tag) > -1) {
21179                 return;
21180             }
21181             this.black.push(tag);
21182             
21183         }, this);
21184         
21185         Roo.each(b, function(tag) {
21186             if (w.indexOf(tag) > -1) {
21187                 return;
21188             }
21189             if (this.black.indexOf(tag) > -1) {
21190                 return;
21191             }
21192             this.black.push(tag);
21193             
21194         }, this);
21195         
21196         
21197         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21198         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21199         
21200         this.cwhite = [];
21201         this.cblack = [];
21202         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21203             if (b.indexOf(tag) > -1) {
21204                 return;
21205             }
21206             this.cwhite.push(tag);
21207             
21208         }, this);
21209         
21210         Roo.each(w, function(tag) {
21211             if (b.indexOf(tag) > -1) {
21212                 return;
21213             }
21214             if (this.cwhite.indexOf(tag) > -1) {
21215                 return;
21216             }
21217             this.cwhite.push(tag);
21218             
21219         }, this);
21220         
21221         
21222         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21223             if (w.indexOf(tag) > -1) {
21224                 return;
21225             }
21226             this.cblack.push(tag);
21227             
21228         }, this);
21229         
21230         Roo.each(b, function(tag) {
21231             if (w.indexOf(tag) > -1) {
21232                 return;
21233             }
21234             if (this.cblack.indexOf(tag) > -1) {
21235                 return;
21236             }
21237             this.cblack.push(tag);
21238             
21239         }, this);
21240     },
21241     
21242     setStylesheets : function(stylesheets)
21243     {
21244         if(typeof(stylesheets) == 'string'){
21245             Roo.get(this.iframe.contentDocument.head).createChild({
21246                 tag : 'link',
21247                 rel : 'stylesheet',
21248                 type : 'text/css',
21249                 href : stylesheets
21250             });
21251             
21252             return;
21253         }
21254         var _this = this;
21255      
21256         Roo.each(stylesheets, function(s) {
21257             if(!s.length){
21258                 return;
21259             }
21260             
21261             Roo.get(_this.iframe.contentDocument.head).createChild({
21262                 tag : 'link',
21263                 rel : 'stylesheet',
21264                 type : 'text/css',
21265                 href : s
21266             });
21267         });
21268
21269         
21270     },
21271     
21272     removeStylesheets : function()
21273     {
21274         var _this = this;
21275         
21276         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21277             s.remove();
21278         });
21279     }
21280     
21281     // hide stuff that is not compatible
21282     /**
21283      * @event blur
21284      * @hide
21285      */
21286     /**
21287      * @event change
21288      * @hide
21289      */
21290     /**
21291      * @event focus
21292      * @hide
21293      */
21294     /**
21295      * @event specialkey
21296      * @hide
21297      */
21298     /**
21299      * @cfg {String} fieldClass @hide
21300      */
21301     /**
21302      * @cfg {String} focusClass @hide
21303      */
21304     /**
21305      * @cfg {String} autoCreate @hide
21306      */
21307     /**
21308      * @cfg {String} inputType @hide
21309      */
21310     /**
21311      * @cfg {String} invalidClass @hide
21312      */
21313     /**
21314      * @cfg {String} invalidText @hide
21315      */
21316     /**
21317      * @cfg {String} msgFx @hide
21318      */
21319     /**
21320      * @cfg {String} validateOnBlur @hide
21321      */
21322 });
21323
21324 Roo.HtmlEditorCore.white = [
21325         'area', 'br', 'img', 'input', 'hr', 'wbr',
21326         
21327        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21328        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21329        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21330        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21331        'table',   'ul',         'xmp', 
21332        
21333        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21334       'thead',   'tr', 
21335      
21336       'dir', 'menu', 'ol', 'ul', 'dl',
21337        
21338       'embed',  'object'
21339 ];
21340
21341
21342 Roo.HtmlEditorCore.black = [
21343     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21344         'applet', // 
21345         'base',   'basefont', 'bgsound', 'blink',  'body', 
21346         'frame',  'frameset', 'head',    'html',   'ilayer', 
21347         'iframe', 'layer',  'link',     'meta',    'object',   
21348         'script', 'style' ,'title',  'xml' // clean later..
21349 ];
21350 Roo.HtmlEditorCore.clean = [
21351     'script', 'style', 'title', 'xml'
21352 ];
21353 Roo.HtmlEditorCore.remove = [
21354     'font'
21355 ];
21356 // attributes..
21357
21358 Roo.HtmlEditorCore.ablack = [
21359     'on'
21360 ];
21361     
21362 Roo.HtmlEditorCore.aclean = [ 
21363     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21364 ];
21365
21366 // protocols..
21367 Roo.HtmlEditorCore.pwhite= [
21368         'http',  'https',  'mailto'
21369 ];
21370
21371 // white listed style attributes.
21372 Roo.HtmlEditorCore.cwhite= [
21373       //  'text-align', /// default is to allow most things..
21374       
21375          
21376 //        'font-size'//??
21377 ];
21378
21379 // black listed style attributes.
21380 Roo.HtmlEditorCore.cblack= [
21381       //  'font-size' -- this can be set by the project 
21382 ];
21383
21384
21385 Roo.HtmlEditorCore.swapCodes   =[ 
21386     [    8211, "--" ], 
21387     [    8212, "--" ], 
21388     [    8216,  "'" ],  
21389     [    8217, "'" ],  
21390     [    8220, '"' ],  
21391     [    8221, '"' ],  
21392     [    8226, "*" ],  
21393     [    8230, "..." ]
21394 ]; 
21395
21396     /*
21397  * - LGPL
21398  *
21399  * HtmlEditor
21400  * 
21401  */
21402
21403 /**
21404  * @class Roo.bootstrap.HtmlEditor
21405  * @extends Roo.bootstrap.TextArea
21406  * Bootstrap HtmlEditor class
21407
21408  * @constructor
21409  * Create a new HtmlEditor
21410  * @param {Object} config The config object
21411  */
21412
21413 Roo.bootstrap.HtmlEditor = function(config){
21414     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21415     if (!this.toolbars) {
21416         this.toolbars = [];
21417     }
21418     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21419     this.addEvents({
21420             /**
21421              * @event initialize
21422              * Fires when the editor is fully initialized (including the iframe)
21423              * @param {HtmlEditor} this
21424              */
21425             initialize: true,
21426             /**
21427              * @event activate
21428              * Fires when the editor is first receives the focus. Any insertion must wait
21429              * until after this event.
21430              * @param {HtmlEditor} this
21431              */
21432             activate: true,
21433              /**
21434              * @event beforesync
21435              * Fires before the textarea is updated with content from the editor iframe. Return false
21436              * to cancel the sync.
21437              * @param {HtmlEditor} this
21438              * @param {String} html
21439              */
21440             beforesync: true,
21441              /**
21442              * @event beforepush
21443              * Fires before the iframe editor is updated with content from the textarea. Return false
21444              * to cancel the push.
21445              * @param {HtmlEditor} this
21446              * @param {String} html
21447              */
21448             beforepush: true,
21449              /**
21450              * @event sync
21451              * Fires when the textarea is updated with content from the editor iframe.
21452              * @param {HtmlEditor} this
21453              * @param {String} html
21454              */
21455             sync: true,
21456              /**
21457              * @event push
21458              * Fires when the iframe editor is updated with content from the textarea.
21459              * @param {HtmlEditor} this
21460              * @param {String} html
21461              */
21462             push: true,
21463              /**
21464              * @event editmodechange
21465              * Fires when the editor switches edit modes
21466              * @param {HtmlEditor} this
21467              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21468              */
21469             editmodechange: true,
21470             /**
21471              * @event editorevent
21472              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21473              * @param {HtmlEditor} this
21474              */
21475             editorevent: true,
21476             /**
21477              * @event firstfocus
21478              * Fires when on first focus - needed by toolbars..
21479              * @param {HtmlEditor} this
21480              */
21481             firstfocus: true,
21482             /**
21483              * @event autosave
21484              * Auto save the htmlEditor value as a file into Events
21485              * @param {HtmlEditor} this
21486              */
21487             autosave: true,
21488             /**
21489              * @event savedpreview
21490              * preview the saved version of htmlEditor
21491              * @param {HtmlEditor} this
21492              */
21493             savedpreview: true
21494         });
21495 };
21496
21497
21498 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21499     
21500     
21501       /**
21502      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21503      */
21504     toolbars : false,
21505    
21506      /**
21507      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21508      *                        Roo.resizable.
21509      */
21510     resizable : false,
21511      /**
21512      * @cfg {Number} height (in pixels)
21513      */   
21514     height: 300,
21515    /**
21516      * @cfg {Number} width (in pixels)
21517      */   
21518     width: false,
21519     
21520     /**
21521      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21522      * 
21523      */
21524     stylesheets: false,
21525     
21526     // id of frame..
21527     frameId: false,
21528     
21529     // private properties
21530     validationEvent : false,
21531     deferHeight: true,
21532     initialized : false,
21533     activated : false,
21534     
21535     onFocus : Roo.emptyFn,
21536     iframePad:3,
21537     hideMode:'offsets',
21538     
21539     
21540     tbContainer : false,
21541     
21542     toolbarContainer :function() {
21543         return this.wrap.select('.x-html-editor-tb',true).first();
21544     },
21545
21546     /**
21547      * Protected method that will not generally be called directly. It
21548      * is called when the editor creates its toolbar. Override this method if you need to
21549      * add custom toolbar buttons.
21550      * @param {HtmlEditor} editor
21551      */
21552     createToolbar : function(){
21553         
21554         Roo.log("create toolbars");
21555         
21556         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21557         this.toolbars[0].render(this.toolbarContainer());
21558         
21559         return;
21560         
21561 //        if (!editor.toolbars || !editor.toolbars.length) {
21562 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21563 //        }
21564 //        
21565 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21566 //            editor.toolbars[i] = Roo.factory(
21567 //                    typeof(editor.toolbars[i]) == 'string' ?
21568 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21569 //                Roo.bootstrap.HtmlEditor);
21570 //            editor.toolbars[i].init(editor);
21571 //        }
21572     },
21573
21574      
21575     // private
21576     onRender : function(ct, position)
21577     {
21578        // Roo.log("Call onRender: " + this.xtype);
21579         var _t = this;
21580         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21581       
21582         this.wrap = this.inputEl().wrap({
21583             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21584         });
21585         
21586         this.editorcore.onRender(ct, position);
21587          
21588         if (this.resizable) {
21589             this.resizeEl = new Roo.Resizable(this.wrap, {
21590                 pinned : true,
21591                 wrap: true,
21592                 dynamic : true,
21593                 minHeight : this.height,
21594                 height: this.height,
21595                 handles : this.resizable,
21596                 width: this.width,
21597                 listeners : {
21598                     resize : function(r, w, h) {
21599                         _t.onResize(w,h); // -something
21600                     }
21601                 }
21602             });
21603             
21604         }
21605         this.createToolbar(this);
21606        
21607         
21608         if(!this.width && this.resizable){
21609             this.setSize(this.wrap.getSize());
21610         }
21611         if (this.resizeEl) {
21612             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21613             // should trigger onReize..
21614         }
21615         
21616     },
21617
21618     // private
21619     onResize : function(w, h)
21620     {
21621         Roo.log('resize: ' +w + ',' + h );
21622         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21623         var ew = false;
21624         var eh = false;
21625         
21626         if(this.inputEl() ){
21627             if(typeof w == 'number'){
21628                 var aw = w - this.wrap.getFrameWidth('lr');
21629                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21630                 ew = aw;
21631             }
21632             if(typeof h == 'number'){
21633                  var tbh = -11;  // fixme it needs to tool bar size!
21634                 for (var i =0; i < this.toolbars.length;i++) {
21635                     // fixme - ask toolbars for heights?
21636                     tbh += this.toolbars[i].el.getHeight();
21637                     //if (this.toolbars[i].footer) {
21638                     //    tbh += this.toolbars[i].footer.el.getHeight();
21639                     //}
21640                 }
21641               
21642                 
21643                 
21644                 
21645                 
21646                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21647                 ah -= 5; // knock a few pixes off for look..
21648                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21649                 var eh = ah;
21650             }
21651         }
21652         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21653         this.editorcore.onResize(ew,eh);
21654         
21655     },
21656
21657     /**
21658      * Toggles the editor between standard and source edit mode.
21659      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21660      */
21661     toggleSourceEdit : function(sourceEditMode)
21662     {
21663         this.editorcore.toggleSourceEdit(sourceEditMode);
21664         
21665         if(this.editorcore.sourceEditMode){
21666             Roo.log('editor - showing textarea');
21667             
21668 //            Roo.log('in');
21669 //            Roo.log(this.syncValue());
21670             this.syncValue();
21671             this.inputEl().removeClass(['hide', 'x-hidden']);
21672             this.inputEl().dom.removeAttribute('tabIndex');
21673             this.inputEl().focus();
21674         }else{
21675             Roo.log('editor - hiding textarea');
21676 //            Roo.log('out')
21677 //            Roo.log(this.pushValue()); 
21678             this.pushValue();
21679             
21680             this.inputEl().addClass(['hide', 'x-hidden']);
21681             this.inputEl().dom.setAttribute('tabIndex', -1);
21682             //this.deferFocus();
21683         }
21684          
21685         if(this.resizable){
21686             this.setSize(this.wrap.getSize());
21687         }
21688         
21689         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21690     },
21691  
21692     // private (for BoxComponent)
21693     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21694
21695     // private (for BoxComponent)
21696     getResizeEl : function(){
21697         return this.wrap;
21698     },
21699
21700     // private (for BoxComponent)
21701     getPositionEl : function(){
21702         return this.wrap;
21703     },
21704
21705     // private
21706     initEvents : function(){
21707         this.originalValue = this.getValue();
21708     },
21709
21710 //    /**
21711 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21712 //     * @method
21713 //     */
21714 //    markInvalid : Roo.emptyFn,
21715 //    /**
21716 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21717 //     * @method
21718 //     */
21719 //    clearInvalid : Roo.emptyFn,
21720
21721     setValue : function(v){
21722         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21723         this.editorcore.pushValue();
21724     },
21725
21726      
21727     // private
21728     deferFocus : function(){
21729         this.focus.defer(10, this);
21730     },
21731
21732     // doc'ed in Field
21733     focus : function(){
21734         this.editorcore.focus();
21735         
21736     },
21737       
21738
21739     // private
21740     onDestroy : function(){
21741         
21742         
21743         
21744         if(this.rendered){
21745             
21746             for (var i =0; i < this.toolbars.length;i++) {
21747                 // fixme - ask toolbars for heights?
21748                 this.toolbars[i].onDestroy();
21749             }
21750             
21751             this.wrap.dom.innerHTML = '';
21752             this.wrap.remove();
21753         }
21754     },
21755
21756     // private
21757     onFirstFocus : function(){
21758         //Roo.log("onFirstFocus");
21759         this.editorcore.onFirstFocus();
21760          for (var i =0; i < this.toolbars.length;i++) {
21761             this.toolbars[i].onFirstFocus();
21762         }
21763         
21764     },
21765     
21766     // private
21767     syncValue : function()
21768     {   
21769         this.editorcore.syncValue();
21770     },
21771     
21772     pushValue : function()
21773     {   
21774         this.editorcore.pushValue();
21775     }
21776      
21777     
21778     // hide stuff that is not compatible
21779     /**
21780      * @event blur
21781      * @hide
21782      */
21783     /**
21784      * @event change
21785      * @hide
21786      */
21787     /**
21788      * @event focus
21789      * @hide
21790      */
21791     /**
21792      * @event specialkey
21793      * @hide
21794      */
21795     /**
21796      * @cfg {String} fieldClass @hide
21797      */
21798     /**
21799      * @cfg {String} focusClass @hide
21800      */
21801     /**
21802      * @cfg {String} autoCreate @hide
21803      */
21804     /**
21805      * @cfg {String} inputType @hide
21806      */
21807     /**
21808      * @cfg {String} invalidClass @hide
21809      */
21810     /**
21811      * @cfg {String} invalidText @hide
21812      */
21813     /**
21814      * @cfg {String} msgFx @hide
21815      */
21816     /**
21817      * @cfg {String} validateOnBlur @hide
21818      */
21819 });
21820  
21821     
21822    
21823    
21824    
21825       
21826 Roo.namespace('Roo.bootstrap.htmleditor');
21827 /**
21828  * @class Roo.bootstrap.HtmlEditorToolbar1
21829  * Basic Toolbar
21830  * 
21831  * Usage:
21832  *
21833  new Roo.bootstrap.HtmlEditor({
21834     ....
21835     toolbars : [
21836         new Roo.bootstrap.HtmlEditorToolbar1({
21837             disable : { fonts: 1 , format: 1, ..., ... , ...],
21838             btns : [ .... ]
21839         })
21840     }
21841      
21842  * 
21843  * @cfg {Object} disable List of elements to disable..
21844  * @cfg {Array} btns List of additional buttons.
21845  * 
21846  * 
21847  * NEEDS Extra CSS? 
21848  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21849  */
21850  
21851 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21852 {
21853     
21854     Roo.apply(this, config);
21855     
21856     // default disabled, based on 'good practice'..
21857     this.disable = this.disable || {};
21858     Roo.applyIf(this.disable, {
21859         fontSize : true,
21860         colors : true,
21861         specialElements : true
21862     });
21863     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21864     
21865     this.editor = config.editor;
21866     this.editorcore = config.editor.editorcore;
21867     
21868     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21869     
21870     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21871     // dont call parent... till later.
21872 }
21873 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21874      
21875     bar : true,
21876     
21877     editor : false,
21878     editorcore : false,
21879     
21880     
21881     formats : [
21882         "p" ,  
21883         "h1","h2","h3","h4","h5","h6", 
21884         "pre", "code", 
21885         "abbr", "acronym", "address", "cite", "samp", "var",
21886         'div','span'
21887     ],
21888     
21889     onRender : function(ct, position)
21890     {
21891        // Roo.log("Call onRender: " + this.xtype);
21892         
21893        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21894        Roo.log(this.el);
21895        this.el.dom.style.marginBottom = '0';
21896        var _this = this;
21897        var editorcore = this.editorcore;
21898        var editor= this.editor;
21899        
21900        var children = [];
21901        var btn = function(id,cmd , toggle, handler){
21902        
21903             var  event = toggle ? 'toggle' : 'click';
21904        
21905             var a = {
21906                 size : 'sm',
21907                 xtype: 'Button',
21908                 xns: Roo.bootstrap,
21909                 glyphicon : id,
21910                 cmd : id || cmd,
21911                 enableToggle:toggle !== false,
21912                 //html : 'submit'
21913                 pressed : toggle ? false : null,
21914                 listeners : {}
21915             };
21916             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21917                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21918             };
21919             children.push(a);
21920             return a;
21921        }
21922         
21923         var style = {
21924                 xtype: 'Button',
21925                 size : 'sm',
21926                 xns: Roo.bootstrap,
21927                 glyphicon : 'font',
21928                 //html : 'submit'
21929                 menu : {
21930                     xtype: 'Menu',
21931                     xns: Roo.bootstrap,
21932                     items:  []
21933                 }
21934         };
21935         Roo.each(this.formats, function(f) {
21936             style.menu.items.push({
21937                 xtype :'MenuItem',
21938                 xns: Roo.bootstrap,
21939                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21940                 tagname : f,
21941                 listeners : {
21942                     click : function()
21943                     {
21944                         editorcore.insertTag(this.tagname);
21945                         editor.focus();
21946                     }
21947                 }
21948                 
21949             });
21950         });
21951          children.push(style);   
21952             
21953             
21954         btn('bold',false,true);
21955         btn('italic',false,true);
21956         btn('align-left', 'justifyleft',true);
21957         btn('align-center', 'justifycenter',true);
21958         btn('align-right' , 'justifyright',true);
21959         btn('link', false, false, function(btn) {
21960             //Roo.log("create link?");
21961             var url = prompt(this.createLinkText, this.defaultLinkValue);
21962             if(url && url != 'http:/'+'/'){
21963                 this.editorcore.relayCmd('createlink', url);
21964             }
21965         }),
21966         btn('list','insertunorderedlist',true);
21967         btn('pencil', false,true, function(btn){
21968                 Roo.log(this);
21969                 
21970                 this.toggleSourceEdit(btn.pressed);
21971         });
21972         /*
21973         var cog = {
21974                 xtype: 'Button',
21975                 size : 'sm',
21976                 xns: Roo.bootstrap,
21977                 glyphicon : 'cog',
21978                 //html : 'submit'
21979                 menu : {
21980                     xtype: 'Menu',
21981                     xns: Roo.bootstrap,
21982                     items:  []
21983                 }
21984         };
21985         
21986         cog.menu.items.push({
21987             xtype :'MenuItem',
21988             xns: Roo.bootstrap,
21989             html : Clean styles,
21990             tagname : f,
21991             listeners : {
21992                 click : function()
21993                 {
21994                     editorcore.insertTag(this.tagname);
21995                     editor.focus();
21996                 }
21997             }
21998             
21999         });
22000        */
22001         
22002          
22003        this.xtype = 'NavSimplebar';
22004         
22005         for(var i=0;i< children.length;i++) {
22006             
22007             this.buttons.add(this.addxtypeChild(children[i]));
22008             
22009         }
22010         
22011         editor.on('editorevent', this.updateToolbar, this);
22012     },
22013     onBtnClick : function(id)
22014     {
22015        this.editorcore.relayCmd(id);
22016        this.editorcore.focus();
22017     },
22018     
22019     /**
22020      * Protected method that will not generally be called directly. It triggers
22021      * a toolbar update by reading the markup state of the current selection in the editor.
22022      */
22023     updateToolbar: function(){
22024
22025         if(!this.editorcore.activated){
22026             this.editor.onFirstFocus(); // is this neeed?
22027             return;
22028         }
22029
22030         var btns = this.buttons; 
22031         var doc = this.editorcore.doc;
22032         btns.get('bold').setActive(doc.queryCommandState('bold'));
22033         btns.get('italic').setActive(doc.queryCommandState('italic'));
22034         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22035         
22036         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22037         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22038         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22039         
22040         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22041         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22042          /*
22043         
22044         var ans = this.editorcore.getAllAncestors();
22045         if (this.formatCombo) {
22046             
22047             
22048             var store = this.formatCombo.store;
22049             this.formatCombo.setValue("");
22050             for (var i =0; i < ans.length;i++) {
22051                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22052                     // select it..
22053                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22054                     break;
22055                 }
22056             }
22057         }
22058         
22059         
22060         
22061         // hides menus... - so this cant be on a menu...
22062         Roo.bootstrap.MenuMgr.hideAll();
22063         */
22064         Roo.bootstrap.MenuMgr.hideAll();
22065         //this.editorsyncValue();
22066     },
22067     onFirstFocus: function() {
22068         this.buttons.each(function(item){
22069            item.enable();
22070         });
22071     },
22072     toggleSourceEdit : function(sourceEditMode){
22073         
22074           
22075         if(sourceEditMode){
22076             Roo.log("disabling buttons");
22077            this.buttons.each( function(item){
22078                 if(item.cmd != 'pencil'){
22079                     item.disable();
22080                 }
22081             });
22082           
22083         }else{
22084             Roo.log("enabling buttons");
22085             if(this.editorcore.initialized){
22086                 this.buttons.each( function(item){
22087                     item.enable();
22088                 });
22089             }
22090             
22091         }
22092         Roo.log("calling toggole on editor");
22093         // tell the editor that it's been pressed..
22094         this.editor.toggleSourceEdit(sourceEditMode);
22095        
22096     }
22097 });
22098
22099
22100
22101
22102
22103 /**
22104  * @class Roo.bootstrap.Table.AbstractSelectionModel
22105  * @extends Roo.util.Observable
22106  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22107  * implemented by descendant classes.  This class should not be directly instantiated.
22108  * @constructor
22109  */
22110 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22111     this.locked = false;
22112     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22113 };
22114
22115
22116 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22117     /** @ignore Called by the grid automatically. Do not call directly. */
22118     init : function(grid){
22119         this.grid = grid;
22120         this.initEvents();
22121     },
22122
22123     /**
22124      * Locks the selections.
22125      */
22126     lock : function(){
22127         this.locked = true;
22128     },
22129
22130     /**
22131      * Unlocks the selections.
22132      */
22133     unlock : function(){
22134         this.locked = false;
22135     },
22136
22137     /**
22138      * Returns true if the selections are locked.
22139      * @return {Boolean}
22140      */
22141     isLocked : function(){
22142         return this.locked;
22143     }
22144 });
22145 /**
22146  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22147  * @class Roo.bootstrap.Table.RowSelectionModel
22148  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22149  * It supports multiple selections and keyboard selection/navigation. 
22150  * @constructor
22151  * @param {Object} config
22152  */
22153
22154 Roo.bootstrap.Table.RowSelectionModel = function(config){
22155     Roo.apply(this, config);
22156     this.selections = new Roo.util.MixedCollection(false, function(o){
22157         return o.id;
22158     });
22159
22160     this.last = false;
22161     this.lastActive = false;
22162
22163     this.addEvents({
22164         /**
22165              * @event selectionchange
22166              * Fires when the selection changes
22167              * @param {SelectionModel} this
22168              */
22169             "selectionchange" : true,
22170         /**
22171              * @event afterselectionchange
22172              * Fires after the selection changes (eg. by key press or clicking)
22173              * @param {SelectionModel} this
22174              */
22175             "afterselectionchange" : true,
22176         /**
22177              * @event beforerowselect
22178              * Fires when a row is selected being selected, return false to cancel.
22179              * @param {SelectionModel} this
22180              * @param {Number} rowIndex The selected index
22181              * @param {Boolean} keepExisting False if other selections will be cleared
22182              */
22183             "beforerowselect" : true,
22184         /**
22185              * @event rowselect
22186              * Fires when a row is selected.
22187              * @param {SelectionModel} this
22188              * @param {Number} rowIndex The selected index
22189              * @param {Roo.data.Record} r The record
22190              */
22191             "rowselect" : true,
22192         /**
22193              * @event rowdeselect
22194              * Fires when a row is deselected.
22195              * @param {SelectionModel} this
22196              * @param {Number} rowIndex The selected index
22197              */
22198         "rowdeselect" : true
22199     });
22200     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22201     this.locked = false;
22202 };
22203
22204 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22205     /**
22206      * @cfg {Boolean} singleSelect
22207      * True to allow selection of only one row at a time (defaults to false)
22208      */
22209     singleSelect : false,
22210
22211     // private
22212     initEvents : function(){
22213
22214         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22215             this.grid.on("mousedown", this.handleMouseDown, this);
22216         }else{ // allow click to work like normal
22217             this.grid.on("rowclick", this.handleDragableRowClick, this);
22218         }
22219
22220         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22221             "up" : function(e){
22222                 if(!e.shiftKey){
22223                     this.selectPrevious(e.shiftKey);
22224                 }else if(this.last !== false && this.lastActive !== false){
22225                     var last = this.last;
22226                     this.selectRange(this.last,  this.lastActive-1);
22227                     this.grid.getView().focusRow(this.lastActive);
22228                     if(last !== false){
22229                         this.last = last;
22230                     }
22231                 }else{
22232                     this.selectFirstRow();
22233                 }
22234                 this.fireEvent("afterselectionchange", this);
22235             },
22236             "down" : function(e){
22237                 if(!e.shiftKey){
22238                     this.selectNext(e.shiftKey);
22239                 }else if(this.last !== false && this.lastActive !== false){
22240                     var last = this.last;
22241                     this.selectRange(this.last,  this.lastActive+1);
22242                     this.grid.getView().focusRow(this.lastActive);
22243                     if(last !== false){
22244                         this.last = last;
22245                     }
22246                 }else{
22247                     this.selectFirstRow();
22248                 }
22249                 this.fireEvent("afterselectionchange", this);
22250             },
22251             scope: this
22252         });
22253
22254         var view = this.grid.view;
22255         view.on("refresh", this.onRefresh, this);
22256         view.on("rowupdated", this.onRowUpdated, this);
22257         view.on("rowremoved", this.onRemove, this);
22258     },
22259
22260     // private
22261     onRefresh : function(){
22262         var ds = this.grid.dataSource, i, v = this.grid.view;
22263         var s = this.selections;
22264         s.each(function(r){
22265             if((i = ds.indexOfId(r.id)) != -1){
22266                 v.onRowSelect(i);
22267             }else{
22268                 s.remove(r);
22269             }
22270         });
22271     },
22272
22273     // private
22274     onRemove : function(v, index, r){
22275         this.selections.remove(r);
22276     },
22277
22278     // private
22279     onRowUpdated : function(v, index, r){
22280         if(this.isSelected(r)){
22281             v.onRowSelect(index);
22282         }
22283     },
22284
22285     /**
22286      * Select records.
22287      * @param {Array} records The records to select
22288      * @param {Boolean} keepExisting (optional) True to keep existing selections
22289      */
22290     selectRecords : function(records, keepExisting){
22291         if(!keepExisting){
22292             this.clearSelections();
22293         }
22294         var ds = this.grid.dataSource;
22295         for(var i = 0, len = records.length; i < len; i++){
22296             this.selectRow(ds.indexOf(records[i]), true);
22297         }
22298     },
22299
22300     /**
22301      * Gets the number of selected rows.
22302      * @return {Number}
22303      */
22304     getCount : function(){
22305         return this.selections.length;
22306     },
22307
22308     /**
22309      * Selects the first row in the grid.
22310      */
22311     selectFirstRow : function(){
22312         this.selectRow(0);
22313     },
22314
22315     /**
22316      * Select the last row.
22317      * @param {Boolean} keepExisting (optional) True to keep existing selections
22318      */
22319     selectLastRow : function(keepExisting){
22320         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22321     },
22322
22323     /**
22324      * Selects the row immediately following the last selected row.
22325      * @param {Boolean} keepExisting (optional) True to keep existing selections
22326      */
22327     selectNext : function(keepExisting){
22328         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22329             this.selectRow(this.last+1, keepExisting);
22330             this.grid.getView().focusRow(this.last);
22331         }
22332     },
22333
22334     /**
22335      * Selects the row that precedes the last selected row.
22336      * @param {Boolean} keepExisting (optional) True to keep existing selections
22337      */
22338     selectPrevious : function(keepExisting){
22339         if(this.last){
22340             this.selectRow(this.last-1, keepExisting);
22341             this.grid.getView().focusRow(this.last);
22342         }
22343     },
22344
22345     /**
22346      * Returns the selected records
22347      * @return {Array} Array of selected records
22348      */
22349     getSelections : function(){
22350         return [].concat(this.selections.items);
22351     },
22352
22353     /**
22354      * Returns the first selected record.
22355      * @return {Record}
22356      */
22357     getSelected : function(){
22358         return this.selections.itemAt(0);
22359     },
22360
22361
22362     /**
22363      * Clears all selections.
22364      */
22365     clearSelections : function(fast){
22366         if(this.locked) {
22367             return;
22368         }
22369         if(fast !== true){
22370             var ds = this.grid.dataSource;
22371             var s = this.selections;
22372             s.each(function(r){
22373                 this.deselectRow(ds.indexOfId(r.id));
22374             }, this);
22375             s.clear();
22376         }else{
22377             this.selections.clear();
22378         }
22379         this.last = false;
22380     },
22381
22382
22383     /**
22384      * Selects all rows.
22385      */
22386     selectAll : function(){
22387         if(this.locked) {
22388             return;
22389         }
22390         this.selections.clear();
22391         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22392             this.selectRow(i, true);
22393         }
22394     },
22395
22396     /**
22397      * Returns True if there is a selection.
22398      * @return {Boolean}
22399      */
22400     hasSelection : function(){
22401         return this.selections.length > 0;
22402     },
22403
22404     /**
22405      * Returns True if the specified row is selected.
22406      * @param {Number/Record} record The record or index of the record to check
22407      * @return {Boolean}
22408      */
22409     isSelected : function(index){
22410         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22411         return (r && this.selections.key(r.id) ? true : false);
22412     },
22413
22414     /**
22415      * Returns True if the specified record id is selected.
22416      * @param {String} id The id of record to check
22417      * @return {Boolean}
22418      */
22419     isIdSelected : function(id){
22420         return (this.selections.key(id) ? true : false);
22421     },
22422
22423     // private
22424     handleMouseDown : function(e, t){
22425         var view = this.grid.getView(), rowIndex;
22426         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22427             return;
22428         };
22429         if(e.shiftKey && this.last !== false){
22430             var last = this.last;
22431             this.selectRange(last, rowIndex, e.ctrlKey);
22432             this.last = last; // reset the last
22433             view.focusRow(rowIndex);
22434         }else{
22435             var isSelected = this.isSelected(rowIndex);
22436             if(e.button !== 0 && isSelected){
22437                 view.focusRow(rowIndex);
22438             }else if(e.ctrlKey && isSelected){
22439                 this.deselectRow(rowIndex);
22440             }else if(!isSelected){
22441                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22442                 view.focusRow(rowIndex);
22443             }
22444         }
22445         this.fireEvent("afterselectionchange", this);
22446     },
22447     // private
22448     handleDragableRowClick :  function(grid, rowIndex, e) 
22449     {
22450         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22451             this.selectRow(rowIndex, false);
22452             grid.view.focusRow(rowIndex);
22453              this.fireEvent("afterselectionchange", this);
22454         }
22455     },
22456     
22457     /**
22458      * Selects multiple rows.
22459      * @param {Array} rows Array of the indexes of the row to select
22460      * @param {Boolean} keepExisting (optional) True to keep existing selections
22461      */
22462     selectRows : function(rows, keepExisting){
22463         if(!keepExisting){
22464             this.clearSelections();
22465         }
22466         for(var i = 0, len = rows.length; i < len; i++){
22467             this.selectRow(rows[i], true);
22468         }
22469     },
22470
22471     /**
22472      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22473      * @param {Number} startRow The index of the first row in the range
22474      * @param {Number} endRow The index of the last row in the range
22475      * @param {Boolean} keepExisting (optional) True to retain existing selections
22476      */
22477     selectRange : function(startRow, endRow, keepExisting){
22478         if(this.locked) {
22479             return;
22480         }
22481         if(!keepExisting){
22482             this.clearSelections();
22483         }
22484         if(startRow <= endRow){
22485             for(var i = startRow; i <= endRow; i++){
22486                 this.selectRow(i, true);
22487             }
22488         }else{
22489             for(var i = startRow; i >= endRow; i--){
22490                 this.selectRow(i, true);
22491             }
22492         }
22493     },
22494
22495     /**
22496      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22497      * @param {Number} startRow The index of the first row in the range
22498      * @param {Number} endRow The index of the last row in the range
22499      */
22500     deselectRange : function(startRow, endRow, preventViewNotify){
22501         if(this.locked) {
22502             return;
22503         }
22504         for(var i = startRow; i <= endRow; i++){
22505             this.deselectRow(i, preventViewNotify);
22506         }
22507     },
22508
22509     /**
22510      * Selects a row.
22511      * @param {Number} row The index of the row to select
22512      * @param {Boolean} keepExisting (optional) True to keep existing selections
22513      */
22514     selectRow : function(index, keepExisting, preventViewNotify){
22515         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22516             return;
22517         }
22518         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22519             if(!keepExisting || this.singleSelect){
22520                 this.clearSelections();
22521             }
22522             var r = this.grid.dataSource.getAt(index);
22523             this.selections.add(r);
22524             this.last = this.lastActive = index;
22525             if(!preventViewNotify){
22526                 this.grid.getView().onRowSelect(index);
22527             }
22528             this.fireEvent("rowselect", this, index, r);
22529             this.fireEvent("selectionchange", this);
22530         }
22531     },
22532
22533     /**
22534      * Deselects a row.
22535      * @param {Number} row The index of the row to deselect
22536      */
22537     deselectRow : function(index, preventViewNotify){
22538         if(this.locked) {
22539             return;
22540         }
22541         if(this.last == index){
22542             this.last = false;
22543         }
22544         if(this.lastActive == index){
22545             this.lastActive = false;
22546         }
22547         var r = this.grid.dataSource.getAt(index);
22548         this.selections.remove(r);
22549         if(!preventViewNotify){
22550             this.grid.getView().onRowDeselect(index);
22551         }
22552         this.fireEvent("rowdeselect", this, index);
22553         this.fireEvent("selectionchange", this);
22554     },
22555
22556     // private
22557     restoreLast : function(){
22558         if(this._last){
22559             this.last = this._last;
22560         }
22561     },
22562
22563     // private
22564     acceptsNav : function(row, col, cm){
22565         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22566     },
22567
22568     // private
22569     onEditorKey : function(field, e){
22570         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22571         if(k == e.TAB){
22572             e.stopEvent();
22573             ed.completeEdit();
22574             if(e.shiftKey){
22575                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22576             }else{
22577                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22578             }
22579         }else if(k == e.ENTER && !e.ctrlKey){
22580             e.stopEvent();
22581             ed.completeEdit();
22582             if(e.shiftKey){
22583                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22584             }else{
22585                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22586             }
22587         }else if(k == e.ESC){
22588             ed.cancelEdit();
22589         }
22590         if(newCell){
22591             g.startEditing(newCell[0], newCell[1]);
22592         }
22593     }
22594 });/*
22595  * Based on:
22596  * Ext JS Library 1.1.1
22597  * Copyright(c) 2006-2007, Ext JS, LLC.
22598  *
22599  * Originally Released Under LGPL - original licence link has changed is not relivant.
22600  *
22601  * Fork - LGPL
22602  * <script type="text/javascript">
22603  */
22604  
22605 /**
22606  * @class Roo.bootstrap.PagingToolbar
22607  * @extends Roo.bootstrap.NavSimplebar
22608  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22609  * @constructor
22610  * Create a new PagingToolbar
22611  * @param {Object} config The config object
22612  * @param {Roo.data.Store} store
22613  */
22614 Roo.bootstrap.PagingToolbar = function(config)
22615 {
22616     // old args format still supported... - xtype is prefered..
22617         // created from xtype...
22618     
22619     this.ds = config.dataSource;
22620     
22621     if (config.store && !this.ds) {
22622         this.store= Roo.factory(config.store, Roo.data);
22623         this.ds = this.store;
22624         this.ds.xmodule = this.xmodule || false;
22625     }
22626     
22627     this.toolbarItems = [];
22628     if (config.items) {
22629         this.toolbarItems = config.items;
22630     }
22631     
22632     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22633     
22634     this.cursor = 0;
22635     
22636     if (this.ds) { 
22637         this.bind(this.ds);
22638     }
22639     
22640     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22641     
22642 };
22643
22644 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22645     /**
22646      * @cfg {Roo.data.Store} dataSource
22647      * The underlying data store providing the paged data
22648      */
22649     /**
22650      * @cfg {String/HTMLElement/Element} container
22651      * container The id or element that will contain the toolbar
22652      */
22653     /**
22654      * @cfg {Boolean} displayInfo
22655      * True to display the displayMsg (defaults to false)
22656      */
22657     /**
22658      * @cfg {Number} pageSize
22659      * The number of records to display per page (defaults to 20)
22660      */
22661     pageSize: 20,
22662     /**
22663      * @cfg {String} displayMsg
22664      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22665      */
22666     displayMsg : 'Displaying {0} - {1} of {2}',
22667     /**
22668      * @cfg {String} emptyMsg
22669      * The message to display when no records are found (defaults to "No data to display")
22670      */
22671     emptyMsg : 'No data to display',
22672     /**
22673      * Customizable piece of the default paging text (defaults to "Page")
22674      * @type String
22675      */
22676     beforePageText : "Page",
22677     /**
22678      * Customizable piece of the default paging text (defaults to "of %0")
22679      * @type String
22680      */
22681     afterPageText : "of {0}",
22682     /**
22683      * Customizable piece of the default paging text (defaults to "First Page")
22684      * @type String
22685      */
22686     firstText : "First Page",
22687     /**
22688      * Customizable piece of the default paging text (defaults to "Previous Page")
22689      * @type String
22690      */
22691     prevText : "Previous Page",
22692     /**
22693      * Customizable piece of the default paging text (defaults to "Next Page")
22694      * @type String
22695      */
22696     nextText : "Next Page",
22697     /**
22698      * Customizable piece of the default paging text (defaults to "Last Page")
22699      * @type String
22700      */
22701     lastText : "Last Page",
22702     /**
22703      * Customizable piece of the default paging text (defaults to "Refresh")
22704      * @type String
22705      */
22706     refreshText : "Refresh",
22707
22708     buttons : false,
22709     // private
22710     onRender : function(ct, position) 
22711     {
22712         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22713         this.navgroup.parentId = this.id;
22714         this.navgroup.onRender(this.el, null);
22715         // add the buttons to the navgroup
22716         
22717         if(this.displayInfo){
22718             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22719             this.displayEl = this.el.select('.x-paging-info', true).first();
22720 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22721 //            this.displayEl = navel.el.select('span',true).first();
22722         }
22723         
22724         var _this = this;
22725         
22726         if(this.buttons){
22727             Roo.each(_this.buttons, function(e){ // this might need to use render????
22728                Roo.factory(e).onRender(_this.el, null);
22729             });
22730         }
22731             
22732         Roo.each(_this.toolbarItems, function(e) {
22733             _this.navgroup.addItem(e);
22734         });
22735         
22736         
22737         this.first = this.navgroup.addItem({
22738             tooltip: this.firstText,
22739             cls: "prev",
22740             icon : 'fa fa-backward',
22741             disabled: true,
22742             preventDefault: true,
22743             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22744         });
22745         
22746         this.prev =  this.navgroup.addItem({
22747             tooltip: this.prevText,
22748             cls: "prev",
22749             icon : 'fa fa-step-backward',
22750             disabled: true,
22751             preventDefault: true,
22752             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22753         });
22754     //this.addSeparator();
22755         
22756         
22757         var field = this.navgroup.addItem( {
22758             tagtype : 'span',
22759             cls : 'x-paging-position',
22760             
22761             html : this.beforePageText  +
22762                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22763                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22764          } ); //?? escaped?
22765         
22766         this.field = field.el.select('input', true).first();
22767         this.field.on("keydown", this.onPagingKeydown, this);
22768         this.field.on("focus", function(){this.dom.select();});
22769     
22770     
22771         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22772         //this.field.setHeight(18);
22773         //this.addSeparator();
22774         this.next = this.navgroup.addItem({
22775             tooltip: this.nextText,
22776             cls: "next",
22777             html : ' <i class="fa fa-step-forward">',
22778             disabled: true,
22779             preventDefault: true,
22780             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22781         });
22782         this.last = this.navgroup.addItem({
22783             tooltip: this.lastText,
22784             icon : 'fa fa-forward',
22785             cls: "next",
22786             disabled: true,
22787             preventDefault: true,
22788             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22789         });
22790     //this.addSeparator();
22791         this.loading = this.navgroup.addItem({
22792             tooltip: this.refreshText,
22793             icon: 'fa fa-refresh',
22794             preventDefault: true,
22795             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22796         });
22797         
22798     },
22799
22800     // private
22801     updateInfo : function(){
22802         if(this.displayEl){
22803             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22804             var msg = count == 0 ?
22805                 this.emptyMsg :
22806                 String.format(
22807                     this.displayMsg,
22808                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22809                 );
22810             this.displayEl.update(msg);
22811         }
22812     },
22813
22814     // private
22815     onLoad : function(ds, r, o){
22816        this.cursor = o.params ? o.params.start : 0;
22817        var d = this.getPageData(),
22818             ap = d.activePage,
22819             ps = d.pages;
22820         
22821        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22822        this.field.dom.value = ap;
22823        this.first.setDisabled(ap == 1);
22824        this.prev.setDisabled(ap == 1);
22825        this.next.setDisabled(ap == ps);
22826        this.last.setDisabled(ap == ps);
22827        this.loading.enable();
22828        this.updateInfo();
22829     },
22830
22831     // private
22832     getPageData : function(){
22833         var total = this.ds.getTotalCount();
22834         return {
22835             total : total,
22836             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22837             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22838         };
22839     },
22840
22841     // private
22842     onLoadError : function(){
22843         this.loading.enable();
22844     },
22845
22846     // private
22847     onPagingKeydown : function(e){
22848         var k = e.getKey();
22849         var d = this.getPageData();
22850         if(k == e.RETURN){
22851             var v = this.field.dom.value, pageNum;
22852             if(!v || isNaN(pageNum = parseInt(v, 10))){
22853                 this.field.dom.value = d.activePage;
22854                 return;
22855             }
22856             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22857             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22858             e.stopEvent();
22859         }
22860         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))
22861         {
22862           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22863           this.field.dom.value = pageNum;
22864           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22865           e.stopEvent();
22866         }
22867         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22868         {
22869           var v = this.field.dom.value, pageNum; 
22870           var increment = (e.shiftKey) ? 10 : 1;
22871           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22872                 increment *= -1;
22873           }
22874           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22875             this.field.dom.value = d.activePage;
22876             return;
22877           }
22878           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22879           {
22880             this.field.dom.value = parseInt(v, 10) + increment;
22881             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22882             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22883           }
22884           e.stopEvent();
22885         }
22886     },
22887
22888     // private
22889     beforeLoad : function(){
22890         if(this.loading){
22891             this.loading.disable();
22892         }
22893     },
22894
22895     // private
22896     onClick : function(which){
22897         
22898         var ds = this.ds;
22899         if (!ds) {
22900             return;
22901         }
22902         
22903         switch(which){
22904             case "first":
22905                 ds.load({params:{start: 0, limit: this.pageSize}});
22906             break;
22907             case "prev":
22908                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22909             break;
22910             case "next":
22911                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22912             break;
22913             case "last":
22914                 var total = ds.getTotalCount();
22915                 var extra = total % this.pageSize;
22916                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22917                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22918             break;
22919             case "refresh":
22920                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22921             break;
22922         }
22923     },
22924
22925     /**
22926      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22927      * @param {Roo.data.Store} store The data store to unbind
22928      */
22929     unbind : function(ds){
22930         ds.un("beforeload", this.beforeLoad, this);
22931         ds.un("load", this.onLoad, this);
22932         ds.un("loadexception", this.onLoadError, this);
22933         ds.un("remove", this.updateInfo, this);
22934         ds.un("add", this.updateInfo, this);
22935         this.ds = undefined;
22936     },
22937
22938     /**
22939      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22940      * @param {Roo.data.Store} store The data store to bind
22941      */
22942     bind : function(ds){
22943         ds.on("beforeload", this.beforeLoad, this);
22944         ds.on("load", this.onLoad, this);
22945         ds.on("loadexception", this.onLoadError, this);
22946         ds.on("remove", this.updateInfo, this);
22947         ds.on("add", this.updateInfo, this);
22948         this.ds = ds;
22949     }
22950 });/*
22951  * - LGPL
22952  *
22953  * element
22954  * 
22955  */
22956
22957 /**
22958  * @class Roo.bootstrap.MessageBar
22959  * @extends Roo.bootstrap.Component
22960  * Bootstrap MessageBar class
22961  * @cfg {String} html contents of the MessageBar
22962  * @cfg {String} weight (info | success | warning | danger) default info
22963  * @cfg {String} beforeClass insert the bar before the given class
22964  * @cfg {Boolean} closable (true | false) default false
22965  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22966  * 
22967  * @constructor
22968  * Create a new Element
22969  * @param {Object} config The config object
22970  */
22971
22972 Roo.bootstrap.MessageBar = function(config){
22973     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22974 };
22975
22976 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22977     
22978     html: '',
22979     weight: 'info',
22980     closable: false,
22981     fixed: false,
22982     beforeClass: 'bootstrap-sticky-wrap',
22983     
22984     getAutoCreate : function(){
22985         
22986         var cfg = {
22987             tag: 'div',
22988             cls: 'alert alert-dismissable alert-' + this.weight,
22989             cn: [
22990                 {
22991                     tag: 'span',
22992                     cls: 'message',
22993                     html: this.html || ''
22994                 }
22995             ]
22996         };
22997         
22998         if(this.fixed){
22999             cfg.cls += ' alert-messages-fixed';
23000         }
23001         
23002         if(this.closable){
23003             cfg.cn.push({
23004                 tag: 'button',
23005                 cls: 'close',
23006                 html: 'x'
23007             });
23008         }
23009         
23010         return cfg;
23011     },
23012     
23013     onRender : function(ct, position)
23014     {
23015         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23016         
23017         if(!this.el){
23018             var cfg = Roo.apply({},  this.getAutoCreate());
23019             cfg.id = Roo.id();
23020             
23021             if (this.cls) {
23022                 cfg.cls += ' ' + this.cls;
23023             }
23024             if (this.style) {
23025                 cfg.style = this.style;
23026             }
23027             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23028             
23029             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23030         }
23031         
23032         this.el.select('>button.close').on('click', this.hide, this);
23033         
23034     },
23035     
23036     show : function()
23037     {
23038         if (!this.rendered) {
23039             this.render();
23040         }
23041         
23042         this.el.show();
23043         
23044         this.fireEvent('show', this);
23045         
23046     },
23047     
23048     hide : function()
23049     {
23050         if (!this.rendered) {
23051             this.render();
23052         }
23053         
23054         this.el.hide();
23055         
23056         this.fireEvent('hide', this);
23057     },
23058     
23059     update : function()
23060     {
23061 //        var e = this.el.dom.firstChild;
23062 //        
23063 //        if(this.closable){
23064 //            e = e.nextSibling;
23065 //        }
23066 //        
23067 //        e.data = this.html || '';
23068
23069         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23070     }
23071    
23072 });
23073
23074  
23075
23076      /*
23077  * - LGPL
23078  *
23079  * Graph
23080  * 
23081  */
23082
23083
23084 /**
23085  * @class Roo.bootstrap.Graph
23086  * @extends Roo.bootstrap.Component
23087  * Bootstrap Graph class
23088 > Prameters
23089  -sm {number} sm 4
23090  -md {number} md 5
23091  @cfg {String} graphtype  bar | vbar | pie
23092  @cfg {number} g_x coodinator | centre x (pie)
23093  @cfg {number} g_y coodinator | centre y (pie)
23094  @cfg {number} g_r radius (pie)
23095  @cfg {number} g_height height of the chart (respected by all elements in the set)
23096  @cfg {number} g_width width of the chart (respected by all elements in the set)
23097  @cfg {Object} title The title of the chart
23098     
23099  -{Array}  values
23100  -opts (object) options for the chart 
23101      o {
23102      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23103      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23104      o vgutter (number)
23105      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.
23106      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23107      o to
23108      o stretch (boolean)
23109      o }
23110  -opts (object) options for the pie
23111      o{
23112      o cut
23113      o startAngle (number)
23114      o endAngle (number)
23115      } 
23116  *
23117  * @constructor
23118  * Create a new Input
23119  * @param {Object} config The config object
23120  */
23121
23122 Roo.bootstrap.Graph = function(config){
23123     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23124     
23125     this.addEvents({
23126         // img events
23127         /**
23128          * @event click
23129          * The img click event for the img.
23130          * @param {Roo.EventObject} e
23131          */
23132         "click" : true
23133     });
23134 };
23135
23136 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23137     
23138     sm: 4,
23139     md: 5,
23140     graphtype: 'bar',
23141     g_height: 250,
23142     g_width: 400,
23143     g_x: 50,
23144     g_y: 50,
23145     g_r: 30,
23146     opts:{
23147         //g_colors: this.colors,
23148         g_type: 'soft',
23149         g_gutter: '20%'
23150
23151     },
23152     title : false,
23153
23154     getAutoCreate : function(){
23155         
23156         var cfg = {
23157             tag: 'div',
23158             html : null
23159         };
23160         
23161         
23162         return  cfg;
23163     },
23164
23165     onRender : function(ct,position){
23166         
23167         
23168         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23169         
23170         if (typeof(Raphael) == 'undefined') {
23171             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23172             return;
23173         }
23174         
23175         this.raphael = Raphael(this.el.dom);
23176         
23177                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23178                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23179                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23180                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23181                 /*
23182                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23183                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23184                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23185                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23186                 
23187                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23188                 r.barchart(330, 10, 300, 220, data1);
23189                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23190                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23191                 */
23192                 
23193                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23194                 // r.barchart(30, 30, 560, 250,  xdata, {
23195                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23196                 //     axis : "0 0 1 1",
23197                 //     axisxlabels :  xdata
23198                 //     //yvalues : cols,
23199                    
23200                 // });
23201 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23202 //        
23203 //        this.load(null,xdata,{
23204 //                axis : "0 0 1 1",
23205 //                axisxlabels :  xdata
23206 //                });
23207
23208     },
23209
23210     load : function(graphtype,xdata,opts)
23211     {
23212         this.raphael.clear();
23213         if(!graphtype) {
23214             graphtype = this.graphtype;
23215         }
23216         if(!opts){
23217             opts = this.opts;
23218         }
23219         var r = this.raphael,
23220             fin = function () {
23221                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23222             },
23223             fout = function () {
23224                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23225             },
23226             pfin = function() {
23227                 this.sector.stop();
23228                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23229
23230                 if (this.label) {
23231                     this.label[0].stop();
23232                     this.label[0].attr({ r: 7.5 });
23233                     this.label[1].attr({ "font-weight": 800 });
23234                 }
23235             },
23236             pfout = function() {
23237                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23238
23239                 if (this.label) {
23240                     this.label[0].animate({ r: 5 }, 500, "bounce");
23241                     this.label[1].attr({ "font-weight": 400 });
23242                 }
23243             };
23244
23245         switch(graphtype){
23246             case 'bar':
23247                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23248                 break;
23249             case 'hbar':
23250                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23251                 break;
23252             case 'pie':
23253 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23254 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23255 //            
23256                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23257                 
23258                 break;
23259
23260         }
23261         
23262         if(this.title){
23263             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23264         }
23265         
23266     },
23267     
23268     setTitle: function(o)
23269     {
23270         this.title = o;
23271     },
23272     
23273     initEvents: function() {
23274         
23275         if(!this.href){
23276             this.el.on('click', this.onClick, this);
23277         }
23278     },
23279     
23280     onClick : function(e)
23281     {
23282         Roo.log('img onclick');
23283         this.fireEvent('click', this, e);
23284     }
23285    
23286 });
23287
23288  
23289 /*
23290  * - LGPL
23291  *
23292  * numberBox
23293  * 
23294  */
23295 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23296
23297 /**
23298  * @class Roo.bootstrap.dash.NumberBox
23299  * @extends Roo.bootstrap.Component
23300  * Bootstrap NumberBox class
23301  * @cfg {String} headline Box headline
23302  * @cfg {String} content Box content
23303  * @cfg {String} icon Box icon
23304  * @cfg {String} footer Footer text
23305  * @cfg {String} fhref Footer href
23306  * 
23307  * @constructor
23308  * Create a new NumberBox
23309  * @param {Object} config The config object
23310  */
23311
23312
23313 Roo.bootstrap.dash.NumberBox = function(config){
23314     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23315     
23316 };
23317
23318 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23319     
23320     headline : '',
23321     content : '',
23322     icon : '',
23323     footer : '',
23324     fhref : '',
23325     ficon : '',
23326     
23327     getAutoCreate : function(){
23328         
23329         var cfg = {
23330             tag : 'div',
23331             cls : 'small-box ',
23332             cn : [
23333                 {
23334                     tag : 'div',
23335                     cls : 'inner',
23336                     cn :[
23337                         {
23338                             tag : 'h3',
23339                             cls : 'roo-headline',
23340                             html : this.headline
23341                         },
23342                         {
23343                             tag : 'p',
23344                             cls : 'roo-content',
23345                             html : this.content
23346                         }
23347                     ]
23348                 }
23349             ]
23350         };
23351         
23352         if(this.icon){
23353             cfg.cn.push({
23354                 tag : 'div',
23355                 cls : 'icon',
23356                 cn :[
23357                     {
23358                         tag : 'i',
23359                         cls : 'ion ' + this.icon
23360                     }
23361                 ]
23362             });
23363         }
23364         
23365         if(this.footer){
23366             var footer = {
23367                 tag : 'a',
23368                 cls : 'small-box-footer',
23369                 href : this.fhref || '#',
23370                 html : this.footer
23371             };
23372             
23373             cfg.cn.push(footer);
23374             
23375         }
23376         
23377         return  cfg;
23378     },
23379
23380     onRender : function(ct,position){
23381         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23382
23383
23384        
23385                 
23386     },
23387
23388     setHeadline: function (value)
23389     {
23390         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23391     },
23392     
23393     setFooter: function (value, href)
23394     {
23395         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23396         
23397         if(href){
23398             this.el.select('a.small-box-footer',true).first().attr('href', href);
23399         }
23400         
23401     },
23402
23403     setContent: function (value)
23404     {
23405         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23406     },
23407
23408     initEvents: function() 
23409     {   
23410         
23411     }
23412     
23413 });
23414
23415  
23416 /*
23417  * - LGPL
23418  *
23419  * TabBox
23420  * 
23421  */
23422 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23423
23424 /**
23425  * @class Roo.bootstrap.dash.TabBox
23426  * @extends Roo.bootstrap.Component
23427  * Bootstrap TabBox class
23428  * @cfg {String} title Title of the TabBox
23429  * @cfg {String} icon Icon of the TabBox
23430  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23431  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23432  * 
23433  * @constructor
23434  * Create a new TabBox
23435  * @param {Object} config The config object
23436  */
23437
23438
23439 Roo.bootstrap.dash.TabBox = function(config){
23440     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23441     this.addEvents({
23442         // raw events
23443         /**
23444          * @event addpane
23445          * When a pane is added
23446          * @param {Roo.bootstrap.dash.TabPane} pane
23447          */
23448         "addpane" : true,
23449         /**
23450          * @event activatepane
23451          * When a pane is activated
23452          * @param {Roo.bootstrap.dash.TabPane} pane
23453          */
23454         "activatepane" : true
23455         
23456          
23457     });
23458     
23459     this.panes = [];
23460 };
23461
23462 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23463
23464     title : '',
23465     icon : false,
23466     showtabs : true,
23467     tabScrollable : false,
23468     
23469     getChildContainer : function()
23470     {
23471         return this.el.select('.tab-content', true).first();
23472     },
23473     
23474     getAutoCreate : function(){
23475         
23476         var header = {
23477             tag: 'li',
23478             cls: 'pull-left header',
23479             html: this.title,
23480             cn : []
23481         };
23482         
23483         if(this.icon){
23484             header.cn.push({
23485                 tag: 'i',
23486                 cls: 'fa ' + this.icon
23487             });
23488         }
23489         
23490         var h = {
23491             tag: 'ul',
23492             cls: 'nav nav-tabs pull-right',
23493             cn: [
23494                 header
23495             ]
23496         };
23497         
23498         if(this.tabScrollable){
23499             h = {
23500                 tag: 'div',
23501                 cls: 'tab-header',
23502                 cn: [
23503                     {
23504                         tag: 'ul',
23505                         cls: 'nav nav-tabs pull-right',
23506                         cn: [
23507                             header
23508                         ]
23509                     }
23510                 ]
23511             };
23512         }
23513         
23514         var cfg = {
23515             tag: 'div',
23516             cls: 'nav-tabs-custom',
23517             cn: [
23518                 h,
23519                 {
23520                     tag: 'div',
23521                     cls: 'tab-content no-padding',
23522                     cn: []
23523                 }
23524             ]
23525         };
23526
23527         return  cfg;
23528     },
23529     initEvents : function()
23530     {
23531         //Roo.log('add add pane handler');
23532         this.on('addpane', this.onAddPane, this);
23533     },
23534      /**
23535      * Updates the box title
23536      * @param {String} html to set the title to.
23537      */
23538     setTitle : function(value)
23539     {
23540         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23541     },
23542     onAddPane : function(pane)
23543     {
23544         this.panes.push(pane);
23545         //Roo.log('addpane');
23546         //Roo.log(pane);
23547         // tabs are rendere left to right..
23548         if(!this.showtabs){
23549             return;
23550         }
23551         
23552         var ctr = this.el.select('.nav-tabs', true).first();
23553          
23554          
23555         var existing = ctr.select('.nav-tab',true);
23556         var qty = existing.getCount();;
23557         
23558         
23559         var tab = ctr.createChild({
23560             tag : 'li',
23561             cls : 'nav-tab' + (qty ? '' : ' active'),
23562             cn : [
23563                 {
23564                     tag : 'a',
23565                     href:'#',
23566                     html : pane.title
23567                 }
23568             ]
23569         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23570         pane.tab = tab;
23571         
23572         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23573         if (!qty) {
23574             pane.el.addClass('active');
23575         }
23576         
23577                 
23578     },
23579     onTabClick : function(ev,un,ob,pane)
23580     {
23581         //Roo.log('tab - prev default');
23582         ev.preventDefault();
23583         
23584         
23585         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23586         pane.tab.addClass('active');
23587         //Roo.log(pane.title);
23588         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23589         // technically we should have a deactivate event.. but maybe add later.
23590         // and it should not de-activate the selected tab...
23591         this.fireEvent('activatepane', pane);
23592         pane.el.addClass('active');
23593         pane.fireEvent('activate');
23594         
23595         
23596     },
23597     
23598     getActivePane : function()
23599     {
23600         var r = false;
23601         Roo.each(this.panes, function(p) {
23602             if(p.el.hasClass('active')){
23603                 r = p;
23604                 return false;
23605             }
23606             
23607             return;
23608         });
23609         
23610         return r;
23611     }
23612     
23613     
23614 });
23615
23616  
23617 /*
23618  * - LGPL
23619  *
23620  * Tab pane
23621  * 
23622  */
23623 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23624 /**
23625  * @class Roo.bootstrap.TabPane
23626  * @extends Roo.bootstrap.Component
23627  * Bootstrap TabPane class
23628  * @cfg {Boolean} active (false | true) Default false
23629  * @cfg {String} title title of panel
23630
23631  * 
23632  * @constructor
23633  * Create a new TabPane
23634  * @param {Object} config The config object
23635  */
23636
23637 Roo.bootstrap.dash.TabPane = function(config){
23638     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23639     
23640     this.addEvents({
23641         // raw events
23642         /**
23643          * @event activate
23644          * When a pane is activated
23645          * @param {Roo.bootstrap.dash.TabPane} pane
23646          */
23647         "activate" : true
23648          
23649     });
23650 };
23651
23652 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23653     
23654     active : false,
23655     title : '',
23656     
23657     // the tabBox that this is attached to.
23658     tab : false,
23659      
23660     getAutoCreate : function() 
23661     {
23662         var cfg = {
23663             tag: 'div',
23664             cls: 'tab-pane'
23665         };
23666         
23667         if(this.active){
23668             cfg.cls += ' active';
23669         }
23670         
23671         return cfg;
23672     },
23673     initEvents  : function()
23674     {
23675         //Roo.log('trigger add pane handler');
23676         this.parent().fireEvent('addpane', this)
23677     },
23678     
23679      /**
23680      * Updates the tab title 
23681      * @param {String} html to set the title to.
23682      */
23683     setTitle: function(str)
23684     {
23685         if (!this.tab) {
23686             return;
23687         }
23688         this.title = str;
23689         this.tab.select('a', true).first().dom.innerHTML = str;
23690         
23691     }
23692     
23693     
23694     
23695 });
23696
23697  
23698
23699
23700  /*
23701  * - LGPL
23702  *
23703  * menu
23704  * 
23705  */
23706 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23707
23708 /**
23709  * @class Roo.bootstrap.menu.Menu
23710  * @extends Roo.bootstrap.Component
23711  * Bootstrap Menu class - container for Menu
23712  * @cfg {String} html Text of the menu
23713  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23714  * @cfg {String} icon Font awesome icon
23715  * @cfg {String} pos Menu align to (top | bottom) default bottom
23716  * 
23717  * 
23718  * @constructor
23719  * Create a new Menu
23720  * @param {Object} config The config object
23721  */
23722
23723
23724 Roo.bootstrap.menu.Menu = function(config){
23725     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23726     
23727     this.addEvents({
23728         /**
23729          * @event beforeshow
23730          * Fires before this menu is displayed
23731          * @param {Roo.bootstrap.menu.Menu} this
23732          */
23733         beforeshow : true,
23734         /**
23735          * @event beforehide
23736          * Fires before this menu is hidden
23737          * @param {Roo.bootstrap.menu.Menu} this
23738          */
23739         beforehide : true,
23740         /**
23741          * @event show
23742          * Fires after this menu is displayed
23743          * @param {Roo.bootstrap.menu.Menu} this
23744          */
23745         show : true,
23746         /**
23747          * @event hide
23748          * Fires after this menu is hidden
23749          * @param {Roo.bootstrap.menu.Menu} this
23750          */
23751         hide : true,
23752         /**
23753          * @event click
23754          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23755          * @param {Roo.bootstrap.menu.Menu} this
23756          * @param {Roo.EventObject} e
23757          */
23758         click : true
23759     });
23760     
23761 };
23762
23763 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23764     
23765     submenu : false,
23766     html : '',
23767     weight : 'default',
23768     icon : false,
23769     pos : 'bottom',
23770     
23771     
23772     getChildContainer : function() {
23773         if(this.isSubMenu){
23774             return this.el;
23775         }
23776         
23777         return this.el.select('ul.dropdown-menu', true).first();  
23778     },
23779     
23780     getAutoCreate : function()
23781     {
23782         var text = [
23783             {
23784                 tag : 'span',
23785                 cls : 'roo-menu-text',
23786                 html : this.html
23787             }
23788         ];
23789         
23790         if(this.icon){
23791             text.unshift({
23792                 tag : 'i',
23793                 cls : 'fa ' + this.icon
23794             })
23795         }
23796         
23797         
23798         var cfg = {
23799             tag : 'div',
23800             cls : 'btn-group',
23801             cn : [
23802                 {
23803                     tag : 'button',
23804                     cls : 'dropdown-button btn btn-' + this.weight,
23805                     cn : text
23806                 },
23807                 {
23808                     tag : 'button',
23809                     cls : 'dropdown-toggle btn btn-' + this.weight,
23810                     cn : [
23811                         {
23812                             tag : 'span',
23813                             cls : 'caret'
23814                         }
23815                     ]
23816                 },
23817                 {
23818                     tag : 'ul',
23819                     cls : 'dropdown-menu'
23820                 }
23821             ]
23822             
23823         };
23824         
23825         if(this.pos == 'top'){
23826             cfg.cls += ' dropup';
23827         }
23828         
23829         if(this.isSubMenu){
23830             cfg = {
23831                 tag : 'ul',
23832                 cls : 'dropdown-menu'
23833             }
23834         }
23835         
23836         return cfg;
23837     },
23838     
23839     onRender : function(ct, position)
23840     {
23841         this.isSubMenu = ct.hasClass('dropdown-submenu');
23842         
23843         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23844     },
23845     
23846     initEvents : function() 
23847     {
23848         if(this.isSubMenu){
23849             return;
23850         }
23851         
23852         this.hidden = true;
23853         
23854         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23855         this.triggerEl.on('click', this.onTriggerPress, this);
23856         
23857         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23858         this.buttonEl.on('click', this.onClick, this);
23859         
23860     },
23861     
23862     list : function()
23863     {
23864         if(this.isSubMenu){
23865             return this.el;
23866         }
23867         
23868         return this.el.select('ul.dropdown-menu', true).first();
23869     },
23870     
23871     onClick : function(e)
23872     {
23873         this.fireEvent("click", this, e);
23874     },
23875     
23876     onTriggerPress  : function(e)
23877     {   
23878         if (this.isVisible()) {
23879             this.hide();
23880         } else {
23881             this.show();
23882         }
23883     },
23884     
23885     isVisible : function(){
23886         return !this.hidden;
23887     },
23888     
23889     show : function()
23890     {
23891         this.fireEvent("beforeshow", this);
23892         
23893         this.hidden = false;
23894         this.el.addClass('open');
23895         
23896         Roo.get(document).on("mouseup", this.onMouseUp, this);
23897         
23898         this.fireEvent("show", this);
23899         
23900         
23901     },
23902     
23903     hide : function()
23904     {
23905         this.fireEvent("beforehide", this);
23906         
23907         this.hidden = true;
23908         this.el.removeClass('open');
23909         
23910         Roo.get(document).un("mouseup", this.onMouseUp);
23911         
23912         this.fireEvent("hide", this);
23913     },
23914     
23915     onMouseUp : function()
23916     {
23917         this.hide();
23918     }
23919     
23920 });
23921
23922  
23923  /*
23924  * - LGPL
23925  *
23926  * menu item
23927  * 
23928  */
23929 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23930
23931 /**
23932  * @class Roo.bootstrap.menu.Item
23933  * @extends Roo.bootstrap.Component
23934  * Bootstrap MenuItem class
23935  * @cfg {Boolean} submenu (true | false) default false
23936  * @cfg {String} html text of the item
23937  * @cfg {String} href the link
23938  * @cfg {Boolean} disable (true | false) default false
23939  * @cfg {Boolean} preventDefault (true | false) default true
23940  * @cfg {String} icon Font awesome icon
23941  * @cfg {String} pos Submenu align to (left | right) default right 
23942  * 
23943  * 
23944  * @constructor
23945  * Create a new Item
23946  * @param {Object} config The config object
23947  */
23948
23949
23950 Roo.bootstrap.menu.Item = function(config){
23951     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23952     this.addEvents({
23953         /**
23954          * @event mouseover
23955          * Fires when the mouse is hovering over this menu
23956          * @param {Roo.bootstrap.menu.Item} this
23957          * @param {Roo.EventObject} e
23958          */
23959         mouseover : true,
23960         /**
23961          * @event mouseout
23962          * Fires when the mouse exits this menu
23963          * @param {Roo.bootstrap.menu.Item} this
23964          * @param {Roo.EventObject} e
23965          */
23966         mouseout : true,
23967         // raw events
23968         /**
23969          * @event click
23970          * The raw click event for the entire grid.
23971          * @param {Roo.EventObject} e
23972          */
23973         click : true
23974     });
23975 };
23976
23977 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23978     
23979     submenu : false,
23980     href : '',
23981     html : '',
23982     preventDefault: true,
23983     disable : false,
23984     icon : false,
23985     pos : 'right',
23986     
23987     getAutoCreate : function()
23988     {
23989         var text = [
23990             {
23991                 tag : 'span',
23992                 cls : 'roo-menu-item-text',
23993                 html : this.html
23994             }
23995         ];
23996         
23997         if(this.icon){
23998             text.unshift({
23999                 tag : 'i',
24000                 cls : 'fa ' + this.icon
24001             })
24002         }
24003         
24004         var cfg = {
24005             tag : 'li',
24006             cn : [
24007                 {
24008                     tag : 'a',
24009                     href : this.href || '#',
24010                     cn : text
24011                 }
24012             ]
24013         };
24014         
24015         if(this.disable){
24016             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24017         }
24018         
24019         if(this.submenu){
24020             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24021             
24022             if(this.pos == 'left'){
24023                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24024             }
24025         }
24026         
24027         return cfg;
24028     },
24029     
24030     initEvents : function() 
24031     {
24032         this.el.on('mouseover', this.onMouseOver, this);
24033         this.el.on('mouseout', this.onMouseOut, this);
24034         
24035         this.el.select('a', true).first().on('click', this.onClick, this);
24036         
24037     },
24038     
24039     onClick : function(e)
24040     {
24041         if(this.preventDefault){
24042             e.preventDefault();
24043         }
24044         
24045         this.fireEvent("click", this, e);
24046     },
24047     
24048     onMouseOver : function(e)
24049     {
24050         if(this.submenu && this.pos == 'left'){
24051             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24052         }
24053         
24054         this.fireEvent("mouseover", this, e);
24055     },
24056     
24057     onMouseOut : function(e)
24058     {
24059         this.fireEvent("mouseout", this, e);
24060     }
24061 });
24062
24063  
24064
24065  /*
24066  * - LGPL
24067  *
24068  * menu separator
24069  * 
24070  */
24071 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24072
24073 /**
24074  * @class Roo.bootstrap.menu.Separator
24075  * @extends Roo.bootstrap.Component
24076  * Bootstrap Separator class
24077  * 
24078  * @constructor
24079  * Create a new Separator
24080  * @param {Object} config The config object
24081  */
24082
24083
24084 Roo.bootstrap.menu.Separator = function(config){
24085     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24086 };
24087
24088 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24089     
24090     getAutoCreate : function(){
24091         var cfg = {
24092             tag : 'li',
24093             cls: 'divider'
24094         };
24095         
24096         return cfg;
24097     }
24098    
24099 });
24100
24101  
24102
24103  /*
24104  * - LGPL
24105  *
24106  * Tooltip
24107  * 
24108  */
24109
24110 /**
24111  * @class Roo.bootstrap.Tooltip
24112  * Bootstrap Tooltip class
24113  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24114  * to determine which dom element triggers the tooltip.
24115  * 
24116  * It needs to add support for additional attributes like tooltip-position
24117  * 
24118  * @constructor
24119  * Create a new Toolti
24120  * @param {Object} config The config object
24121  */
24122
24123 Roo.bootstrap.Tooltip = function(config){
24124     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24125 };
24126
24127 Roo.apply(Roo.bootstrap.Tooltip, {
24128     /**
24129      * @function init initialize tooltip monitoring.
24130      * @static
24131      */
24132     currentEl : false,
24133     currentTip : false,
24134     currentRegion : false,
24135     
24136     //  init : delay?
24137     
24138     init : function()
24139     {
24140         Roo.get(document).on('mouseover', this.enter ,this);
24141         Roo.get(document).on('mouseout', this.leave, this);
24142          
24143         
24144         this.currentTip = new Roo.bootstrap.Tooltip();
24145     },
24146     
24147     enter : function(ev)
24148     {
24149         var dom = ev.getTarget();
24150         
24151         //Roo.log(['enter',dom]);
24152         var el = Roo.fly(dom);
24153         if (this.currentEl) {
24154             //Roo.log(dom);
24155             //Roo.log(this.currentEl);
24156             //Roo.log(this.currentEl.contains(dom));
24157             if (this.currentEl == el) {
24158                 return;
24159             }
24160             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24161                 return;
24162             }
24163
24164         }
24165         
24166         if (this.currentTip.el) {
24167             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24168         }    
24169         //Roo.log(ev);
24170         
24171         if(!el || el.dom == document){
24172             return;
24173         }
24174         
24175         var bindEl = el;
24176         
24177         // you can not look for children, as if el is the body.. then everythign is the child..
24178         if (!el.attr('tooltip')) { //
24179             if (!el.select("[tooltip]").elements.length) {
24180                 return;
24181             }
24182             // is the mouse over this child...?
24183             bindEl = el.select("[tooltip]").first();
24184             var xy = ev.getXY();
24185             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24186                 //Roo.log("not in region.");
24187                 return;
24188             }
24189             //Roo.log("child element over..");
24190             
24191         }
24192         this.currentEl = bindEl;
24193         this.currentTip.bind(bindEl);
24194         this.currentRegion = Roo.lib.Region.getRegion(dom);
24195         this.currentTip.enter();
24196         
24197     },
24198     leave : function(ev)
24199     {
24200         var dom = ev.getTarget();
24201         //Roo.log(['leave',dom]);
24202         if (!this.currentEl) {
24203             return;
24204         }
24205         
24206         
24207         if (dom != this.currentEl.dom) {
24208             return;
24209         }
24210         var xy = ev.getXY();
24211         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24212             return;
24213         }
24214         // only activate leave if mouse cursor is outside... bounding box..
24215         
24216         
24217         
24218         
24219         if (this.currentTip) {
24220             this.currentTip.leave();
24221         }
24222         //Roo.log('clear currentEl');
24223         this.currentEl = false;
24224         
24225         
24226     },
24227     alignment : {
24228         'left' : ['r-l', [-2,0], 'right'],
24229         'right' : ['l-r', [2,0], 'left'],
24230         'bottom' : ['t-b', [0,2], 'top'],
24231         'top' : [ 'b-t', [0,-2], 'bottom']
24232     }
24233     
24234 });
24235
24236
24237 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24238     
24239     
24240     bindEl : false,
24241     
24242     delay : null, // can be { show : 300 , hide: 500}
24243     
24244     timeout : null,
24245     
24246     hoverState : null, //???
24247     
24248     placement : 'bottom', 
24249     
24250     getAutoCreate : function(){
24251     
24252         var cfg = {
24253            cls : 'tooltip',
24254            role : 'tooltip',
24255            cn : [
24256                 {
24257                     cls : 'tooltip-arrow'
24258                 },
24259                 {
24260                     cls : 'tooltip-inner'
24261                 }
24262            ]
24263         };
24264         
24265         return cfg;
24266     },
24267     bind : function(el)
24268     {
24269         this.bindEl = el;
24270     },
24271       
24272     
24273     enter : function () {
24274        
24275         if (this.timeout != null) {
24276             clearTimeout(this.timeout);
24277         }
24278         
24279         this.hoverState = 'in';
24280          //Roo.log("enter - show");
24281         if (!this.delay || !this.delay.show) {
24282             this.show();
24283             return;
24284         }
24285         var _t = this;
24286         this.timeout = setTimeout(function () {
24287             if (_t.hoverState == 'in') {
24288                 _t.show();
24289             }
24290         }, this.delay.show);
24291     },
24292     leave : function()
24293     {
24294         clearTimeout(this.timeout);
24295     
24296         this.hoverState = 'out';
24297          if (!this.delay || !this.delay.hide) {
24298             this.hide();
24299             return;
24300         }
24301        
24302         var _t = this;
24303         this.timeout = setTimeout(function () {
24304             //Roo.log("leave - timeout");
24305             
24306             if (_t.hoverState == 'out') {
24307                 _t.hide();
24308                 Roo.bootstrap.Tooltip.currentEl = false;
24309             }
24310         }, delay);
24311     },
24312     
24313     show : function ()
24314     {
24315         if (!this.el) {
24316             this.render(document.body);
24317         }
24318         // set content.
24319         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24320         
24321         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24322         
24323         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24324         
24325         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24326         
24327         var placement = typeof this.placement == 'function' ?
24328             this.placement.call(this, this.el, on_el) :
24329             this.placement;
24330             
24331         var autoToken = /\s?auto?\s?/i;
24332         var autoPlace = autoToken.test(placement);
24333         if (autoPlace) {
24334             placement = placement.replace(autoToken, '') || 'top';
24335         }
24336         
24337         //this.el.detach()
24338         //this.el.setXY([0,0]);
24339         this.el.show();
24340         //this.el.dom.style.display='block';
24341         
24342         //this.el.appendTo(on_el);
24343         
24344         var p = this.getPosition();
24345         var box = this.el.getBox();
24346         
24347         if (autoPlace) {
24348             // fixme..
24349         }
24350         
24351         var align = Roo.bootstrap.Tooltip.alignment[placement];
24352         
24353         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24354         
24355         if(placement == 'top' || placement == 'bottom'){
24356             if(xy[0] < 0){
24357                 placement = 'right';
24358             }
24359             
24360             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24361                 placement = 'left';
24362             }
24363             
24364             var scroll = Roo.select('body', true).first().getScroll();
24365             
24366             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24367                 placement = 'top';
24368             }
24369             
24370         }
24371         
24372         align = Roo.bootstrap.Tooltip.alignment[placement];
24373         
24374         this.el.alignTo(this.bindEl, align[0],align[1]);
24375         //var arrow = this.el.select('.arrow',true).first();
24376         //arrow.set(align[2], 
24377         
24378         this.el.addClass(placement);
24379         
24380         this.el.addClass('in fade');
24381         
24382         this.hoverState = null;
24383         
24384         if (this.el.hasClass('fade')) {
24385             // fade it?
24386         }
24387         
24388     },
24389     hide : function()
24390     {
24391          
24392         if (!this.el) {
24393             return;
24394         }
24395         //this.el.setXY([0,0]);
24396         this.el.removeClass('in');
24397         //this.el.hide();
24398         
24399     }
24400     
24401 });
24402  
24403
24404  /*
24405  * - LGPL
24406  *
24407  * Location Picker
24408  * 
24409  */
24410
24411 /**
24412  * @class Roo.bootstrap.LocationPicker
24413  * @extends Roo.bootstrap.Component
24414  * Bootstrap LocationPicker class
24415  * @cfg {Number} latitude Position when init default 0
24416  * @cfg {Number} longitude Position when init default 0
24417  * @cfg {Number} zoom default 15
24418  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24419  * @cfg {Boolean} mapTypeControl default false
24420  * @cfg {Boolean} disableDoubleClickZoom default false
24421  * @cfg {Boolean} scrollwheel default true
24422  * @cfg {Boolean} streetViewControl default false
24423  * @cfg {Number} radius default 0
24424  * @cfg {String} locationName
24425  * @cfg {Boolean} draggable default true
24426  * @cfg {Boolean} enableAutocomplete default false
24427  * @cfg {Boolean} enableReverseGeocode default true
24428  * @cfg {String} markerTitle
24429  * 
24430  * @constructor
24431  * Create a new LocationPicker
24432  * @param {Object} config The config object
24433  */
24434
24435
24436 Roo.bootstrap.LocationPicker = function(config){
24437     
24438     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24439     
24440     this.addEvents({
24441         /**
24442          * @event initial
24443          * Fires when the picker initialized.
24444          * @param {Roo.bootstrap.LocationPicker} this
24445          * @param {Google Location} location
24446          */
24447         initial : true,
24448         /**
24449          * @event positionchanged
24450          * Fires when the picker position changed.
24451          * @param {Roo.bootstrap.LocationPicker} this
24452          * @param {Google Location} location
24453          */
24454         positionchanged : true,
24455         /**
24456          * @event resize
24457          * Fires when the map resize.
24458          * @param {Roo.bootstrap.LocationPicker} this
24459          */
24460         resize : true,
24461         /**
24462          * @event show
24463          * Fires when the map show.
24464          * @param {Roo.bootstrap.LocationPicker} this
24465          */
24466         show : true,
24467         /**
24468          * @event hide
24469          * Fires when the map hide.
24470          * @param {Roo.bootstrap.LocationPicker} this
24471          */
24472         hide : true,
24473         /**
24474          * @event mapClick
24475          * Fires when click the map.
24476          * @param {Roo.bootstrap.LocationPicker} this
24477          * @param {Map event} e
24478          */
24479         mapClick : true,
24480         /**
24481          * @event mapRightClick
24482          * Fires when right click the map.
24483          * @param {Roo.bootstrap.LocationPicker} this
24484          * @param {Map event} e
24485          */
24486         mapRightClick : true,
24487         /**
24488          * @event markerClick
24489          * Fires when click the marker.
24490          * @param {Roo.bootstrap.LocationPicker} this
24491          * @param {Map event} e
24492          */
24493         markerClick : true,
24494         /**
24495          * @event markerRightClick
24496          * Fires when right click the marker.
24497          * @param {Roo.bootstrap.LocationPicker} this
24498          * @param {Map event} e
24499          */
24500         markerRightClick : true,
24501         /**
24502          * @event OverlayViewDraw
24503          * Fires when OverlayView Draw
24504          * @param {Roo.bootstrap.LocationPicker} this
24505          */
24506         OverlayViewDraw : true,
24507         /**
24508          * @event OverlayViewOnAdd
24509          * Fires when OverlayView Draw
24510          * @param {Roo.bootstrap.LocationPicker} this
24511          */
24512         OverlayViewOnAdd : true,
24513         /**
24514          * @event OverlayViewOnRemove
24515          * Fires when OverlayView Draw
24516          * @param {Roo.bootstrap.LocationPicker} this
24517          */
24518         OverlayViewOnRemove : true,
24519         /**
24520          * @event OverlayViewShow
24521          * Fires when OverlayView Draw
24522          * @param {Roo.bootstrap.LocationPicker} this
24523          * @param {Pixel} cpx
24524          */
24525         OverlayViewShow : true,
24526         /**
24527          * @event OverlayViewHide
24528          * Fires when OverlayView Draw
24529          * @param {Roo.bootstrap.LocationPicker} this
24530          */
24531         OverlayViewHide : true,
24532         /**
24533          * @event loadexception
24534          * Fires when load google lib failed.
24535          * @param {Roo.bootstrap.LocationPicker} this
24536          */
24537         loadexception : true
24538     });
24539         
24540 };
24541
24542 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24543     
24544     gMapContext: false,
24545     
24546     latitude: 0,
24547     longitude: 0,
24548     zoom: 15,
24549     mapTypeId: false,
24550     mapTypeControl: false,
24551     disableDoubleClickZoom: false,
24552     scrollwheel: true,
24553     streetViewControl: false,
24554     radius: 0,
24555     locationName: '',
24556     draggable: true,
24557     enableAutocomplete: false,
24558     enableReverseGeocode: true,
24559     markerTitle: '',
24560     
24561     getAutoCreate: function()
24562     {
24563
24564         var cfg = {
24565             tag: 'div',
24566             cls: 'roo-location-picker'
24567         };
24568         
24569         return cfg
24570     },
24571     
24572     initEvents: function(ct, position)
24573     {       
24574         if(!this.el.getWidth() || this.isApplied()){
24575             return;
24576         }
24577         
24578         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24579         
24580         this.initial();
24581     },
24582     
24583     initial: function()
24584     {
24585         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24586             this.fireEvent('loadexception', this);
24587             return;
24588         }
24589         
24590         if(!this.mapTypeId){
24591             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24592         }
24593         
24594         this.gMapContext = this.GMapContext();
24595         
24596         this.initOverlayView();
24597         
24598         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24599         
24600         var _this = this;
24601                 
24602         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24603             _this.setPosition(_this.gMapContext.marker.position);
24604         });
24605         
24606         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24607             _this.fireEvent('mapClick', this, event);
24608             
24609         });
24610
24611         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24612             _this.fireEvent('mapRightClick', this, event);
24613             
24614         });
24615         
24616         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24617             _this.fireEvent('markerClick', this, event);
24618             
24619         });
24620
24621         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24622             _this.fireEvent('markerRightClick', this, event);
24623             
24624         });
24625         
24626         this.setPosition(this.gMapContext.location);
24627         
24628         this.fireEvent('initial', this, this.gMapContext.location);
24629     },
24630     
24631     initOverlayView: function()
24632     {
24633         var _this = this;
24634         
24635         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24636             
24637             draw: function()
24638             {
24639                 _this.fireEvent('OverlayViewDraw', _this);
24640             },
24641             
24642             onAdd: function()
24643             {
24644                 _this.fireEvent('OverlayViewOnAdd', _this);
24645             },
24646             
24647             onRemove: function()
24648             {
24649                 _this.fireEvent('OverlayViewOnRemove', _this);
24650             },
24651             
24652             show: function(cpx)
24653             {
24654                 _this.fireEvent('OverlayViewShow', _this, cpx);
24655             },
24656             
24657             hide: function()
24658             {
24659                 _this.fireEvent('OverlayViewHide', _this);
24660             }
24661             
24662         });
24663     },
24664     
24665     fromLatLngToContainerPixel: function(event)
24666     {
24667         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24668     },
24669     
24670     isApplied: function() 
24671     {
24672         return this.getGmapContext() == false ? false : true;
24673     },
24674     
24675     getGmapContext: function() 
24676     {
24677         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24678     },
24679     
24680     GMapContext: function() 
24681     {
24682         var position = new google.maps.LatLng(this.latitude, this.longitude);
24683         
24684         var _map = new google.maps.Map(this.el.dom, {
24685             center: position,
24686             zoom: this.zoom,
24687             mapTypeId: this.mapTypeId,
24688             mapTypeControl: this.mapTypeControl,
24689             disableDoubleClickZoom: this.disableDoubleClickZoom,
24690             scrollwheel: this.scrollwheel,
24691             streetViewControl: this.streetViewControl,
24692             locationName: this.locationName,
24693             draggable: this.draggable,
24694             enableAutocomplete: this.enableAutocomplete,
24695             enableReverseGeocode: this.enableReverseGeocode
24696         });
24697         
24698         var _marker = new google.maps.Marker({
24699             position: position,
24700             map: _map,
24701             title: this.markerTitle,
24702             draggable: this.draggable
24703         });
24704         
24705         return {
24706             map: _map,
24707             marker: _marker,
24708             circle: null,
24709             location: position,
24710             radius: this.radius,
24711             locationName: this.locationName,
24712             addressComponents: {
24713                 formatted_address: null,
24714                 addressLine1: null,
24715                 addressLine2: null,
24716                 streetName: null,
24717                 streetNumber: null,
24718                 city: null,
24719                 district: null,
24720                 state: null,
24721                 stateOrProvince: null
24722             },
24723             settings: this,
24724             domContainer: this.el.dom,
24725             geodecoder: new google.maps.Geocoder()
24726         };
24727     },
24728     
24729     drawCircle: function(center, radius, options) 
24730     {
24731         if (this.gMapContext.circle != null) {
24732             this.gMapContext.circle.setMap(null);
24733         }
24734         if (radius > 0) {
24735             radius *= 1;
24736             options = Roo.apply({}, options, {
24737                 strokeColor: "#0000FF",
24738                 strokeOpacity: .35,
24739                 strokeWeight: 2,
24740                 fillColor: "#0000FF",
24741                 fillOpacity: .2
24742             });
24743             
24744             options.map = this.gMapContext.map;
24745             options.radius = radius;
24746             options.center = center;
24747             this.gMapContext.circle = new google.maps.Circle(options);
24748             return this.gMapContext.circle;
24749         }
24750         
24751         return null;
24752     },
24753     
24754     setPosition: function(location) 
24755     {
24756         this.gMapContext.location = location;
24757         this.gMapContext.marker.setPosition(location);
24758         this.gMapContext.map.panTo(location);
24759         this.drawCircle(location, this.gMapContext.radius, {});
24760         
24761         var _this = this;
24762         
24763         if (this.gMapContext.settings.enableReverseGeocode) {
24764             this.gMapContext.geodecoder.geocode({
24765                 latLng: this.gMapContext.location
24766             }, function(results, status) {
24767                 
24768                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24769                     _this.gMapContext.locationName = results[0].formatted_address;
24770                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24771                     
24772                     _this.fireEvent('positionchanged', this, location);
24773                 }
24774             });
24775             
24776             return;
24777         }
24778         
24779         this.fireEvent('positionchanged', this, location);
24780     },
24781     
24782     resize: function()
24783     {
24784         google.maps.event.trigger(this.gMapContext.map, "resize");
24785         
24786         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24787         
24788         this.fireEvent('resize', this);
24789     },
24790     
24791     setPositionByLatLng: function(latitude, longitude)
24792     {
24793         this.setPosition(new google.maps.LatLng(latitude, longitude));
24794     },
24795     
24796     getCurrentPosition: function() 
24797     {
24798         return {
24799             latitude: this.gMapContext.location.lat(),
24800             longitude: this.gMapContext.location.lng()
24801         };
24802     },
24803     
24804     getAddressName: function() 
24805     {
24806         return this.gMapContext.locationName;
24807     },
24808     
24809     getAddressComponents: function() 
24810     {
24811         return this.gMapContext.addressComponents;
24812     },
24813     
24814     address_component_from_google_geocode: function(address_components) 
24815     {
24816         var result = {};
24817         
24818         for (var i = 0; i < address_components.length; i++) {
24819             var component = address_components[i];
24820             if (component.types.indexOf("postal_code") >= 0) {
24821                 result.postalCode = component.short_name;
24822             } else if (component.types.indexOf("street_number") >= 0) {
24823                 result.streetNumber = component.short_name;
24824             } else if (component.types.indexOf("route") >= 0) {
24825                 result.streetName = component.short_name;
24826             } else if (component.types.indexOf("neighborhood") >= 0) {
24827                 result.city = component.short_name;
24828             } else if (component.types.indexOf("locality") >= 0) {
24829                 result.city = component.short_name;
24830             } else if (component.types.indexOf("sublocality") >= 0) {
24831                 result.district = component.short_name;
24832             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24833                 result.stateOrProvince = component.short_name;
24834             } else if (component.types.indexOf("country") >= 0) {
24835                 result.country = component.short_name;
24836             }
24837         }
24838         
24839         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24840         result.addressLine2 = "";
24841         return result;
24842     },
24843     
24844     setZoomLevel: function(zoom)
24845     {
24846         this.gMapContext.map.setZoom(zoom);
24847     },
24848     
24849     show: function()
24850     {
24851         if(!this.el){
24852             return;
24853         }
24854         
24855         this.el.show();
24856         
24857         this.resize();
24858         
24859         this.fireEvent('show', this);
24860     },
24861     
24862     hide: function()
24863     {
24864         if(!this.el){
24865             return;
24866         }
24867         
24868         this.el.hide();
24869         
24870         this.fireEvent('hide', this);
24871     }
24872     
24873 });
24874
24875 Roo.apply(Roo.bootstrap.LocationPicker, {
24876     
24877     OverlayView : function(map, options)
24878     {
24879         options = options || {};
24880         
24881         this.setMap(map);
24882     }
24883     
24884     
24885 });/*
24886  * - LGPL
24887  *
24888  * Alert
24889  * 
24890  */
24891
24892 /**
24893  * @class Roo.bootstrap.Alert
24894  * @extends Roo.bootstrap.Component
24895  * Bootstrap Alert class
24896  * @cfg {String} title The title of alert
24897  * @cfg {String} html The content of alert
24898  * @cfg {String} weight (  success | info | warning | danger )
24899  * @cfg {String} faicon font-awesomeicon
24900  * 
24901  * @constructor
24902  * Create a new alert
24903  * @param {Object} config The config object
24904  */
24905
24906
24907 Roo.bootstrap.Alert = function(config){
24908     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24909     
24910 };
24911
24912 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24913     
24914     title: '',
24915     html: '',
24916     weight: false,
24917     faicon: false,
24918     
24919     getAutoCreate : function()
24920     {
24921         
24922         var cfg = {
24923             tag : 'div',
24924             cls : 'alert',
24925             cn : [
24926                 {
24927                     tag : 'i',
24928                     cls : 'roo-alert-icon'
24929                     
24930                 },
24931                 {
24932                     tag : 'b',
24933                     cls : 'roo-alert-title',
24934                     html : this.title
24935                 },
24936                 {
24937                     tag : 'span',
24938                     cls : 'roo-alert-text',
24939                     html : this.html
24940                 }
24941             ]
24942         };
24943         
24944         if(this.faicon){
24945             cfg.cn[0].cls += ' fa ' + this.faicon;
24946         }
24947         
24948         if(this.weight){
24949             cfg.cls += ' alert-' + this.weight;
24950         }
24951         
24952         return cfg;
24953     },
24954     
24955     initEvents: function() 
24956     {
24957         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24958     },
24959     
24960     setTitle : function(str)
24961     {
24962         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24963     },
24964     
24965     setText : function(str)
24966     {
24967         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24968     },
24969     
24970     setWeight : function(weight)
24971     {
24972         if(this.weight){
24973             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24974         }
24975         
24976         this.weight = weight;
24977         
24978         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24979     },
24980     
24981     setIcon : function(icon)
24982     {
24983         if(this.faicon){
24984             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24985         }
24986         
24987         this.faicon = icon;
24988         
24989         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24990     },
24991     
24992     hide: function() 
24993     {
24994         this.el.hide();   
24995     },
24996     
24997     show: function() 
24998     {  
24999         this.el.show();   
25000     }
25001     
25002 });
25003
25004  
25005 /*
25006 * Licence: LGPL
25007 */
25008
25009 /**
25010  * @class Roo.bootstrap.UploadCropbox
25011  * @extends Roo.bootstrap.Component
25012  * Bootstrap UploadCropbox class
25013  * @cfg {String} emptyText show when image has been loaded
25014  * @cfg {String} rotateNotify show when image too small to rotate
25015  * @cfg {Number} errorTimeout default 3000
25016  * @cfg {Number} minWidth default 300
25017  * @cfg {Number} minHeight default 300
25018  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25019  * @cfg {Boolean} isDocument (true|false) default false
25020  * @cfg {String} url action url
25021  * @cfg {String} paramName default 'imageUpload'
25022  * @cfg {String} method default POST
25023  * @cfg {Boolean} loadMask (true|false) default true
25024  * @cfg {Boolean} loadingText default 'Loading...'
25025  * 
25026  * @constructor
25027  * Create a new UploadCropbox
25028  * @param {Object} config The config object
25029  */
25030
25031 Roo.bootstrap.UploadCropbox = function(config){
25032     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25033     
25034     this.addEvents({
25035         /**
25036          * @event beforeselectfile
25037          * Fire before select file
25038          * @param {Roo.bootstrap.UploadCropbox} this
25039          */
25040         "beforeselectfile" : true,
25041         /**
25042          * @event initial
25043          * Fire after initEvent
25044          * @param {Roo.bootstrap.UploadCropbox} this
25045          */
25046         "initial" : true,
25047         /**
25048          * @event crop
25049          * Fire after initEvent
25050          * @param {Roo.bootstrap.UploadCropbox} this
25051          * @param {String} data
25052          */
25053         "crop" : true,
25054         /**
25055          * @event prepare
25056          * Fire when preparing the file data
25057          * @param {Roo.bootstrap.UploadCropbox} this
25058          * @param {Object} file
25059          */
25060         "prepare" : true,
25061         /**
25062          * @event exception
25063          * Fire when get exception
25064          * @param {Roo.bootstrap.UploadCropbox} this
25065          * @param {XMLHttpRequest} xhr
25066          */
25067         "exception" : true,
25068         /**
25069          * @event beforeloadcanvas
25070          * Fire before load the canvas
25071          * @param {Roo.bootstrap.UploadCropbox} this
25072          * @param {String} src
25073          */
25074         "beforeloadcanvas" : true,
25075         /**
25076          * @event trash
25077          * Fire when trash image
25078          * @param {Roo.bootstrap.UploadCropbox} this
25079          */
25080         "trash" : true,
25081         /**
25082          * @event download
25083          * Fire when download the image
25084          * @param {Roo.bootstrap.UploadCropbox} this
25085          */
25086         "download" : true,
25087         /**
25088          * @event footerbuttonclick
25089          * Fire when footerbuttonclick
25090          * @param {Roo.bootstrap.UploadCropbox} this
25091          * @param {String} type
25092          */
25093         "footerbuttonclick" : true,
25094         /**
25095          * @event resize
25096          * Fire when resize
25097          * @param {Roo.bootstrap.UploadCropbox} this
25098          */
25099         "resize" : true,
25100         /**
25101          * @event rotate
25102          * Fire when rotate the image
25103          * @param {Roo.bootstrap.UploadCropbox} this
25104          * @param {String} pos
25105          */
25106         "rotate" : true,
25107         /**
25108          * @event inspect
25109          * Fire when inspect the file
25110          * @param {Roo.bootstrap.UploadCropbox} this
25111          * @param {Object} file
25112          */
25113         "inspect" : true,
25114         /**
25115          * @event upload
25116          * Fire when xhr upload the file
25117          * @param {Roo.bootstrap.UploadCropbox} this
25118          * @param {Object} data
25119          */
25120         "upload" : true,
25121         /**
25122          * @event arrange
25123          * Fire when arrange the file data
25124          * @param {Roo.bootstrap.UploadCropbox} this
25125          * @param {Object} formData
25126          */
25127         "arrange" : true
25128     });
25129     
25130     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25131 };
25132
25133 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25134     
25135     emptyText : 'Click to upload image',
25136     rotateNotify : 'Image is too small to rotate',
25137     errorTimeout : 3000,
25138     scale : 0,
25139     baseScale : 1,
25140     rotate : 0,
25141     dragable : false,
25142     pinching : false,
25143     mouseX : 0,
25144     mouseY : 0,
25145     cropData : false,
25146     minWidth : 300,
25147     minHeight : 300,
25148     file : false,
25149     exif : {},
25150     baseRotate : 1,
25151     cropType : 'image/jpeg',
25152     buttons : false,
25153     canvasLoaded : false,
25154     isDocument : false,
25155     method : 'POST',
25156     paramName : 'imageUpload',
25157     loadMask : true,
25158     loadingText : 'Loading...',
25159     maskEl : false,
25160     
25161     getAutoCreate : function()
25162     {
25163         var cfg = {
25164             tag : 'div',
25165             cls : 'roo-upload-cropbox',
25166             cn : [
25167                 {
25168                     tag : 'input',
25169                     cls : 'roo-upload-cropbox-selector',
25170                     type : 'file'
25171                 },
25172                 {
25173                     tag : 'div',
25174                     cls : 'roo-upload-cropbox-body',
25175                     style : 'cursor:pointer',
25176                     cn : [
25177                         {
25178                             tag : 'div',
25179                             cls : 'roo-upload-cropbox-preview'
25180                         },
25181                         {
25182                             tag : 'div',
25183                             cls : 'roo-upload-cropbox-thumb'
25184                         },
25185                         {
25186                             tag : 'div',
25187                             cls : 'roo-upload-cropbox-empty-notify',
25188                             html : this.emptyText
25189                         },
25190                         {
25191                             tag : 'div',
25192                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25193                             html : this.rotateNotify
25194                         }
25195                     ]
25196                 },
25197                 {
25198                     tag : 'div',
25199                     cls : 'roo-upload-cropbox-footer',
25200                     cn : {
25201                         tag : 'div',
25202                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25203                         cn : []
25204                     }
25205                 }
25206             ]
25207         };
25208         
25209         return cfg;
25210     },
25211     
25212     onRender : function(ct, position)
25213     {
25214         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25215         
25216         if (this.buttons.length) {
25217             
25218             Roo.each(this.buttons, function(bb) {
25219                 
25220                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25221                 
25222                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25223                 
25224             }, this);
25225         }
25226         
25227         if(this.loadMask){
25228             this.maskEl = this.el;
25229         }
25230     },
25231     
25232     initEvents : function()
25233     {
25234         this.urlAPI = (window.createObjectURL && window) || 
25235                                 (window.URL && URL.revokeObjectURL && URL) || 
25236                                 (window.webkitURL && webkitURL);
25237                         
25238         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25239         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25240         
25241         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25242         this.selectorEl.hide();
25243         
25244         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25245         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25246         
25247         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25248         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25249         this.thumbEl.hide();
25250         
25251         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25252         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25253         
25254         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25255         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25256         this.errorEl.hide();
25257         
25258         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25259         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25260         this.footerEl.hide();
25261         
25262         this.setThumbBoxSize();
25263         
25264         this.bind();
25265         
25266         this.resize();
25267         
25268         this.fireEvent('initial', this);
25269     },
25270
25271     bind : function()
25272     {
25273         var _this = this;
25274         
25275         window.addEventListener("resize", function() { _this.resize(); } );
25276         
25277         this.bodyEl.on('click', this.beforeSelectFile, this);
25278         
25279         if(Roo.isTouch){
25280             this.bodyEl.on('touchstart', this.onTouchStart, this);
25281             this.bodyEl.on('touchmove', this.onTouchMove, this);
25282             this.bodyEl.on('touchend', this.onTouchEnd, this);
25283         }
25284         
25285         if(!Roo.isTouch){
25286             this.bodyEl.on('mousedown', this.onMouseDown, this);
25287             this.bodyEl.on('mousemove', this.onMouseMove, this);
25288             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25289             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25290             Roo.get(document).on('mouseup', this.onMouseUp, this);
25291         }
25292         
25293         this.selectorEl.on('change', this.onFileSelected, this);
25294     },
25295     
25296     reset : function()
25297     {    
25298         this.scale = 0;
25299         this.baseScale = 1;
25300         this.rotate = 0;
25301         this.baseRotate = 1;
25302         this.dragable = false;
25303         this.pinching = false;
25304         this.mouseX = 0;
25305         this.mouseY = 0;
25306         this.cropData = false;
25307         this.notifyEl.dom.innerHTML = this.emptyText;
25308         
25309         this.selectorEl.dom.value = '';
25310         
25311     },
25312     
25313     resize : function()
25314     {
25315         if(this.fireEvent('resize', this) != false){
25316             this.setThumbBoxPosition();
25317             this.setCanvasPosition();
25318         }
25319     },
25320     
25321     onFooterButtonClick : function(e, el, o, type)
25322     {
25323         switch (type) {
25324             case 'rotate-left' :
25325                 this.onRotateLeft(e);
25326                 break;
25327             case 'rotate-right' :
25328                 this.onRotateRight(e);
25329                 break;
25330             case 'picture' :
25331                 this.beforeSelectFile(e);
25332                 break;
25333             case 'trash' :
25334                 this.trash(e);
25335                 break;
25336             case 'crop' :
25337                 this.crop(e);
25338                 break;
25339             case 'download' :
25340                 this.download(e);
25341                 break;
25342             default :
25343                 break;
25344         }
25345         
25346         this.fireEvent('footerbuttonclick', this, type);
25347     },
25348     
25349     beforeSelectFile : function(e)
25350     {
25351         e.preventDefault();
25352         
25353         if(this.fireEvent('beforeselectfile', this) != false){
25354             this.selectorEl.dom.click();
25355         }
25356     },
25357     
25358     onFileSelected : function(e)
25359     {
25360         e.preventDefault();
25361         
25362         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25363             return;
25364         }
25365         
25366         var file = this.selectorEl.dom.files[0];
25367         
25368         if(this.fireEvent('inspect', this, file) != false){
25369             this.prepare(file);
25370         }
25371         
25372     },
25373     
25374     trash : function(e)
25375     {
25376         this.fireEvent('trash', this);
25377     },
25378     
25379     download : function(e)
25380     {
25381         this.fireEvent('download', this);
25382     },
25383     
25384     loadCanvas : function(src)
25385     {   
25386         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25387             
25388             this.reset();
25389             
25390             this.imageEl = document.createElement('img');
25391             
25392             var _this = this;
25393             
25394             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25395             
25396             this.imageEl.src = src;
25397         }
25398     },
25399     
25400     onLoadCanvas : function()
25401     {   
25402         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25403         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25404         
25405         this.bodyEl.un('click', this.beforeSelectFile, this);
25406         
25407         this.notifyEl.hide();
25408         this.thumbEl.show();
25409         this.footerEl.show();
25410         
25411         this.baseRotateLevel();
25412         
25413         if(this.isDocument){
25414             this.setThumbBoxSize();
25415         }
25416         
25417         this.setThumbBoxPosition();
25418         
25419         this.baseScaleLevel();
25420         
25421         this.draw();
25422         
25423         this.resize();
25424         
25425         this.canvasLoaded = true;
25426         
25427         if(this.loadMask){
25428             this.maskEl.unmask();
25429         }
25430         
25431     },
25432     
25433     setCanvasPosition : function()
25434     {   
25435         if(!this.canvasEl){
25436             return;
25437         }
25438         
25439         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25440         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25441         
25442         this.previewEl.setLeft(pw);
25443         this.previewEl.setTop(ph);
25444         
25445     },
25446     
25447     onMouseDown : function(e)
25448     {   
25449         e.stopEvent();
25450         
25451         this.dragable = true;
25452         this.pinching = false;
25453         
25454         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25455             this.dragable = false;
25456             return;
25457         }
25458         
25459         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25460         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25461         
25462     },
25463     
25464     onMouseMove : function(e)
25465     {   
25466         e.stopEvent();
25467         
25468         if(!this.canvasLoaded){
25469             return;
25470         }
25471         
25472         if (!this.dragable){
25473             return;
25474         }
25475         
25476         var minX = Math.ceil(this.thumbEl.getLeft(true));
25477         var minY = Math.ceil(this.thumbEl.getTop(true));
25478         
25479         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25480         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25481         
25482         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25483         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25484         
25485         x = x - this.mouseX;
25486         y = y - this.mouseY;
25487         
25488         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25489         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25490         
25491         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25492         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25493         
25494         this.previewEl.setLeft(bgX);
25495         this.previewEl.setTop(bgY);
25496         
25497         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25498         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25499     },
25500     
25501     onMouseUp : function(e)
25502     {   
25503         e.stopEvent();
25504         
25505         this.dragable = false;
25506     },
25507     
25508     onMouseWheel : function(e)
25509     {   
25510         e.stopEvent();
25511         
25512         this.startScale = this.scale;
25513         
25514         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25515         
25516         if(!this.zoomable()){
25517             this.scale = this.startScale;
25518             return;
25519         }
25520         
25521         this.draw();
25522         
25523         return;
25524     },
25525     
25526     zoomable : function()
25527     {
25528         var minScale = this.thumbEl.getWidth() / this.minWidth;
25529         
25530         if(this.minWidth < this.minHeight){
25531             minScale = this.thumbEl.getHeight() / this.minHeight;
25532         }
25533         
25534         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25535         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25536         
25537         if(
25538                 this.isDocument &&
25539                 (this.rotate == 0 || this.rotate == 180) && 
25540                 (
25541                     width > this.imageEl.OriginWidth || 
25542                     height > this.imageEl.OriginHeight ||
25543                     (width < this.minWidth && height < this.minHeight)
25544                 )
25545         ){
25546             return false;
25547         }
25548         
25549         if(
25550                 this.isDocument &&
25551                 (this.rotate == 90 || this.rotate == 270) && 
25552                 (
25553                     width > this.imageEl.OriginWidth || 
25554                     height > this.imageEl.OriginHeight ||
25555                     (width < this.minHeight && height < this.minWidth)
25556                 )
25557         ){
25558             return false;
25559         }
25560         
25561         if(
25562                 !this.isDocument &&
25563                 (this.rotate == 0 || this.rotate == 180) && 
25564                 (
25565                     width < this.minWidth || 
25566                     width > this.imageEl.OriginWidth || 
25567                     height < this.minHeight || 
25568                     height > this.imageEl.OriginHeight
25569                 )
25570         ){
25571             return false;
25572         }
25573         
25574         if(
25575                 !this.isDocument &&
25576                 (this.rotate == 90 || this.rotate == 270) && 
25577                 (
25578                     width < this.minHeight || 
25579                     width > this.imageEl.OriginWidth || 
25580                     height < this.minWidth || 
25581                     height > this.imageEl.OriginHeight
25582                 )
25583         ){
25584             return false;
25585         }
25586         
25587         return true;
25588         
25589     },
25590     
25591     onRotateLeft : function(e)
25592     {   
25593         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25594             
25595             var minScale = this.thumbEl.getWidth() / this.minWidth;
25596             
25597             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25598             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25599             
25600             this.startScale = this.scale;
25601             
25602             while (this.getScaleLevel() < minScale){
25603             
25604                 this.scale = this.scale + 1;
25605                 
25606                 if(!this.zoomable()){
25607                     break;
25608                 }
25609                 
25610                 if(
25611                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25612                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25613                 ){
25614                     continue;
25615                 }
25616                 
25617                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25618
25619                 this.draw();
25620                 
25621                 return;
25622             }
25623             
25624             this.scale = this.startScale;
25625             
25626             this.onRotateFail();
25627             
25628             return false;
25629         }
25630         
25631         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25632
25633         if(this.isDocument){
25634             this.setThumbBoxSize();
25635             this.setThumbBoxPosition();
25636             this.setCanvasPosition();
25637         }
25638         
25639         this.draw();
25640         
25641         this.fireEvent('rotate', this, 'left');
25642         
25643     },
25644     
25645     onRotateRight : function(e)
25646     {
25647         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25648             
25649             var minScale = this.thumbEl.getWidth() / this.minWidth;
25650         
25651             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25652             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25653             
25654             this.startScale = this.scale;
25655             
25656             while (this.getScaleLevel() < minScale){
25657             
25658                 this.scale = this.scale + 1;
25659                 
25660                 if(!this.zoomable()){
25661                     break;
25662                 }
25663                 
25664                 if(
25665                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25666                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25667                 ){
25668                     continue;
25669                 }
25670                 
25671                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25672
25673                 this.draw();
25674                 
25675                 return;
25676             }
25677             
25678             this.scale = this.startScale;
25679             
25680             this.onRotateFail();
25681             
25682             return false;
25683         }
25684         
25685         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25686
25687         if(this.isDocument){
25688             this.setThumbBoxSize();
25689             this.setThumbBoxPosition();
25690             this.setCanvasPosition();
25691         }
25692         
25693         this.draw();
25694         
25695         this.fireEvent('rotate', this, 'right');
25696     },
25697     
25698     onRotateFail : function()
25699     {
25700         this.errorEl.show(true);
25701         
25702         var _this = this;
25703         
25704         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25705     },
25706     
25707     draw : function()
25708     {
25709         this.previewEl.dom.innerHTML = '';
25710         
25711         var canvasEl = document.createElement("canvas");
25712         
25713         var contextEl = canvasEl.getContext("2d");
25714         
25715         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25716         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25717         var center = this.imageEl.OriginWidth / 2;
25718         
25719         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25720             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25721             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25722             center = this.imageEl.OriginHeight / 2;
25723         }
25724         
25725         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25726         
25727         contextEl.translate(center, center);
25728         contextEl.rotate(this.rotate * Math.PI / 180);
25729
25730         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25731         
25732         this.canvasEl = document.createElement("canvas");
25733         
25734         this.contextEl = this.canvasEl.getContext("2d");
25735         
25736         switch (this.rotate) {
25737             case 0 :
25738                 
25739                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25740                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25741                 
25742                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25743                 
25744                 break;
25745             case 90 : 
25746                 
25747                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25748                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25749                 
25750                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25751                     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);
25752                     break;
25753                 }
25754                 
25755                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25756                 
25757                 break;
25758             case 180 :
25759                 
25760                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25761                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25762                 
25763                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25764                     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);
25765                     break;
25766                 }
25767                 
25768                 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);
25769                 
25770                 break;
25771             case 270 :
25772                 
25773                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25774                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25775         
25776                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25777                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25778                     break;
25779                 }
25780                 
25781                 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);
25782                 
25783                 break;
25784             default : 
25785                 break;
25786         }
25787         
25788         this.previewEl.appendChild(this.canvasEl);
25789         
25790         this.setCanvasPosition();
25791     },
25792     
25793     crop : function()
25794     {
25795         if(!this.canvasLoaded){
25796             return;
25797         }
25798         
25799         var imageCanvas = document.createElement("canvas");
25800         
25801         var imageContext = imageCanvas.getContext("2d");
25802         
25803         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25804         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25805         
25806         var center = imageCanvas.width / 2;
25807         
25808         imageContext.translate(center, center);
25809         
25810         imageContext.rotate(this.rotate * Math.PI / 180);
25811         
25812         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25813         
25814         var canvas = document.createElement("canvas");
25815         
25816         var context = canvas.getContext("2d");
25817                 
25818         canvas.width = this.minWidth;
25819         canvas.height = this.minHeight;
25820
25821         switch (this.rotate) {
25822             case 0 :
25823                 
25824                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25825                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25826                 
25827                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25828                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25829                 
25830                 var targetWidth = this.minWidth - 2 * x;
25831                 var targetHeight = this.minHeight - 2 * y;
25832                 
25833                 var scale = 1;
25834                 
25835                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25836                     scale = targetWidth / width;
25837                 }
25838                 
25839                 if(x > 0 && y == 0){
25840                     scale = targetHeight / height;
25841                 }
25842                 
25843                 if(x > 0 && y > 0){
25844                     scale = targetWidth / width;
25845                     
25846                     if(width < height){
25847                         scale = targetHeight / height;
25848                     }
25849                 }
25850                 
25851                 context.scale(scale, scale);
25852                 
25853                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25854                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25855
25856                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25857                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25858
25859                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25860                 
25861                 break;
25862             case 90 : 
25863                 
25864                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25865                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25866                 
25867                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25868                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25869                 
25870                 var targetWidth = this.minWidth - 2 * x;
25871                 var targetHeight = this.minHeight - 2 * y;
25872                 
25873                 var scale = 1;
25874                 
25875                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25876                     scale = targetWidth / width;
25877                 }
25878                 
25879                 if(x > 0 && y == 0){
25880                     scale = targetHeight / height;
25881                 }
25882                 
25883                 if(x > 0 && y > 0){
25884                     scale = targetWidth / width;
25885                     
25886                     if(width < height){
25887                         scale = targetHeight / height;
25888                     }
25889                 }
25890                 
25891                 context.scale(scale, scale);
25892                 
25893                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25894                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25895
25896                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25897                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25898                 
25899                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25900                 
25901                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25902                 
25903                 break;
25904             case 180 :
25905                 
25906                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25907                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25908                 
25909                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25910                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25911                 
25912                 var targetWidth = this.minWidth - 2 * x;
25913                 var targetHeight = this.minHeight - 2 * y;
25914                 
25915                 var scale = 1;
25916                 
25917                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25918                     scale = targetWidth / width;
25919                 }
25920                 
25921                 if(x > 0 && y == 0){
25922                     scale = targetHeight / height;
25923                 }
25924                 
25925                 if(x > 0 && y > 0){
25926                     scale = targetWidth / width;
25927                     
25928                     if(width < height){
25929                         scale = targetHeight / height;
25930                     }
25931                 }
25932                 
25933                 context.scale(scale, scale);
25934                 
25935                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25936                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25937
25938                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25939                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25940
25941                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25942                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25943                 
25944                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25945                 
25946                 break;
25947             case 270 :
25948                 
25949                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25950                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25951                 
25952                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25953                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25954                 
25955                 var targetWidth = this.minWidth - 2 * x;
25956                 var targetHeight = this.minHeight - 2 * y;
25957                 
25958                 var scale = 1;
25959                 
25960                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25961                     scale = targetWidth / width;
25962                 }
25963                 
25964                 if(x > 0 && y == 0){
25965                     scale = targetHeight / height;
25966                 }
25967                 
25968                 if(x > 0 && y > 0){
25969                     scale = targetWidth / width;
25970                     
25971                     if(width < height){
25972                         scale = targetHeight / height;
25973                     }
25974                 }
25975                 
25976                 context.scale(scale, scale);
25977                 
25978                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25979                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25980
25981                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25982                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25983                 
25984                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25985                 
25986                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25987                 
25988                 break;
25989             default : 
25990                 break;
25991         }
25992         
25993         this.cropData = canvas.toDataURL(this.cropType);
25994         
25995         if(this.fireEvent('crop', this, this.cropData) !== false){
25996             this.process(this.file, this.cropData);
25997         }
25998         
25999         return;
26000         
26001     },
26002     
26003     setThumbBoxSize : function()
26004     {
26005         var width, height;
26006         
26007         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26008             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26009             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26010             
26011             this.minWidth = width;
26012             this.minHeight = height;
26013             
26014             if(this.rotate == 90 || this.rotate == 270){
26015                 this.minWidth = height;
26016                 this.minHeight = width;
26017             }
26018         }
26019         
26020         height = 300;
26021         width = Math.ceil(this.minWidth * height / this.minHeight);
26022         
26023         if(this.minWidth > this.minHeight){
26024             width = 300;
26025             height = Math.ceil(this.minHeight * width / this.minWidth);
26026         }
26027         
26028         this.thumbEl.setStyle({
26029             width : width + 'px',
26030             height : height + 'px'
26031         });
26032
26033         return;
26034             
26035     },
26036     
26037     setThumbBoxPosition : function()
26038     {
26039         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26040         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26041         
26042         this.thumbEl.setLeft(x);
26043         this.thumbEl.setTop(y);
26044         
26045     },
26046     
26047     baseRotateLevel : function()
26048     {
26049         this.baseRotate = 1;
26050         
26051         if(
26052                 typeof(this.exif) != 'undefined' &&
26053                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26054                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26055         ){
26056             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26057         }
26058         
26059         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26060         
26061     },
26062     
26063     baseScaleLevel : function()
26064     {
26065         var width, height;
26066         
26067         if(this.isDocument){
26068             
26069             if(this.baseRotate == 6 || this.baseRotate == 8){
26070             
26071                 height = this.thumbEl.getHeight();
26072                 this.baseScale = height / this.imageEl.OriginWidth;
26073
26074                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26075                     width = this.thumbEl.getWidth();
26076                     this.baseScale = width / this.imageEl.OriginHeight;
26077                 }
26078
26079                 return;
26080             }
26081
26082             height = this.thumbEl.getHeight();
26083             this.baseScale = height / this.imageEl.OriginHeight;
26084
26085             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26086                 width = this.thumbEl.getWidth();
26087                 this.baseScale = width / this.imageEl.OriginWidth;
26088             }
26089
26090             return;
26091         }
26092         
26093         if(this.baseRotate == 6 || this.baseRotate == 8){
26094             
26095             width = this.thumbEl.getHeight();
26096             this.baseScale = width / this.imageEl.OriginHeight;
26097             
26098             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26099                 height = this.thumbEl.getWidth();
26100                 this.baseScale = height / this.imageEl.OriginHeight;
26101             }
26102             
26103             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26104                 height = this.thumbEl.getWidth();
26105                 this.baseScale = height / this.imageEl.OriginHeight;
26106                 
26107                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26108                     width = this.thumbEl.getHeight();
26109                     this.baseScale = width / this.imageEl.OriginWidth;
26110                 }
26111             }
26112             
26113             return;
26114         }
26115         
26116         width = this.thumbEl.getWidth();
26117         this.baseScale = width / this.imageEl.OriginWidth;
26118         
26119         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26120             height = this.thumbEl.getHeight();
26121             this.baseScale = height / this.imageEl.OriginHeight;
26122         }
26123         
26124         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26125             
26126             height = this.thumbEl.getHeight();
26127             this.baseScale = height / this.imageEl.OriginHeight;
26128             
26129             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26130                 width = this.thumbEl.getWidth();
26131                 this.baseScale = width / this.imageEl.OriginWidth;
26132             }
26133             
26134         }
26135         
26136         return;
26137     },
26138     
26139     getScaleLevel : function()
26140     {
26141         return this.baseScale * Math.pow(1.1, this.scale);
26142     },
26143     
26144     onTouchStart : function(e)
26145     {
26146         if(!this.canvasLoaded){
26147             this.beforeSelectFile(e);
26148             return;
26149         }
26150         
26151         var touches = e.browserEvent.touches;
26152         
26153         if(!touches){
26154             return;
26155         }
26156         
26157         if(touches.length == 1){
26158             this.onMouseDown(e);
26159             return;
26160         }
26161         
26162         if(touches.length != 2){
26163             return;
26164         }
26165         
26166         var coords = [];
26167         
26168         for(var i = 0, finger; finger = touches[i]; i++){
26169             coords.push(finger.pageX, finger.pageY);
26170         }
26171         
26172         var x = Math.pow(coords[0] - coords[2], 2);
26173         var y = Math.pow(coords[1] - coords[3], 2);
26174         
26175         this.startDistance = Math.sqrt(x + y);
26176         
26177         this.startScale = this.scale;
26178         
26179         this.pinching = true;
26180         this.dragable = false;
26181         
26182     },
26183     
26184     onTouchMove : function(e)
26185     {
26186         if(!this.pinching && !this.dragable){
26187             return;
26188         }
26189         
26190         var touches = e.browserEvent.touches;
26191         
26192         if(!touches){
26193             return;
26194         }
26195         
26196         if(this.dragable){
26197             this.onMouseMove(e);
26198             return;
26199         }
26200         
26201         var coords = [];
26202         
26203         for(var i = 0, finger; finger = touches[i]; i++){
26204             coords.push(finger.pageX, finger.pageY);
26205         }
26206         
26207         var x = Math.pow(coords[0] - coords[2], 2);
26208         var y = Math.pow(coords[1] - coords[3], 2);
26209         
26210         this.endDistance = Math.sqrt(x + y);
26211         
26212         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26213         
26214         if(!this.zoomable()){
26215             this.scale = this.startScale;
26216             return;
26217         }
26218         
26219         this.draw();
26220         
26221     },
26222     
26223     onTouchEnd : function(e)
26224     {
26225         this.pinching = false;
26226         this.dragable = false;
26227         
26228     },
26229     
26230     process : function(file, crop)
26231     {
26232         if(this.loadMask){
26233             this.maskEl.mask(this.loadingText);
26234         }
26235         
26236         this.xhr = new XMLHttpRequest();
26237         
26238         file.xhr = this.xhr;
26239
26240         this.xhr.open(this.method, this.url, true);
26241         
26242         var headers = {
26243             "Accept": "application/json",
26244             "Cache-Control": "no-cache",
26245             "X-Requested-With": "XMLHttpRequest"
26246         };
26247         
26248         for (var headerName in headers) {
26249             var headerValue = headers[headerName];
26250             if (headerValue) {
26251                 this.xhr.setRequestHeader(headerName, headerValue);
26252             }
26253         }
26254         
26255         var _this = this;
26256         
26257         this.xhr.onload = function()
26258         {
26259             _this.xhrOnLoad(_this.xhr);
26260         }
26261         
26262         this.xhr.onerror = function()
26263         {
26264             _this.xhrOnError(_this.xhr);
26265         }
26266         
26267         var formData = new FormData();
26268
26269         formData.append('returnHTML', 'NO');
26270         
26271         if(crop){
26272             formData.append('crop', crop);
26273         }
26274         
26275         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26276             formData.append(this.paramName, file, file.name);
26277         }
26278         
26279         if(typeof(file.filename) != 'undefined'){
26280             formData.append('filename', file.filename);
26281         }
26282         
26283         if(typeof(file.mimetype) != 'undefined'){
26284             formData.append('mimetype', file.mimetype);
26285         }
26286         
26287         if(this.fireEvent('arrange', this, formData) != false){
26288             this.xhr.send(formData);
26289         };
26290     },
26291     
26292     xhrOnLoad : function(xhr)
26293     {
26294         if(this.loadMask){
26295             this.maskEl.unmask();
26296         }
26297         
26298         if (xhr.readyState !== 4) {
26299             this.fireEvent('exception', this, xhr);
26300             return;
26301         }
26302
26303         var response = Roo.decode(xhr.responseText);
26304         
26305         if(!response.success){
26306             this.fireEvent('exception', this, xhr);
26307             return;
26308         }
26309         
26310         var response = Roo.decode(xhr.responseText);
26311         
26312         this.fireEvent('upload', this, response);
26313         
26314     },
26315     
26316     xhrOnError : function()
26317     {
26318         if(this.loadMask){
26319             this.maskEl.unmask();
26320         }
26321         
26322         Roo.log('xhr on error');
26323         
26324         var response = Roo.decode(xhr.responseText);
26325           
26326         Roo.log(response);
26327         
26328     },
26329     
26330     prepare : function(file)
26331     {   
26332         if(this.loadMask){
26333             this.maskEl.mask(this.loadingText);
26334         }
26335         
26336         this.file = false;
26337         this.exif = {};
26338         
26339         if(typeof(file) === 'string'){
26340             this.loadCanvas(file);
26341             return;
26342         }
26343         
26344         if(!file || !this.urlAPI){
26345             return;
26346         }
26347         
26348         this.file = file;
26349         this.cropType = file.type;
26350         
26351         var _this = this;
26352         
26353         if(this.fireEvent('prepare', this, this.file) != false){
26354             
26355             var reader = new FileReader();
26356             
26357             reader.onload = function (e) {
26358                 if (e.target.error) {
26359                     Roo.log(e.target.error);
26360                     return;
26361                 }
26362                 
26363                 var buffer = e.target.result,
26364                     dataView = new DataView(buffer),
26365                     offset = 2,
26366                     maxOffset = dataView.byteLength - 4,
26367                     markerBytes,
26368                     markerLength;
26369                 
26370                 if (dataView.getUint16(0) === 0xffd8) {
26371                     while (offset < maxOffset) {
26372                         markerBytes = dataView.getUint16(offset);
26373                         
26374                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26375                             markerLength = dataView.getUint16(offset + 2) + 2;
26376                             if (offset + markerLength > dataView.byteLength) {
26377                                 Roo.log('Invalid meta data: Invalid segment size.');
26378                                 break;
26379                             }
26380                             
26381                             if(markerBytes == 0xffe1){
26382                                 _this.parseExifData(
26383                                     dataView,
26384                                     offset,
26385                                     markerLength
26386                                 );
26387                             }
26388                             
26389                             offset += markerLength;
26390                             
26391                             continue;
26392                         }
26393                         
26394                         break;
26395                     }
26396                     
26397                 }
26398                 
26399                 var url = _this.urlAPI.createObjectURL(_this.file);
26400                 
26401                 _this.loadCanvas(url);
26402                 
26403                 return;
26404             }
26405             
26406             reader.readAsArrayBuffer(this.file);
26407             
26408         }
26409         
26410     },
26411     
26412     parseExifData : function(dataView, offset, length)
26413     {
26414         var tiffOffset = offset + 10,
26415             littleEndian,
26416             dirOffset;
26417     
26418         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26419             // No Exif data, might be XMP data instead
26420             return;
26421         }
26422         
26423         // Check for the ASCII code for "Exif" (0x45786966):
26424         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26425             // No Exif data, might be XMP data instead
26426             return;
26427         }
26428         if (tiffOffset + 8 > dataView.byteLength) {
26429             Roo.log('Invalid Exif data: Invalid segment size.');
26430             return;
26431         }
26432         // Check for the two null bytes:
26433         if (dataView.getUint16(offset + 8) !== 0x0000) {
26434             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26435             return;
26436         }
26437         // Check the byte alignment:
26438         switch (dataView.getUint16(tiffOffset)) {
26439         case 0x4949:
26440             littleEndian = true;
26441             break;
26442         case 0x4D4D:
26443             littleEndian = false;
26444             break;
26445         default:
26446             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26447             return;
26448         }
26449         // Check for the TIFF tag marker (0x002A):
26450         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26451             Roo.log('Invalid Exif data: Missing TIFF marker.');
26452             return;
26453         }
26454         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26455         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26456         
26457         this.parseExifTags(
26458             dataView,
26459             tiffOffset,
26460             tiffOffset + dirOffset,
26461             littleEndian
26462         );
26463     },
26464     
26465     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26466     {
26467         var tagsNumber,
26468             dirEndOffset,
26469             i;
26470         if (dirOffset + 6 > dataView.byteLength) {
26471             Roo.log('Invalid Exif data: Invalid directory offset.');
26472             return;
26473         }
26474         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26475         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26476         if (dirEndOffset + 4 > dataView.byteLength) {
26477             Roo.log('Invalid Exif data: Invalid directory size.');
26478             return;
26479         }
26480         for (i = 0; i < tagsNumber; i += 1) {
26481             this.parseExifTag(
26482                 dataView,
26483                 tiffOffset,
26484                 dirOffset + 2 + 12 * i, // tag offset
26485                 littleEndian
26486             );
26487         }
26488         // Return the offset to the next directory:
26489         return dataView.getUint32(dirEndOffset, littleEndian);
26490     },
26491     
26492     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26493     {
26494         var tag = dataView.getUint16(offset, littleEndian);
26495         
26496         this.exif[tag] = this.getExifValue(
26497             dataView,
26498             tiffOffset,
26499             offset,
26500             dataView.getUint16(offset + 2, littleEndian), // tag type
26501             dataView.getUint32(offset + 4, littleEndian), // tag length
26502             littleEndian
26503         );
26504     },
26505     
26506     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26507     {
26508         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26509             tagSize,
26510             dataOffset,
26511             values,
26512             i,
26513             str,
26514             c;
26515     
26516         if (!tagType) {
26517             Roo.log('Invalid Exif data: Invalid tag type.');
26518             return;
26519         }
26520         
26521         tagSize = tagType.size * length;
26522         // Determine if the value is contained in the dataOffset bytes,
26523         // or if the value at the dataOffset is a pointer to the actual data:
26524         dataOffset = tagSize > 4 ?
26525                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26526         if (dataOffset + tagSize > dataView.byteLength) {
26527             Roo.log('Invalid Exif data: Invalid data offset.');
26528             return;
26529         }
26530         if (length === 1) {
26531             return tagType.getValue(dataView, dataOffset, littleEndian);
26532         }
26533         values = [];
26534         for (i = 0; i < length; i += 1) {
26535             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26536         }
26537         
26538         if (tagType.ascii) {
26539             str = '';
26540             // Concatenate the chars:
26541             for (i = 0; i < values.length; i += 1) {
26542                 c = values[i];
26543                 // Ignore the terminating NULL byte(s):
26544                 if (c === '\u0000') {
26545                     break;
26546                 }
26547                 str += c;
26548             }
26549             return str;
26550         }
26551         return values;
26552     }
26553     
26554 });
26555
26556 Roo.apply(Roo.bootstrap.UploadCropbox, {
26557     tags : {
26558         'Orientation': 0x0112
26559     },
26560     
26561     Orientation: {
26562             1: 0, //'top-left',
26563 //            2: 'top-right',
26564             3: 180, //'bottom-right',
26565 //            4: 'bottom-left',
26566 //            5: 'left-top',
26567             6: 90, //'right-top',
26568 //            7: 'right-bottom',
26569             8: 270 //'left-bottom'
26570     },
26571     
26572     exifTagTypes : {
26573         // byte, 8-bit unsigned int:
26574         1: {
26575             getValue: function (dataView, dataOffset) {
26576                 return dataView.getUint8(dataOffset);
26577             },
26578             size: 1
26579         },
26580         // ascii, 8-bit byte:
26581         2: {
26582             getValue: function (dataView, dataOffset) {
26583                 return String.fromCharCode(dataView.getUint8(dataOffset));
26584             },
26585             size: 1,
26586             ascii: true
26587         },
26588         // short, 16 bit int:
26589         3: {
26590             getValue: function (dataView, dataOffset, littleEndian) {
26591                 return dataView.getUint16(dataOffset, littleEndian);
26592             },
26593             size: 2
26594         },
26595         // long, 32 bit int:
26596         4: {
26597             getValue: function (dataView, dataOffset, littleEndian) {
26598                 return dataView.getUint32(dataOffset, littleEndian);
26599             },
26600             size: 4
26601         },
26602         // rational = two long values, first is numerator, second is denominator:
26603         5: {
26604             getValue: function (dataView, dataOffset, littleEndian) {
26605                 return dataView.getUint32(dataOffset, littleEndian) /
26606                     dataView.getUint32(dataOffset + 4, littleEndian);
26607             },
26608             size: 8
26609         },
26610         // slong, 32 bit signed int:
26611         9: {
26612             getValue: function (dataView, dataOffset, littleEndian) {
26613                 return dataView.getInt32(dataOffset, littleEndian);
26614             },
26615             size: 4
26616         },
26617         // srational, two slongs, first is numerator, second is denominator:
26618         10: {
26619             getValue: function (dataView, dataOffset, littleEndian) {
26620                 return dataView.getInt32(dataOffset, littleEndian) /
26621                     dataView.getInt32(dataOffset + 4, littleEndian);
26622             },
26623             size: 8
26624         }
26625     },
26626     
26627     footer : {
26628         STANDARD : [
26629             {
26630                 tag : 'div',
26631                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26632                 action : 'rotate-left',
26633                 cn : [
26634                     {
26635                         tag : 'button',
26636                         cls : 'btn btn-default',
26637                         html : '<i class="fa fa-undo"></i>'
26638                     }
26639                 ]
26640             },
26641             {
26642                 tag : 'div',
26643                 cls : 'btn-group roo-upload-cropbox-picture',
26644                 action : 'picture',
26645                 cn : [
26646                     {
26647                         tag : 'button',
26648                         cls : 'btn btn-default',
26649                         html : '<i class="fa fa-picture-o"></i>'
26650                     }
26651                 ]
26652             },
26653             {
26654                 tag : 'div',
26655                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26656                 action : 'rotate-right',
26657                 cn : [
26658                     {
26659                         tag : 'button',
26660                         cls : 'btn btn-default',
26661                         html : '<i class="fa fa-repeat"></i>'
26662                     }
26663                 ]
26664             }
26665         ],
26666         DOCUMENT : [
26667             {
26668                 tag : 'div',
26669                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26670                 action : 'rotate-left',
26671                 cn : [
26672                     {
26673                         tag : 'button',
26674                         cls : 'btn btn-default',
26675                         html : '<i class="fa fa-undo"></i>'
26676                     }
26677                 ]
26678             },
26679             {
26680                 tag : 'div',
26681                 cls : 'btn-group roo-upload-cropbox-download',
26682                 action : 'download',
26683                 cn : [
26684                     {
26685                         tag : 'button',
26686                         cls : 'btn btn-default',
26687                         html : '<i class="fa fa-download"></i>'
26688                     }
26689                 ]
26690             },
26691             {
26692                 tag : 'div',
26693                 cls : 'btn-group roo-upload-cropbox-crop',
26694                 action : 'crop',
26695                 cn : [
26696                     {
26697                         tag : 'button',
26698                         cls : 'btn btn-default',
26699                         html : '<i class="fa fa-crop"></i>'
26700                     }
26701                 ]
26702             },
26703             {
26704                 tag : 'div',
26705                 cls : 'btn-group roo-upload-cropbox-trash',
26706                 action : 'trash',
26707                 cn : [
26708                     {
26709                         tag : 'button',
26710                         cls : 'btn btn-default',
26711                         html : '<i class="fa fa-trash"></i>'
26712                     }
26713                 ]
26714             },
26715             {
26716                 tag : 'div',
26717                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26718                 action : 'rotate-right',
26719                 cn : [
26720                     {
26721                         tag : 'button',
26722                         cls : 'btn btn-default',
26723                         html : '<i class="fa fa-repeat"></i>'
26724                     }
26725                 ]
26726             }
26727         ],
26728         ROTATOR : [
26729             {
26730                 tag : 'div',
26731                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26732                 action : 'rotate-left',
26733                 cn : [
26734                     {
26735                         tag : 'button',
26736                         cls : 'btn btn-default',
26737                         html : '<i class="fa fa-undo"></i>'
26738                     }
26739                 ]
26740             },
26741             {
26742                 tag : 'div',
26743                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26744                 action : 'rotate-right',
26745                 cn : [
26746                     {
26747                         tag : 'button',
26748                         cls : 'btn btn-default',
26749                         html : '<i class="fa fa-repeat"></i>'
26750                     }
26751                 ]
26752             }
26753         ]
26754     }
26755 });
26756
26757 /*
26758 * Licence: LGPL
26759 */
26760
26761 /**
26762  * @class Roo.bootstrap.DocumentManager
26763  * @extends Roo.bootstrap.Component
26764  * Bootstrap DocumentManager class
26765  * @cfg {String} paramName default 'imageUpload'
26766  * @cfg {String} method default POST
26767  * @cfg {String} url action url
26768  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26769  * @cfg {Boolean} multiple multiple upload default true
26770  * @cfg {Number} thumbSize default 300
26771  * @cfg {String} fieldLabel
26772  * @cfg {Number} labelWidth default 4
26773  * @cfg {String} labelAlign (left|top) default left
26774  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26775  * 
26776  * @constructor
26777  * Create a new DocumentManager
26778  * @param {Object} config The config object
26779  */
26780
26781 Roo.bootstrap.DocumentManager = function(config){
26782     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26783     
26784     this.addEvents({
26785         /**
26786          * @event initial
26787          * Fire when initial the DocumentManager
26788          * @param {Roo.bootstrap.DocumentManager} this
26789          */
26790         "initial" : true,
26791         /**
26792          * @event inspect
26793          * inspect selected file
26794          * @param {Roo.bootstrap.DocumentManager} this
26795          * @param {File} file
26796          */
26797         "inspect" : true,
26798         /**
26799          * @event exception
26800          * Fire when xhr load exception
26801          * @param {Roo.bootstrap.DocumentManager} this
26802          * @param {XMLHttpRequest} xhr
26803          */
26804         "exception" : true,
26805         /**
26806          * @event prepare
26807          * prepare the form data
26808          * @param {Roo.bootstrap.DocumentManager} this
26809          * @param {Object} formData
26810          */
26811         "prepare" : true,
26812         /**
26813          * @event remove
26814          * Fire when remove the file
26815          * @param {Roo.bootstrap.DocumentManager} this
26816          * @param {Object} file
26817          */
26818         "remove" : true,
26819         /**
26820          * @event refresh
26821          * Fire after refresh the file
26822          * @param {Roo.bootstrap.DocumentManager} this
26823          */
26824         "refresh" : true,
26825         /**
26826          * @event click
26827          * Fire after click the image
26828          * @param {Roo.bootstrap.DocumentManager} this
26829          * @param {Object} file
26830          */
26831         "click" : true,
26832         /**
26833          * @event edit
26834          * Fire when upload a image and editable set to true
26835          * @param {Roo.bootstrap.DocumentManager} this
26836          * @param {Object} file
26837          */
26838         "edit" : true,
26839         /**
26840          * @event beforeselectfile
26841          * Fire before select file
26842          * @param {Roo.bootstrap.DocumentManager} this
26843          */
26844         "beforeselectfile" : true,
26845         /**
26846          * @event process
26847          * Fire before process file
26848          * @param {Roo.bootstrap.DocumentManager} this
26849          * @param {Object} file
26850          */
26851         "process" : true
26852         
26853     });
26854 };
26855
26856 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26857     
26858     boxes : 0,
26859     inputName : '',
26860     thumbSize : 300,
26861     multiple : true,
26862     files : [],
26863     method : 'POST',
26864     url : '',
26865     paramName : 'imageUpload',
26866     fieldLabel : '',
26867     labelWidth : 4,
26868     labelAlign : 'left',
26869     editable : true,
26870     delegates : [],
26871     
26872     
26873     xhr : false, 
26874     
26875     getAutoCreate : function()
26876     {   
26877         var managerWidget = {
26878             tag : 'div',
26879             cls : 'roo-document-manager',
26880             cn : [
26881                 {
26882                     tag : 'input',
26883                     cls : 'roo-document-manager-selector',
26884                     type : 'file'
26885                 },
26886                 {
26887                     tag : 'div',
26888                     cls : 'roo-document-manager-uploader',
26889                     cn : [
26890                         {
26891                             tag : 'div',
26892                             cls : 'roo-document-manager-upload-btn',
26893                             html : '<i class="fa fa-plus"></i>'
26894                         }
26895                     ]
26896                     
26897                 }
26898             ]
26899         };
26900         
26901         var content = [
26902             {
26903                 tag : 'div',
26904                 cls : 'column col-md-12',
26905                 cn : managerWidget
26906             }
26907         ];
26908         
26909         if(this.fieldLabel.length){
26910             
26911             content = [
26912                 {
26913                     tag : 'div',
26914                     cls : 'column col-md-12',
26915                     html : this.fieldLabel
26916                 },
26917                 {
26918                     tag : 'div',
26919                     cls : 'column col-md-12',
26920                     cn : managerWidget
26921                 }
26922             ];
26923
26924             if(this.labelAlign == 'left'){
26925                 content = [
26926                     {
26927                         tag : 'div',
26928                         cls : 'column col-md-' + this.labelWidth,
26929                         html : this.fieldLabel
26930                     },
26931                     {
26932                         tag : 'div',
26933                         cls : 'column col-md-' + (12 - this.labelWidth),
26934                         cn : managerWidget
26935                     }
26936                 ];
26937                 
26938             }
26939         }
26940         
26941         var cfg = {
26942             tag : 'div',
26943             cls : 'row clearfix',
26944             cn : content
26945         };
26946         
26947         return cfg;
26948         
26949     },
26950     
26951     initEvents : function()
26952     {
26953         this.managerEl = this.el.select('.roo-document-manager', true).first();
26954         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26955         
26956         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26957         this.selectorEl.hide();
26958         
26959         if(this.multiple){
26960             this.selectorEl.attr('multiple', 'multiple');
26961         }
26962         
26963         this.selectorEl.on('change', this.onFileSelected, this);
26964         
26965         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26966         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26967         
26968         this.uploader.on('click', this.onUploaderClick, this);
26969         
26970         this.renderProgressDialog();
26971         
26972         var _this = this;
26973         
26974         window.addEventListener("resize", function() { _this.refresh(); } );
26975         
26976         this.fireEvent('initial', this);
26977     },
26978     
26979     renderProgressDialog : function()
26980     {
26981         var _this = this;
26982         
26983         this.progressDialog = new Roo.bootstrap.Modal({
26984             cls : 'roo-document-manager-progress-dialog',
26985             allow_close : false,
26986             title : '',
26987             buttons : [
26988                 {
26989                     name  :'cancel',
26990                     weight : 'danger',
26991                     html : 'Cancel'
26992                 }
26993             ], 
26994             listeners : { 
26995                 btnclick : function() {
26996                     _this.uploadCancel();
26997                     this.hide();
26998                 }
26999             }
27000         });
27001          
27002         this.progressDialog.render(Roo.get(document.body));
27003          
27004         this.progress = new Roo.bootstrap.Progress({
27005             cls : 'roo-document-manager-progress',
27006             active : true,
27007             striped : true
27008         });
27009         
27010         this.progress.render(this.progressDialog.getChildContainer());
27011         
27012         this.progressBar = new Roo.bootstrap.ProgressBar({
27013             cls : 'roo-document-manager-progress-bar',
27014             aria_valuenow : 0,
27015             aria_valuemin : 0,
27016             aria_valuemax : 12,
27017             panel : 'success'
27018         });
27019         
27020         this.progressBar.render(this.progress.getChildContainer());
27021     },
27022     
27023     onUploaderClick : function(e)
27024     {
27025         e.preventDefault();
27026      
27027         if(this.fireEvent('beforeselectfile', this) != false){
27028             this.selectorEl.dom.click();
27029         }
27030         
27031     },
27032     
27033     onFileSelected : function(e)
27034     {
27035         e.preventDefault();
27036         
27037         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27038             return;
27039         }
27040         
27041         Roo.each(this.selectorEl.dom.files, function(file){
27042             if(this.fireEvent('inspect', this, file) != false){
27043                 this.files.push(file);
27044             }
27045         }, this);
27046         
27047         this.queue();
27048         
27049     },
27050     
27051     queue : function()
27052     {
27053         this.selectorEl.dom.value = '';
27054         
27055         if(!this.files.length){
27056             return;
27057         }
27058         
27059         if(this.boxes > 0 && this.files.length > this.boxes){
27060             this.files = this.files.slice(0, this.boxes);
27061         }
27062         
27063         this.uploader.show();
27064         
27065         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27066             this.uploader.hide();
27067         }
27068         
27069         var _this = this;
27070         
27071         var files = [];
27072         
27073         var docs = [];
27074         
27075         Roo.each(this.files, function(file){
27076             
27077             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27078                 var f = this.renderPreview(file);
27079                 files.push(f);
27080                 return;
27081             }
27082             
27083             if(file.type.indexOf('image') != -1){
27084                 this.delegates.push(
27085                     (function(){
27086                         _this.process(file);
27087                     }).createDelegate(this)
27088                 );
27089         
27090                 return;
27091             }
27092             
27093             docs.push(
27094                 (function(){
27095                     _this.process(file);
27096                 }).createDelegate(this)
27097             );
27098             
27099         }, this);
27100         
27101         this.files = files;
27102         
27103         this.delegates = this.delegates.concat(docs);
27104         
27105         if(!this.delegates.length){
27106             this.refresh();
27107             return;
27108         }
27109         
27110         this.progressBar.aria_valuemax = this.delegates.length;
27111         
27112         this.arrange();
27113         
27114         return;
27115     },
27116     
27117     arrange : function()
27118     {
27119         if(!this.delegates.length){
27120             this.progressDialog.hide();
27121             this.refresh();
27122             return;
27123         }
27124         
27125         var delegate = this.delegates.shift();
27126         
27127         this.progressDialog.show();
27128         
27129         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27130         
27131         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27132         
27133         delegate();
27134     },
27135     
27136     refresh : function()
27137     {
27138         this.uploader.show();
27139         
27140         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27141             this.uploader.hide();
27142         }
27143         
27144         Roo.isTouch ? this.closable(false) : this.closable(true);
27145         
27146         this.fireEvent('refresh', this);
27147     },
27148     
27149     onRemove : function(e, el, o)
27150     {
27151         e.preventDefault();
27152         
27153         this.fireEvent('remove', this, o);
27154         
27155     },
27156     
27157     remove : function(o)
27158     {
27159         var files = [];
27160         
27161         Roo.each(this.files, function(file){
27162             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27163                 files.push(file);
27164                 return;
27165             }
27166
27167             o.target.remove();
27168
27169         }, this);
27170         
27171         this.files = files;
27172         
27173         this.refresh();
27174     },
27175     
27176     clear : function()
27177     {
27178         Roo.each(this.files, function(file){
27179             if(!file.target){
27180                 return;
27181             }
27182             
27183             file.target.remove();
27184
27185         }, this);
27186         
27187         this.files = [];
27188         
27189         this.refresh();
27190     },
27191     
27192     onClick : function(e, el, o)
27193     {
27194         e.preventDefault();
27195         
27196         this.fireEvent('click', this, o);
27197         
27198     },
27199     
27200     closable : function(closable)
27201     {
27202         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27203             
27204             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27205             
27206             if(closable){
27207                 el.show();
27208                 return;
27209             }
27210             
27211             el.hide();
27212             
27213         }, this);
27214     },
27215     
27216     xhrOnLoad : function(xhr)
27217     {
27218         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27219             el.remove();
27220         }, this);
27221         
27222         if (xhr.readyState !== 4) {
27223             this.arrange();
27224             this.fireEvent('exception', this, xhr);
27225             return;
27226         }
27227
27228         var response = Roo.decode(xhr.responseText);
27229         
27230         if(!response.success){
27231             this.arrange();
27232             this.fireEvent('exception', this, xhr);
27233             return;
27234         }
27235         
27236         var file = this.renderPreview(response.data);
27237         
27238         this.files.push(file);
27239         
27240         this.arrange();
27241         
27242     },
27243     
27244     xhrOnError : function(xhr)
27245     {
27246         Roo.log('xhr on error');
27247         
27248         var response = Roo.decode(xhr.responseText);
27249           
27250         Roo.log(response);
27251         
27252         this.arrange();
27253     },
27254     
27255     process : function(file)
27256     {
27257         if(this.fireEvent('process', this, file) !== false){
27258             if(this.editable && file.type.indexOf('image') != -1){
27259                 this.fireEvent('edit', this, file);
27260                 return;
27261             }
27262
27263             this.uploadStart(file, false);
27264
27265             return;
27266         }
27267         
27268     },
27269     
27270     uploadStart : function(file, crop)
27271     {
27272         this.xhr = new XMLHttpRequest();
27273         
27274         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27275             this.arrange();
27276             return;
27277         }
27278         
27279         file.xhr = this.xhr;
27280             
27281         this.managerEl.createChild({
27282             tag : 'div',
27283             cls : 'roo-document-manager-loading',
27284             cn : [
27285                 {
27286                     tag : 'div',
27287                     tooltip : file.name,
27288                     cls : 'roo-document-manager-thumb',
27289                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27290                 }
27291             ]
27292
27293         });
27294
27295         this.xhr.open(this.method, this.url, true);
27296         
27297         var headers = {
27298             "Accept": "application/json",
27299             "Cache-Control": "no-cache",
27300             "X-Requested-With": "XMLHttpRequest"
27301         };
27302         
27303         for (var headerName in headers) {
27304             var headerValue = headers[headerName];
27305             if (headerValue) {
27306                 this.xhr.setRequestHeader(headerName, headerValue);
27307             }
27308         }
27309         
27310         var _this = this;
27311         
27312         this.xhr.onload = function()
27313         {
27314             _this.xhrOnLoad(_this.xhr);
27315         }
27316         
27317         this.xhr.onerror = function()
27318         {
27319             _this.xhrOnError(_this.xhr);
27320         }
27321         
27322         var formData = new FormData();
27323
27324         formData.append('returnHTML', 'NO');
27325         
27326         if(crop){
27327             formData.append('crop', crop);
27328         }
27329         
27330         formData.append(this.paramName, file, file.name);
27331         
27332         if(this.fireEvent('prepare', this, formData) != false){
27333             this.xhr.send(formData);
27334         };
27335     },
27336     
27337     uploadCancel : function()
27338     {
27339         if (this.xhr) {
27340             this.xhr.abort();
27341         }
27342         
27343         
27344         this.delegates = [];
27345         
27346         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27347             el.remove();
27348         }, this);
27349         
27350         this.arrange();
27351     },
27352     
27353     renderPreview : function(file)
27354     {
27355         if(typeof(file.target) != 'undefined' && file.target){
27356             return file;
27357         }
27358         
27359         var previewEl = this.managerEl.createChild({
27360             tag : 'div',
27361             cls : 'roo-document-manager-preview',
27362             cn : [
27363                 {
27364                     tag : 'div',
27365                     tooltip : file.filename,
27366                     cls : 'roo-document-manager-thumb',
27367                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27368                 },
27369                 {
27370                     tag : 'button',
27371                     cls : 'close',
27372                     html : '<i class="fa fa-times-circle"></i>'
27373                 }
27374             ]
27375         });
27376
27377         var close = previewEl.select('button.close', true).first();
27378
27379         close.on('click', this.onRemove, this, file);
27380
27381         file.target = previewEl;
27382
27383         var image = previewEl.select('img', true).first();
27384         
27385         var _this = this;
27386         
27387         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27388         
27389         image.on('click', this.onClick, this, file);
27390         
27391         return file;
27392         
27393     },
27394     
27395     onPreviewLoad : function(file, image)
27396     {
27397         if(typeof(file.target) == 'undefined' || !file.target){
27398             return;
27399         }
27400         
27401         var width = image.dom.naturalWidth || image.dom.width;
27402         var height = image.dom.naturalHeight || image.dom.height;
27403         
27404         if(width > height){
27405             file.target.addClass('wide');
27406             return;
27407         }
27408         
27409         file.target.addClass('tall');
27410         return;
27411         
27412     },
27413     
27414     uploadFromSource : function(file, crop)
27415     {
27416         this.xhr = new XMLHttpRequest();
27417         
27418         this.managerEl.createChild({
27419             tag : 'div',
27420             cls : 'roo-document-manager-loading',
27421             cn : [
27422                 {
27423                     tag : 'div',
27424                     tooltip : file.name,
27425                     cls : 'roo-document-manager-thumb',
27426                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27427                 }
27428             ]
27429
27430         });
27431
27432         this.xhr.open(this.method, this.url, true);
27433         
27434         var headers = {
27435             "Accept": "application/json",
27436             "Cache-Control": "no-cache",
27437             "X-Requested-With": "XMLHttpRequest"
27438         };
27439         
27440         for (var headerName in headers) {
27441             var headerValue = headers[headerName];
27442             if (headerValue) {
27443                 this.xhr.setRequestHeader(headerName, headerValue);
27444             }
27445         }
27446         
27447         var _this = this;
27448         
27449         this.xhr.onload = function()
27450         {
27451             _this.xhrOnLoad(_this.xhr);
27452         }
27453         
27454         this.xhr.onerror = function()
27455         {
27456             _this.xhrOnError(_this.xhr);
27457         }
27458         
27459         var formData = new FormData();
27460
27461         formData.append('returnHTML', 'NO');
27462         
27463         formData.append('crop', crop);
27464         
27465         if(typeof(file.filename) != 'undefined'){
27466             formData.append('filename', file.filename);
27467         }
27468         
27469         if(typeof(file.mimetype) != 'undefined'){
27470             formData.append('mimetype', file.mimetype);
27471         }
27472         
27473         if(this.fireEvent('prepare', this, formData) != false){
27474             this.xhr.send(formData);
27475         };
27476     }
27477 });
27478
27479 /*
27480 * Licence: LGPL
27481 */
27482
27483 /**
27484  * @class Roo.bootstrap.DocumentViewer
27485  * @extends Roo.bootstrap.Component
27486  * Bootstrap DocumentViewer class
27487  * 
27488  * @constructor
27489  * Create a new DocumentViewer
27490  * @param {Object} config The config object
27491  */
27492
27493 Roo.bootstrap.DocumentViewer = function(config){
27494     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27495     
27496     this.addEvents({
27497         /**
27498          * @event initial
27499          * Fire after initEvent
27500          * @param {Roo.bootstrap.DocumentViewer} this
27501          */
27502         "initial" : true,
27503         /**
27504          * @event click
27505          * Fire after click
27506          * @param {Roo.bootstrap.DocumentViewer} this
27507          */
27508         "click" : true,
27509         /**
27510          * @event trash
27511          * Fire after trash button
27512          * @param {Roo.bootstrap.DocumentViewer} this
27513          */
27514         "trash" : true
27515         
27516     });
27517 };
27518
27519 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27520     
27521     getAutoCreate : function()
27522     {
27523         var cfg = {
27524             tag : 'div',
27525             cls : 'roo-document-viewer',
27526             cn : [
27527                 {
27528                     tag : 'div',
27529                     cls : 'roo-document-viewer-body',
27530                     cn : [
27531                         {
27532                             tag : 'div',
27533                             cls : 'roo-document-viewer-thumb',
27534                             cn : [
27535                                 {
27536                                     tag : 'img',
27537                                     cls : 'roo-document-viewer-image'
27538                                 }
27539                             ]
27540                         }
27541                     ]
27542                 },
27543                 {
27544                     tag : 'div',
27545                     cls : 'roo-document-viewer-footer',
27546                     cn : {
27547                         tag : 'div',
27548                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27549                         cn : [
27550                             {
27551                                 tag : 'div',
27552                                 cls : 'btn-group',
27553                                 cn : [
27554                                     {
27555                                         tag : 'button',
27556                                         cls : 'btn btn-default roo-document-viewer-trash',
27557                                         html : '<i class="fa fa-trash"></i>'
27558                                     }
27559                                 ]
27560                             }
27561                         ]
27562                     }
27563                 }
27564             ]
27565         };
27566         
27567         return cfg;
27568     },
27569     
27570     initEvents : function()
27571     {
27572         
27573         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27574         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27575         
27576         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27577         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27578         
27579         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27580         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27581         
27582         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27583         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27584         
27585         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27586         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27587         
27588         this.bodyEl.on('click', this.onClick, this);
27589         
27590         this.trashBtn.on('click', this.onTrash, this);
27591         
27592     },
27593     
27594     initial : function()
27595     {
27596 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27597         
27598         
27599         this.fireEvent('initial', this);
27600         
27601     },
27602     
27603     onClick : function(e)
27604     {
27605         e.preventDefault();
27606         
27607         this.fireEvent('click', this);
27608     },
27609     
27610     onTrash : function(e)
27611     {
27612         e.preventDefault();
27613         
27614         this.fireEvent('trash', this);
27615     }
27616     
27617 });
27618 /*
27619  * - LGPL
27620  *
27621  * nav progress bar
27622  * 
27623  */
27624
27625 /**
27626  * @class Roo.bootstrap.NavProgressBar
27627  * @extends Roo.bootstrap.Component
27628  * Bootstrap NavProgressBar class
27629  * 
27630  * @constructor
27631  * Create a new nav progress bar
27632  * @param {Object} config The config object
27633  */
27634
27635 Roo.bootstrap.NavProgressBar = function(config){
27636     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27637
27638     this.bullets = this.bullets || [];
27639    
27640 //    Roo.bootstrap.NavProgressBar.register(this);
27641      this.addEvents({
27642         /**
27643              * @event changed
27644              * Fires when the active item changes
27645              * @param {Roo.bootstrap.NavProgressBar} this
27646              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27647              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27648          */
27649         'changed': true
27650      });
27651     
27652 };
27653
27654 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27655     
27656     bullets : [],
27657     barItems : [],
27658     
27659     getAutoCreate : function()
27660     {
27661         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27662         
27663         cfg = {
27664             tag : 'div',
27665             cls : 'roo-navigation-bar-group',
27666             cn : [
27667                 {
27668                     tag : 'div',
27669                     cls : 'roo-navigation-top-bar'
27670                 },
27671                 {
27672                     tag : 'div',
27673                     cls : 'roo-navigation-bullets-bar',
27674                     cn : [
27675                         {
27676                             tag : 'ul',
27677                             cls : 'roo-navigation-bar'
27678                         }
27679                     ]
27680                 },
27681                 
27682                 {
27683                     tag : 'div',
27684                     cls : 'roo-navigation-bottom-bar'
27685                 }
27686             ]
27687             
27688         };
27689         
27690         return cfg;
27691         
27692     },
27693     
27694     initEvents: function() 
27695     {
27696         
27697     },
27698     
27699     onRender : function(ct, position) 
27700     {
27701         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27702         
27703         if(this.bullets.length){
27704             Roo.each(this.bullets, function(b){
27705                this.addItem(b);
27706             }, this);
27707         }
27708         
27709         this.format();
27710         
27711     },
27712     
27713     addItem : function(cfg)
27714     {
27715         var item = new Roo.bootstrap.NavProgressItem(cfg);
27716         
27717         item.parentId = this.id;
27718         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27719         
27720         if(cfg.html){
27721             var top = new Roo.bootstrap.Element({
27722                 tag : 'div',
27723                 cls : 'roo-navigation-bar-text'
27724             });
27725             
27726             var bottom = new Roo.bootstrap.Element({
27727                 tag : 'div',
27728                 cls : 'roo-navigation-bar-text'
27729             });
27730             
27731             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27732             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27733             
27734             var topText = new Roo.bootstrap.Element({
27735                 tag : 'span',
27736                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27737             });
27738             
27739             var bottomText = new Roo.bootstrap.Element({
27740                 tag : 'span',
27741                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27742             });
27743             
27744             topText.onRender(top.el, null);
27745             bottomText.onRender(bottom.el, null);
27746             
27747             item.topEl = top;
27748             item.bottomEl = bottom;
27749         }
27750         
27751         this.barItems.push(item);
27752         
27753         return item;
27754     },
27755     
27756     getActive : function()
27757     {
27758         var active = false;
27759         
27760         Roo.each(this.barItems, function(v){
27761             
27762             if (!v.isActive()) {
27763                 return;
27764             }
27765             
27766             active = v;
27767             return false;
27768             
27769         });
27770         
27771         return active;
27772     },
27773     
27774     setActiveItem : function(item)
27775     {
27776         var prev = false;
27777         
27778         Roo.each(this.barItems, function(v){
27779             if (v.rid == item.rid) {
27780                 return ;
27781             }
27782             
27783             if (v.isActive()) {
27784                 v.setActive(false);
27785                 prev = v;
27786             }
27787         });
27788
27789         item.setActive(true);
27790         
27791         this.fireEvent('changed', this, item, prev);
27792     },
27793     
27794     getBarItem: function(rid)
27795     {
27796         var ret = false;
27797         
27798         Roo.each(this.barItems, function(e) {
27799             if (e.rid != rid) {
27800                 return;
27801             }
27802             
27803             ret =  e;
27804             return false;
27805         });
27806         
27807         return ret;
27808     },
27809     
27810     indexOfItem : function(item)
27811     {
27812         var index = false;
27813         
27814         Roo.each(this.barItems, function(v, i){
27815             
27816             if (v.rid != item.rid) {
27817                 return;
27818             }
27819             
27820             index = i;
27821             return false
27822         });
27823         
27824         return index;
27825     },
27826     
27827     setActiveNext : function()
27828     {
27829         var i = this.indexOfItem(this.getActive());
27830         
27831         if (i > this.barItems.length) {
27832             return;
27833         }
27834         
27835         this.setActiveItem(this.barItems[i+1]);
27836     },
27837     
27838     setActivePrev : function()
27839     {
27840         var i = this.indexOfItem(this.getActive());
27841         
27842         if (i  < 1) {
27843             return;
27844         }
27845         
27846         this.setActiveItem(this.barItems[i-1]);
27847     },
27848     
27849     format : function()
27850     {
27851         if(!this.barItems.length){
27852             return;
27853         }
27854      
27855         var width = 100 / this.barItems.length;
27856         
27857         Roo.each(this.barItems, function(i){
27858             i.el.setStyle('width', width + '%');
27859             i.topEl.el.setStyle('width', width + '%');
27860             i.bottomEl.el.setStyle('width', width + '%');
27861         }, this);
27862         
27863     }
27864     
27865 });
27866 /*
27867  * - LGPL
27868  *
27869  * Nav Progress Item
27870  * 
27871  */
27872
27873 /**
27874  * @class Roo.bootstrap.NavProgressItem
27875  * @extends Roo.bootstrap.Component
27876  * Bootstrap NavProgressItem class
27877  * @cfg {String} rid the reference id
27878  * @cfg {Boolean} active (true|false) Is item active default false
27879  * @cfg {Boolean} disabled (true|false) Is item active default false
27880  * @cfg {String} html
27881  * @cfg {String} position (top|bottom) text position default bottom
27882  * @cfg {String} icon show icon instead of number
27883  * 
27884  * @constructor
27885  * Create a new NavProgressItem
27886  * @param {Object} config The config object
27887  */
27888 Roo.bootstrap.NavProgressItem = function(config){
27889     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27890     this.addEvents({
27891         // raw events
27892         /**
27893          * @event click
27894          * The raw click event for the entire grid.
27895          * @param {Roo.bootstrap.NavProgressItem} this
27896          * @param {Roo.EventObject} e
27897          */
27898         "click" : true
27899     });
27900    
27901 };
27902
27903 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27904     
27905     rid : '',
27906     active : false,
27907     disabled : false,
27908     html : '',
27909     position : 'bottom',
27910     icon : false,
27911     
27912     getAutoCreate : function()
27913     {
27914         var iconCls = 'roo-navigation-bar-item-icon';
27915         
27916         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27917         
27918         var cfg = {
27919             tag: 'li',
27920             cls: 'roo-navigation-bar-item',
27921             cn : [
27922                 {
27923                     tag : 'i',
27924                     cls : iconCls
27925                 }
27926             ]
27927         };
27928         
27929         if(this.active){
27930             cfg.cls += ' active';
27931         }
27932         if(this.disabled){
27933             cfg.cls += ' disabled';
27934         }
27935         
27936         return cfg;
27937     },
27938     
27939     disable : function()
27940     {
27941         this.setDisabled(true);
27942     },
27943     
27944     enable : function()
27945     {
27946         this.setDisabled(false);
27947     },
27948     
27949     initEvents: function() 
27950     {
27951         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27952         
27953         this.iconEl.on('click', this.onClick, this);
27954     },
27955     
27956     onClick : function(e)
27957     {
27958         e.preventDefault();
27959         
27960         if(this.disabled){
27961             return;
27962         }
27963         
27964         if(this.fireEvent('click', this, e) === false){
27965             return;
27966         };
27967         
27968         this.parent().setActiveItem(this);
27969     },
27970     
27971     isActive: function () 
27972     {
27973         return this.active;
27974     },
27975     
27976     setActive : function(state)
27977     {
27978         if(this.active == state){
27979             return;
27980         }
27981         
27982         this.active = state;
27983         
27984         if (state) {
27985             this.el.addClass('active');
27986             return;
27987         }
27988         
27989         this.el.removeClass('active');
27990         
27991         return;
27992     },
27993     
27994     setDisabled : function(state)
27995     {
27996         if(this.disabled == state){
27997             return;
27998         }
27999         
28000         this.disabled = state;
28001         
28002         if (state) {
28003             this.el.addClass('disabled');
28004             return;
28005         }
28006         
28007         this.el.removeClass('disabled');
28008     },
28009     
28010     tooltipEl : function()
28011     {
28012         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28013     }
28014 });
28015  
28016
28017  /*
28018  * - LGPL
28019  *
28020  * FieldLabel
28021  * 
28022  */
28023
28024 /**
28025  * @class Roo.bootstrap.FieldLabel
28026  * @extends Roo.bootstrap.Component
28027  * Bootstrap FieldLabel class
28028  * @cfg {String} html contents of the element
28029  * @cfg {String} tag tag of the element default label
28030  * @cfg {String} cls class of the element
28031  * @cfg {String} target label target 
28032  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28033  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28034  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28035  * @cfg {String} iconTooltip default "This field is required"
28036  * 
28037  * @constructor
28038  * Create a new FieldLabel
28039  * @param {Object} config The config object
28040  */
28041
28042 Roo.bootstrap.FieldLabel = function(config){
28043     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28044     
28045     this.addEvents({
28046             /**
28047              * @event invalid
28048              * Fires after the field has been marked as invalid.
28049              * @param {Roo.form.FieldLabel} this
28050              * @param {String} msg The validation message
28051              */
28052             invalid : true,
28053             /**
28054              * @event valid
28055              * Fires after the field has been validated with no errors.
28056              * @param {Roo.form.FieldLabel} this
28057              */
28058             valid : true
28059         });
28060 };
28061
28062 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28063     
28064     tag: 'label',
28065     cls: '',
28066     html: '',
28067     target: '',
28068     allowBlank : true,
28069     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28070     validClass : 'text-success fa fa-lg fa-check',
28071     iconTooltip : 'This field is required',
28072     
28073     getAutoCreate : function(){
28074         
28075         var cfg = {
28076             tag : this.tag,
28077             cls : 'roo-bootstrap-field-label ' + this.cls,
28078             for : this.target,
28079             cn : [
28080                 {
28081                     tag : 'i',
28082                     cls : '',
28083                     tooltip : this.iconTooltip
28084                 },
28085                 {
28086                     tag : 'span',
28087                     html : this.html
28088                 }
28089             ] 
28090         };
28091         
28092         return cfg;
28093     },
28094     
28095     initEvents: function() 
28096     {
28097         Roo.bootstrap.Element.superclass.initEvents.call(this);
28098         
28099         this.iconEl = this.el.select('i', true).first();
28100         
28101         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28102         
28103         Roo.bootstrap.FieldLabel.register(this);
28104     },
28105     
28106     /**
28107      * Mark this field as valid
28108      */
28109     markValid : function()
28110     {
28111         this.iconEl.show();
28112         
28113         this.iconEl.removeClass(this.invalidClass);
28114         
28115         this.iconEl.addClass(this.validClass);
28116         
28117         this.fireEvent('valid', this);
28118     },
28119     
28120     /**
28121      * Mark this field as invalid
28122      * @param {String} msg The validation message
28123      */
28124     markInvalid : function(msg)
28125     {
28126         this.iconEl.show();
28127         
28128         this.iconEl.removeClass(this.validClass);
28129         
28130         this.iconEl.addClass(this.invalidClass);
28131         
28132         this.fireEvent('invalid', this, msg);
28133     }
28134     
28135    
28136 });
28137
28138 Roo.apply(Roo.bootstrap.FieldLabel, {
28139     
28140     groups: {},
28141     
28142      /**
28143     * register a FieldLabel Group
28144     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28145     */
28146     register : function(label)
28147     {
28148         if(this.groups.hasOwnProperty(label.target)){
28149             return;
28150         }
28151      
28152         this.groups[label.target] = label;
28153         
28154     },
28155     /**
28156     * fetch a FieldLabel Group based on the target
28157     * @param {string} target
28158     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28159     */
28160     get: function(target) {
28161         if (typeof(this.groups[target]) == 'undefined') {
28162             return false;
28163         }
28164         
28165         return this.groups[target] ;
28166     }
28167 });
28168
28169  
28170
28171  /*
28172  * - LGPL
28173  *
28174  * page DateSplitField.
28175  * 
28176  */
28177
28178
28179 /**
28180  * @class Roo.bootstrap.DateSplitField
28181  * @extends Roo.bootstrap.Component
28182  * Bootstrap DateSplitField class
28183  * @cfg {string} fieldLabel - the label associated
28184  * @cfg {Number} labelWidth set the width of label (0-12)
28185  * @cfg {String} labelAlign (top|left)
28186  * @cfg {Boolean} dayAllowBlank (true|false) default false
28187  * @cfg {Boolean} monthAllowBlank (true|false) default false
28188  * @cfg {Boolean} yearAllowBlank (true|false) default false
28189  * @cfg {string} dayPlaceholder 
28190  * @cfg {string} monthPlaceholder
28191  * @cfg {string} yearPlaceholder
28192  * @cfg {string} dayFormat default 'd'
28193  * @cfg {string} monthFormat default 'm'
28194  * @cfg {string} yearFormat default 'Y'
28195
28196  *     
28197  * @constructor
28198  * Create a new DateSplitField
28199  * @param {Object} config The config object
28200  */
28201
28202 Roo.bootstrap.DateSplitField = function(config){
28203     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28204     
28205     this.addEvents({
28206         // raw events
28207          /**
28208          * @event years
28209          * getting the data of years
28210          * @param {Roo.bootstrap.DateSplitField} this
28211          * @param {Object} years
28212          */
28213         "years" : true,
28214         /**
28215          * @event days
28216          * getting the data of days
28217          * @param {Roo.bootstrap.DateSplitField} this
28218          * @param {Object} days
28219          */
28220         "days" : true,
28221         /**
28222          * @event invalid
28223          * Fires after the field has been marked as invalid.
28224          * @param {Roo.form.Field} this
28225          * @param {String} msg The validation message
28226          */
28227         invalid : true,
28228        /**
28229          * @event valid
28230          * Fires after the field has been validated with no errors.
28231          * @param {Roo.form.Field} this
28232          */
28233         valid : true
28234     });
28235 };
28236
28237 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28238     
28239     fieldLabel : '',
28240     labelAlign : 'top',
28241     labelWidth : 3,
28242     dayAllowBlank : false,
28243     monthAllowBlank : false,
28244     yearAllowBlank : false,
28245     dayPlaceholder : '',
28246     monthPlaceholder : '',
28247     yearPlaceholder : '',
28248     dayFormat : 'd',
28249     monthFormat : 'm',
28250     yearFormat : 'Y',
28251     isFormField : true,
28252     
28253     getAutoCreate : function()
28254     {
28255         var cfg = {
28256             tag : 'div',
28257             cls : 'row roo-date-split-field-group',
28258             cn : [
28259                 {
28260                     tag : 'input',
28261                     type : 'hidden',
28262                     cls : 'form-hidden-field roo-date-split-field-group-value',
28263                     name : this.name
28264                 }
28265             ]
28266         };
28267         
28268         if(this.fieldLabel){
28269             cfg.cn.push({
28270                 tag : 'div',
28271                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28272                 cn : [
28273                     {
28274                         tag : 'label',
28275                         html : this.fieldLabel
28276                     }
28277                 ]
28278             });
28279         }
28280         
28281         Roo.each(['day', 'month', 'year'], function(t){
28282             cfg.cn.push({
28283                 tag : 'div',
28284                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28285             });
28286         }, this);
28287         
28288         return cfg;
28289     },
28290     
28291     inputEl: function ()
28292     {
28293         return this.el.select('.roo-date-split-field-group-value', true).first();
28294     },
28295     
28296     onRender : function(ct, position) 
28297     {
28298         var _this = this;
28299         
28300         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28301         
28302         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28303         
28304         this.dayField = new Roo.bootstrap.ComboBox({
28305             allowBlank : this.dayAllowBlank,
28306             alwaysQuery : true,
28307             displayField : 'value',
28308             editable : false,
28309             fieldLabel : '',
28310             forceSelection : true,
28311             mode : 'local',
28312             placeholder : this.dayPlaceholder,
28313             selectOnFocus : true,
28314             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28315             triggerAction : 'all',
28316             typeAhead : true,
28317             valueField : 'value',
28318             store : new Roo.data.SimpleStore({
28319                 data : (function() {    
28320                     var days = [];
28321                     _this.fireEvent('days', _this, days);
28322                     return days;
28323                 })(),
28324                 fields : [ 'value' ]
28325             }),
28326             listeners : {
28327                 select : function (_self, record, index)
28328                 {
28329                     _this.setValue(_this.getValue());
28330                 }
28331             }
28332         });
28333
28334         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28335         
28336         this.monthField = new Roo.bootstrap.MonthField({
28337             after : '<i class=\"fa fa-calendar\"></i>',
28338             allowBlank : this.monthAllowBlank,
28339             placeholder : this.monthPlaceholder,
28340             readOnly : true,
28341             listeners : {
28342                 render : function (_self)
28343                 {
28344                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28345                         e.preventDefault();
28346                         _self.focus();
28347                     });
28348                 },
28349                 select : function (_self, oldvalue, newvalue)
28350                 {
28351                     _this.setValue(_this.getValue());
28352                 }
28353             }
28354         });
28355         
28356         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28357         
28358         this.yearField = new Roo.bootstrap.ComboBox({
28359             allowBlank : this.yearAllowBlank,
28360             alwaysQuery : true,
28361             displayField : 'value',
28362             editable : false,
28363             fieldLabel : '',
28364             forceSelection : true,
28365             mode : 'local',
28366             placeholder : this.yearPlaceholder,
28367             selectOnFocus : true,
28368             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28369             triggerAction : 'all',
28370             typeAhead : true,
28371             valueField : 'value',
28372             store : new Roo.data.SimpleStore({
28373                 data : (function() {
28374                     var years = [];
28375                     _this.fireEvent('years', _this, years);
28376                     return years;
28377                 })(),
28378                 fields : [ 'value' ]
28379             }),
28380             listeners : {
28381                 select : function (_self, record, index)
28382                 {
28383                     _this.setValue(_this.getValue());
28384                 }
28385             }
28386         });
28387
28388         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28389     },
28390     
28391     setValue : function(v, format)
28392     {
28393         this.inputEl.dom.value = v;
28394         
28395         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28396         
28397         var d = Date.parseDate(v, f);
28398         
28399         if(!d){
28400             this.validate();
28401             return;
28402         }
28403         
28404         this.setDay(d.format(this.dayFormat));
28405         this.setMonth(d.format(this.monthFormat));
28406         this.setYear(d.format(this.yearFormat));
28407         
28408         this.validate();
28409         
28410         return;
28411     },
28412     
28413     setDay : function(v)
28414     {
28415         this.dayField.setValue(v);
28416         this.inputEl.dom.value = this.getValue();
28417         this.validate();
28418         return;
28419     },
28420     
28421     setMonth : function(v)
28422     {
28423         this.monthField.setValue(v, true);
28424         this.inputEl.dom.value = this.getValue();
28425         this.validate();
28426         return;
28427     },
28428     
28429     setYear : function(v)
28430     {
28431         this.yearField.setValue(v);
28432         this.inputEl.dom.value = this.getValue();
28433         this.validate();
28434         return;
28435     },
28436     
28437     getDay : function()
28438     {
28439         return this.dayField.getValue();
28440     },
28441     
28442     getMonth : function()
28443     {
28444         return this.monthField.getValue();
28445     },
28446     
28447     getYear : function()
28448     {
28449         return this.yearField.getValue();
28450     },
28451     
28452     getValue : function()
28453     {
28454         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28455         
28456         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28457         
28458         return date;
28459     },
28460     
28461     reset : function()
28462     {
28463         this.setDay('');
28464         this.setMonth('');
28465         this.setYear('');
28466         this.inputEl.dom.value = '';
28467         this.validate();
28468         return;
28469     },
28470     
28471     validate : function()
28472     {
28473         var d = this.dayField.validate();
28474         var m = this.monthField.validate();
28475         var y = this.yearField.validate();
28476         
28477         var valid = true;
28478         
28479         if(
28480                 (!this.dayAllowBlank && !d) ||
28481                 (!this.monthAllowBlank && !m) ||
28482                 (!this.yearAllowBlank && !y)
28483         ){
28484             valid = false;
28485         }
28486         
28487         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28488             return valid;
28489         }
28490         
28491         if(valid){
28492             this.markValid();
28493             return valid;
28494         }
28495         
28496         this.markInvalid();
28497         
28498         return valid;
28499     },
28500     
28501     markValid : function()
28502     {
28503         
28504         var label = this.el.select('label', true).first();
28505         var icon = this.el.select('i.fa-star', true).first();
28506
28507         if(label && icon){
28508             icon.remove();
28509         }
28510         
28511         this.fireEvent('valid', this);
28512     },
28513     
28514      /**
28515      * Mark this field as invalid
28516      * @param {String} msg The validation message
28517      */
28518     markInvalid : function(msg)
28519     {
28520         
28521         var label = this.el.select('label', true).first();
28522         var icon = this.el.select('i.fa-star', true).first();
28523
28524         if(label && !icon){
28525             this.el.select('.roo-date-split-field-label', true).createChild({
28526                 tag : 'i',
28527                 cls : 'text-danger fa fa-lg fa-star',
28528                 tooltip : 'This field is required',
28529                 style : 'margin-right:5px;'
28530             }, label, true);
28531         }
28532         
28533         this.fireEvent('invalid', this, msg);
28534     },
28535     
28536     clearInvalid : function()
28537     {
28538         var label = this.el.select('label', true).first();
28539         var icon = this.el.select('i.fa-star', true).first();
28540
28541         if(label && icon){
28542             icon.remove();
28543         }
28544         
28545         this.fireEvent('valid', this);
28546     },
28547     
28548     getName: function()
28549     {
28550         return this.name;
28551     }
28552     
28553 });
28554
28555  /**
28556  *
28557  * This is based on 
28558  * http://masonry.desandro.com
28559  *
28560  * The idea is to render all the bricks based on vertical width...
28561  *
28562  * The original code extends 'outlayer' - we might need to use that....
28563  * 
28564  */
28565
28566
28567 /**
28568  * @class Roo.bootstrap.LayoutMasonry
28569  * @extends Roo.bootstrap.Component
28570  * Bootstrap Layout Masonry class
28571  * 
28572  * @constructor
28573  * Create a new Element
28574  * @param {Object} config The config object
28575  */
28576
28577 Roo.bootstrap.LayoutMasonry = function(config){
28578     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28579     
28580     this.bricks = [];
28581     
28582 };
28583
28584 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28585     
28586     /**
28587      * @cfg {Boolean} isLayoutInstant = no animation?
28588      */   
28589     isLayoutInstant : false, // needed?
28590    
28591     /**
28592      * @cfg {Number} boxWidth  width of the columns
28593      */   
28594     boxWidth : 450,
28595     
28596       /**
28597      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28598      */   
28599     boxHeight : 0,
28600     
28601     /**
28602      * @cfg {Number} padWidth padding below box..
28603      */   
28604     padWidth : 10, 
28605     
28606     /**
28607      * @cfg {Number} gutter gutter width..
28608      */   
28609     gutter : 10,
28610     
28611      /**
28612      * @cfg {Number} maxCols maximum number of columns
28613      */   
28614     
28615     maxCols: 0,
28616     
28617     /**
28618      * @cfg {Boolean} isAutoInitial defalut true
28619      */   
28620     isAutoInitial : true, 
28621     
28622     containerWidth: 0,
28623     
28624     /**
28625      * @cfg {Boolean} isHorizontal defalut false
28626      */   
28627     isHorizontal : false, 
28628
28629     currentSize : null,
28630     
28631     tag: 'div',
28632     
28633     cls: '',
28634     
28635     bricks: null, //CompositeElement
28636     
28637     cols : 1,
28638     
28639     _isLayoutInited : false,
28640     
28641 //    isAlternative : false, // only use for vertical layout...
28642     
28643     /**
28644      * @cfg {Number} alternativePadWidth padding below box..
28645      */   
28646     alternativePadWidth : 50, 
28647     
28648     getAutoCreate : function(){
28649         
28650         var cfg = {
28651             tag: this.tag,
28652             cls: 'blog-masonary-wrapper ' + this.cls,
28653             cn : {
28654                 cls : 'mas-boxes masonary'
28655             }
28656         };
28657         
28658         return cfg;
28659     },
28660     
28661     getChildContainer: function( )
28662     {
28663         if (this.boxesEl) {
28664             return this.boxesEl;
28665         }
28666         
28667         this.boxesEl = this.el.select('.mas-boxes').first();
28668         
28669         return this.boxesEl;
28670     },
28671     
28672     
28673     initEvents : function()
28674     {
28675         var _this = this;
28676         
28677         if(this.isAutoInitial){
28678             Roo.log('hook children rendered');
28679             this.on('childrenrendered', function() {
28680                 Roo.log('children rendered');
28681                 _this.initial();
28682             } ,this);
28683         }
28684     },
28685     
28686     initial : function()
28687     {
28688         this.currentSize = this.el.getBox(true);
28689         
28690         Roo.EventManager.onWindowResize(this.resize, this); 
28691
28692         if(!this.isAutoInitial){
28693             this.layout();
28694             return;
28695         }
28696         
28697         this.layout();
28698         
28699         return;
28700         //this.layout.defer(500,this);
28701         
28702     },
28703     
28704     resize : function()
28705     {
28706         Roo.log('resize');
28707         
28708         var cs = this.el.getBox(true);
28709         
28710         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28711             Roo.log("no change in with or X");
28712             return;
28713         }
28714         
28715         this.currentSize = cs;
28716         
28717         this.layout();
28718         
28719     },
28720     
28721     layout : function()
28722     {   
28723         this._resetLayout();
28724         
28725         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28726         
28727         this.layoutItems( isInstant );
28728       
28729         this._isLayoutInited = true;
28730         
28731     },
28732     
28733     _resetLayout : function()
28734     {
28735         if(this.isHorizontal){
28736             this.horizontalMeasureColumns();
28737             return;
28738         }
28739         
28740         this.verticalMeasureColumns();
28741         
28742     },
28743     
28744     verticalMeasureColumns : function()
28745     {
28746         this.getContainerWidth();
28747         
28748 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28749 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28750 //            return;
28751 //        }
28752         
28753         var boxWidth = this.boxWidth + this.padWidth;
28754         
28755         if(this.containerWidth < this.boxWidth){
28756             boxWidth = this.containerWidth
28757         }
28758         
28759         var containerWidth = this.containerWidth;
28760         
28761         var cols = Math.floor(containerWidth / boxWidth);
28762         
28763         this.cols = Math.max( cols, 1 );
28764         
28765         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28766         
28767         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28768         
28769         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28770         
28771         this.colWidth = boxWidth + avail - this.padWidth;
28772         
28773         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28774         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28775     },
28776     
28777     horizontalMeasureColumns : function()
28778     {
28779         this.getContainerWidth();
28780         
28781         var boxWidth = this.boxWidth;
28782         
28783         if(this.containerWidth < boxWidth){
28784             boxWidth = this.containerWidth;
28785         }
28786         
28787         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28788         
28789         this.el.setHeight(boxWidth);
28790         
28791     },
28792     
28793     getContainerWidth : function()
28794     {
28795         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
28796     },
28797     
28798     layoutItems : function( isInstant )
28799     {
28800         var items = Roo.apply([], this.bricks);
28801         
28802         if(this.isHorizontal){
28803             this._horizontalLayoutItems( items , isInstant );
28804             return;
28805         }
28806         
28807 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28808 //            this._verticalAlternativeLayoutItems( items , isInstant );
28809 //            return;
28810 //        }
28811         
28812         this._verticalLayoutItems( items , isInstant );
28813         
28814     },
28815     
28816     _verticalLayoutItems : function ( items , isInstant)
28817     {
28818         if ( !items || !items.length ) {
28819             return;
28820         }
28821         
28822         var standard = [
28823             ['xs', 'xs', 'xs', 'tall'],
28824             ['xs', 'xs', 'tall'],
28825             ['xs', 'xs', 'sm'],
28826             ['xs', 'xs', 'xs'],
28827             ['xs', 'tall'],
28828             ['xs', 'sm'],
28829             ['xs', 'xs'],
28830             ['xs'],
28831             
28832             ['sm', 'xs', 'xs'],
28833             ['sm', 'xs'],
28834             ['sm'],
28835             
28836             ['tall', 'xs', 'xs', 'xs'],
28837             ['tall', 'xs', 'xs'],
28838             ['tall', 'xs'],
28839             ['tall']
28840             
28841         ];
28842         
28843         var queue = [];
28844         
28845         var boxes = [];
28846         
28847         var box = [];
28848         
28849         Roo.each(items, function(item, k){
28850             
28851             switch (item.size) {
28852                 // these layouts take up a full box,
28853                 case 'md' :
28854                 case 'md-left' :
28855                 case 'md-right' :
28856                 case 'wide' :
28857                     
28858                     if(box.length){
28859                         boxes.push(box);
28860                         box = [];
28861                     }
28862                     
28863                     boxes.push([item]);
28864                     
28865                     break;
28866                     
28867                 case 'xs' :
28868                 case 'sm' :
28869                 case 'tall' :
28870                     
28871                     box.push(item);
28872                     
28873                     break;
28874                 default :
28875                     break;
28876                     
28877             }
28878             
28879         }, this);
28880         
28881         if(box.length){
28882             boxes.push(box);
28883             box = [];
28884         }
28885         
28886         var filterPattern = function(box, length)
28887         {
28888             if(!box.length){
28889                 return;
28890             }
28891             
28892             var match = false;
28893             
28894             var pattern = box.slice(0, length);
28895             
28896             var format = [];
28897             
28898             Roo.each(pattern, function(i){
28899                 format.push(i.size);
28900             }, this);
28901             
28902             Roo.each(standard, function(s){
28903                 
28904                 if(String(s) != String(format)){
28905                     return;
28906                 }
28907                 
28908                 match = true;
28909                 return false;
28910                 
28911             }, this);
28912             
28913             if(!match && length == 1){
28914                 return;
28915             }
28916             
28917             if(!match){
28918                 filterPattern(box, length - 1);
28919                 return;
28920             }
28921                 
28922             queue.push(pattern);
28923
28924             box = box.slice(length, box.length);
28925
28926             filterPattern(box, 4);
28927
28928             return;
28929             
28930         }
28931         
28932         Roo.each(boxes, function(box, k){
28933             
28934             if(!box.length){
28935                 return;
28936             }
28937             
28938             if(box.length == 1){
28939                 queue.push(box);
28940                 return;
28941             }
28942             
28943             filterPattern(box, 4);
28944             
28945         }, this);
28946         
28947         this._processVerticalLayoutQueue( queue, isInstant );
28948         
28949     },
28950     
28951 //    _verticalAlternativeLayoutItems : function( items , isInstant )
28952 //    {
28953 //        if ( !items || !items.length ) {
28954 //            return;
28955 //        }
28956 //
28957 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
28958 //        
28959 //    },
28960     
28961     _horizontalLayoutItems : function ( items , isInstant)
28962     {
28963         if ( !items || !items.length || items.length < 3) {
28964             return;
28965         }
28966         
28967         items.reverse();
28968         
28969         var eItems = items.slice(0, 3);
28970         
28971         items = items.slice(3, items.length);
28972         
28973         var standard = [
28974             ['xs', 'xs', 'xs', 'wide'],
28975             ['xs', 'xs', 'wide'],
28976             ['xs', 'xs', 'sm'],
28977             ['xs', 'xs', 'xs'],
28978             ['xs', 'wide'],
28979             ['xs', 'sm'],
28980             ['xs', 'xs'],
28981             ['xs'],
28982             
28983             ['sm', 'xs', 'xs'],
28984             ['sm', 'xs'],
28985             ['sm'],
28986             
28987             ['wide', 'xs', 'xs', 'xs'],
28988             ['wide', 'xs', 'xs'],
28989             ['wide', 'xs'],
28990             ['wide'],
28991             
28992             ['wide-thin']
28993         ];
28994         
28995         var queue = [];
28996         
28997         var boxes = [];
28998         
28999         var box = [];
29000         
29001         Roo.each(items, function(item, k){
29002             
29003             switch (item.size) {
29004                 case 'md' :
29005                 case 'md-left' :
29006                 case 'md-right' :
29007                 case 'tall' :
29008                     
29009                     if(box.length){
29010                         boxes.push(box);
29011                         box = [];
29012                     }
29013                     
29014                     boxes.push([item]);
29015                     
29016                     break;
29017                     
29018                 case 'xs' :
29019                 case 'sm' :
29020                 case 'wide' :
29021                 case 'wide-thin' :
29022                     
29023                     box.push(item);
29024                     
29025                     break;
29026                 default :
29027                     break;
29028                     
29029             }
29030             
29031         }, this);
29032         
29033         if(box.length){
29034             boxes.push(box);
29035             box = [];
29036         }
29037         
29038         var filterPattern = function(box, length)
29039         {
29040             if(!box.length){
29041                 return;
29042             }
29043             
29044             var match = false;
29045             
29046             var pattern = box.slice(0, length);
29047             
29048             var format = [];
29049             
29050             Roo.each(pattern, function(i){
29051                 format.push(i.size);
29052             }, this);
29053             
29054             Roo.each(standard, function(s){
29055                 
29056                 if(String(s) != String(format)){
29057                     return;
29058                 }
29059                 
29060                 match = true;
29061                 return false;
29062                 
29063             }, this);
29064             
29065             if(!match && length == 1){
29066                 return;
29067             }
29068             
29069             if(!match){
29070                 filterPattern(box, length - 1);
29071                 return;
29072             }
29073                 
29074             queue.push(pattern);
29075
29076             box = box.slice(length, box.length);
29077
29078             filterPattern(box, 4);
29079
29080             return;
29081             
29082         }
29083         
29084         Roo.each(boxes, function(box, k){
29085             
29086             if(!box.length){
29087                 return;
29088             }
29089             
29090             if(box.length == 1){
29091                 queue.push(box);
29092                 return;
29093             }
29094             
29095             filterPattern(box, 4);
29096             
29097         }, this);
29098         
29099         
29100         var prune = [];
29101         
29102         var pos = this.el.getBox(true);
29103         
29104         var minX = pos.x;
29105         
29106         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29107         
29108         var hit_end = false;
29109         
29110         Roo.each(queue, function(box){
29111             
29112             if(hit_end){
29113                 
29114                 Roo.each(box, function(b){
29115                 
29116                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29117                     b.el.hide();
29118
29119                 }, this);
29120
29121                 return;
29122             }
29123             
29124             var mx = 0;
29125             
29126             Roo.each(box, function(b){
29127                 
29128                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29129                 b.el.show();
29130
29131                 mx = Math.max(mx, b.x);
29132                 
29133             }, this);
29134             
29135             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29136             
29137             if(maxX < minX){
29138                 
29139                 Roo.each(box, function(b){
29140                 
29141                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29142                     b.el.hide();
29143                     
29144                 }, this);
29145                 
29146                 hit_end = true;
29147                 
29148                 return;
29149             }
29150             
29151             prune.push(box);
29152             
29153         }, this);
29154         
29155         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29156     },
29157     
29158     /** Sets position of item in DOM
29159     * @param {Element} item
29160     * @param {Number} x - horizontal position
29161     * @param {Number} y - vertical position
29162     * @param {Boolean} isInstant - disables transitions
29163     */
29164     _processVerticalLayoutQueue : function( queue, isInstant )
29165     {
29166         var pos = this.el.getBox(true);
29167         var x = pos.x;
29168         var y = pos.y;
29169         var maxY = [];
29170         
29171         for (var i = 0; i < this.cols; i++){
29172             maxY[i] = pos.y;
29173         }
29174         
29175         Roo.each(queue, function(box, k){
29176             
29177             var col = k % this.cols;
29178             
29179             Roo.each(box, function(b,kk){
29180                 
29181                 b.el.position('absolute');
29182                 
29183                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29184                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29185                 
29186                 if(b.size == 'md-left' || b.size == 'md-right'){
29187                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29188                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29189                 }
29190                 
29191                 b.el.setWidth(width);
29192                 b.el.setHeight(height);
29193                 // iframe?
29194                 b.el.select('iframe',true).setSize(width,height);
29195                 
29196             }, this);
29197             
29198             for (var i = 0; i < this.cols; i++){
29199                 
29200                 if(maxY[i] < maxY[col]){
29201                     col = i;
29202                     continue;
29203                 }
29204                 
29205                 col = Math.min(col, i);
29206                 
29207             }
29208             
29209             x = pos.x + col * (this.colWidth + this.padWidth);
29210             
29211             y = maxY[col];
29212             
29213             var positions = [];
29214             
29215             switch (box.length){
29216                 case 1 :
29217                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29218                     break;
29219                 case 2 :
29220                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29221                     break;
29222                 case 3 :
29223                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29224                     break;
29225                 case 4 :
29226                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29227                     break;
29228                 default :
29229                     break;
29230             }
29231             
29232             Roo.each(box, function(b,kk){
29233                 
29234                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29235                 
29236                 var sz = b.el.getSize();
29237                 
29238                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29239                 
29240             }, this);
29241             
29242         }, this);
29243         
29244         var mY = 0;
29245         
29246         for (var i = 0; i < this.cols; i++){
29247             mY = Math.max(mY, maxY[i]);
29248         }
29249         
29250         this.el.setHeight(mY - pos.y);
29251         
29252     },
29253     
29254 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29255 //    {
29256 //        var pos = this.el.getBox(true);
29257 //        var x = pos.x;
29258 //        var y = pos.y;
29259 //        var maxX = pos.right;
29260 //        
29261 //        var maxHeight = 0;
29262 //        
29263 //        Roo.each(items, function(item, k){
29264 //            
29265 //            var c = k % 2;
29266 //            
29267 //            item.el.position('absolute');
29268 //                
29269 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29270 //
29271 //            item.el.setWidth(width);
29272 //
29273 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29274 //
29275 //            item.el.setHeight(height);
29276 //            
29277 //            if(c == 0){
29278 //                item.el.setXY([x, y], isInstant ? false : true);
29279 //            } else {
29280 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29281 //            }
29282 //            
29283 //            y = y + height + this.alternativePadWidth;
29284 //            
29285 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29286 //            
29287 //        }, this);
29288 //        
29289 //        this.el.setHeight(maxHeight);
29290 //        
29291 //    },
29292     
29293     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29294     {
29295         var pos = this.el.getBox(true);
29296         
29297         var minX = pos.x;
29298         var minY = pos.y;
29299         
29300         var maxX = pos.right;
29301         
29302         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29303         
29304         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29305         
29306         Roo.each(queue, function(box, k){
29307             
29308             Roo.each(box, function(b, kk){
29309                 
29310                 b.el.position('absolute');
29311                 
29312                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29313                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29314                 
29315                 if(b.size == 'md-left' || b.size == 'md-right'){
29316                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29317                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29318                 }
29319                 
29320                 b.el.setWidth(width);
29321                 b.el.setHeight(height);
29322                 
29323             }, this);
29324             
29325             if(!box.length){
29326                 return;
29327             }
29328             
29329             var positions = [];
29330             
29331             switch (box.length){
29332                 case 1 :
29333                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29334                     break;
29335                 case 2 :
29336                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29337                     break;
29338                 case 3 :
29339                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29340                     break;
29341                 case 4 :
29342                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29343                     break;
29344                 default :
29345                     break;
29346             }
29347             
29348             Roo.each(box, function(b,kk){
29349                 
29350                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29351                 
29352                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29353                 
29354             }, this);
29355             
29356         }, this);
29357         
29358     },
29359     
29360     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29361     {
29362         Roo.each(eItems, function(b,k){
29363             
29364             b.size = (k == 0) ? 'sm' : 'xs';
29365             b.x = (k == 0) ? 2 : 1;
29366             b.y = (k == 0) ? 2 : 1;
29367             
29368             b.el.position('absolute');
29369             
29370             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29371                 
29372             b.el.setWidth(width);
29373             
29374             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29375             
29376             b.el.setHeight(height);
29377             
29378         }, this);
29379
29380         var positions = [];
29381         
29382         positions.push({
29383             x : maxX - this.unitWidth * 2 - this.gutter,
29384             y : minY
29385         });
29386         
29387         positions.push({
29388             x : maxX - this.unitWidth,
29389             y : minY + (this.unitWidth + this.gutter) * 2
29390         });
29391         
29392         positions.push({
29393             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29394             y : minY
29395         });
29396         
29397         Roo.each(eItems, function(b,k){
29398             
29399             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29400
29401         }, this);
29402         
29403     },
29404     
29405     getVerticalOneBoxColPositions : function(x, y, box)
29406     {
29407         var pos = [];
29408         
29409         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29410         
29411         if(box[0].size == 'md-left'){
29412             rand = 0;
29413         }
29414         
29415         if(box[0].size == 'md-right'){
29416             rand = 1;
29417         }
29418         
29419         pos.push({
29420             x : x + (this.unitWidth + this.gutter) * rand,
29421             y : y
29422         });
29423         
29424         return pos;
29425     },
29426     
29427     getVerticalTwoBoxColPositions : function(x, y, box)
29428     {
29429         var pos = [];
29430         
29431         if(box[0].size == 'xs'){
29432             
29433             pos.push({
29434                 x : x,
29435                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29436             });
29437
29438             pos.push({
29439                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29440                 y : y
29441             });
29442             
29443             return pos;
29444             
29445         }
29446         
29447         pos.push({
29448             x : x,
29449             y : y
29450         });
29451
29452         pos.push({
29453             x : x + (this.unitWidth + this.gutter) * 2,
29454             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29455         });
29456         
29457         return pos;
29458         
29459     },
29460     
29461     getVerticalThreeBoxColPositions : function(x, y, box)
29462     {
29463         var pos = [];
29464         
29465         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29466             
29467             pos.push({
29468                 x : x,
29469                 y : y
29470             });
29471
29472             pos.push({
29473                 x : x + (this.unitWidth + this.gutter) * 1,
29474                 y : y
29475             });
29476             
29477             pos.push({
29478                 x : x + (this.unitWidth + this.gutter) * 2,
29479                 y : y
29480             });
29481             
29482             return pos;
29483             
29484         }
29485         
29486         if(box[0].size == 'xs' && box[1].size == 'xs'){
29487             
29488             pos.push({
29489                 x : x,
29490                 y : y
29491             });
29492
29493             pos.push({
29494                 x : x,
29495                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29496             });
29497             
29498             pos.push({
29499                 x : x + (this.unitWidth + this.gutter) * 1,
29500                 y : y
29501             });
29502             
29503             return pos;
29504             
29505         }
29506         
29507         pos.push({
29508             x : x,
29509             y : y
29510         });
29511
29512         pos.push({
29513             x : x + (this.unitWidth + this.gutter) * 2,
29514             y : y
29515         });
29516
29517         pos.push({
29518             x : x + (this.unitWidth + this.gutter) * 2,
29519             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29520         });
29521             
29522         return pos;
29523         
29524     },
29525     
29526     getVerticalFourBoxColPositions : function(x, y, box)
29527     {
29528         var pos = [];
29529         
29530         if(box[0].size == 'xs'){
29531             
29532             pos.push({
29533                 x : x,
29534                 y : y
29535             });
29536
29537             pos.push({
29538                 x : x,
29539                 y : y + (this.unitHeight + this.gutter) * 1
29540             });
29541             
29542             pos.push({
29543                 x : x,
29544                 y : y + (this.unitHeight + this.gutter) * 2
29545             });
29546             
29547             pos.push({
29548                 x : x + (this.unitWidth + this.gutter) * 1,
29549                 y : y
29550             });
29551             
29552             return pos;
29553             
29554         }
29555         
29556         pos.push({
29557             x : x,
29558             y : y
29559         });
29560
29561         pos.push({
29562             x : x + (this.unitWidth + this.gutter) * 2,
29563             y : y
29564         });
29565
29566         pos.push({
29567             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29568             y : y + (this.unitHeight + this.gutter) * 1
29569         });
29570
29571         pos.push({
29572             x : x + (this.unitWidth + this.gutter) * 2,
29573             y : y + (this.unitWidth + this.gutter) * 2
29574         });
29575
29576         return pos;
29577         
29578     },
29579     
29580     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29581     {
29582         var pos = [];
29583         
29584         if(box[0].size == 'md-left'){
29585             pos.push({
29586                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29587                 y : minY
29588             });
29589             
29590             return pos;
29591         }
29592         
29593         if(box[0].size == 'md-right'){
29594             pos.push({
29595                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29596                 y : minY + (this.unitWidth + this.gutter) * 1
29597             });
29598             
29599             return pos;
29600         }
29601         
29602         var rand = Math.floor(Math.random() * (4 - box[0].y));
29603         
29604         pos.push({
29605             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29606             y : minY + (this.unitWidth + this.gutter) * rand
29607         });
29608         
29609         return pos;
29610         
29611     },
29612     
29613     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29614     {
29615         var pos = [];
29616         
29617         if(box[0].size == 'xs'){
29618             
29619             pos.push({
29620                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29621                 y : minY
29622             });
29623
29624             pos.push({
29625                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29626                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29627             });
29628             
29629             return pos;
29630             
29631         }
29632         
29633         pos.push({
29634             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29635             y : minY
29636         });
29637
29638         pos.push({
29639             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29640             y : minY + (this.unitWidth + this.gutter) * 2
29641         });
29642         
29643         return pos;
29644         
29645     },
29646     
29647     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29648     {
29649         var pos = [];
29650         
29651         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29652             
29653             pos.push({
29654                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29655                 y : minY
29656             });
29657
29658             pos.push({
29659                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29660                 y : minY + (this.unitWidth + this.gutter) * 1
29661             });
29662             
29663             pos.push({
29664                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29665                 y : minY + (this.unitWidth + this.gutter) * 2
29666             });
29667             
29668             return pos;
29669             
29670         }
29671         
29672         if(box[0].size == 'xs' && box[1].size == 'xs'){
29673             
29674             pos.push({
29675                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29676                 y : minY
29677             });
29678
29679             pos.push({
29680                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29681                 y : minY
29682             });
29683             
29684             pos.push({
29685                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29686                 y : minY + (this.unitWidth + this.gutter) * 1
29687             });
29688             
29689             return pos;
29690             
29691         }
29692         
29693         pos.push({
29694             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29695             y : minY
29696         });
29697
29698         pos.push({
29699             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29700             y : minY + (this.unitWidth + this.gutter) * 2
29701         });
29702
29703         pos.push({
29704             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29705             y : minY + (this.unitWidth + this.gutter) * 2
29706         });
29707             
29708         return pos;
29709         
29710     },
29711     
29712     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29713     {
29714         var pos = [];
29715         
29716         if(box[0].size == 'xs'){
29717             
29718             pos.push({
29719                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29720                 y : minY
29721             });
29722
29723             pos.push({
29724                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29725                 y : minY
29726             });
29727             
29728             pos.push({
29729                 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),
29730                 y : minY
29731             });
29732             
29733             pos.push({
29734                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29735                 y : minY + (this.unitWidth + this.gutter) * 1
29736             });
29737             
29738             return pos;
29739             
29740         }
29741         
29742         pos.push({
29743             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29744             y : minY
29745         });
29746         
29747         pos.push({
29748             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29749             y : minY + (this.unitWidth + this.gutter) * 2
29750         });
29751         
29752         pos.push({
29753             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29754             y : minY + (this.unitWidth + this.gutter) * 2
29755         });
29756         
29757         pos.push({
29758             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),
29759             y : minY + (this.unitWidth + this.gutter) * 2
29760         });
29761
29762         return pos;
29763         
29764     }
29765     
29766 });
29767
29768  
29769
29770  /**
29771  *
29772  * This is based on 
29773  * http://masonry.desandro.com
29774  *
29775  * The idea is to render all the bricks based on vertical width...
29776  *
29777  * The original code extends 'outlayer' - we might need to use that....
29778  * 
29779  */
29780
29781
29782 /**
29783  * @class Roo.bootstrap.LayoutMasonryAuto
29784  * @extends Roo.bootstrap.Component
29785  * Bootstrap Layout Masonry class
29786  * 
29787  * @constructor
29788  * Create a new Element
29789  * @param {Object} config The config object
29790  */
29791
29792 Roo.bootstrap.LayoutMasonryAuto = function(config){
29793     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29794 };
29795
29796 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
29797     
29798       /**
29799      * @cfg {Boolean} isFitWidth  - resize the width..
29800      */   
29801     isFitWidth : false,  // options..
29802     /**
29803      * @cfg {Boolean} isOriginLeft = left align?
29804      */   
29805     isOriginLeft : true,
29806     /**
29807      * @cfg {Boolean} isOriginTop = top align?
29808      */   
29809     isOriginTop : false,
29810     /**
29811      * @cfg {Boolean} isLayoutInstant = no animation?
29812      */   
29813     isLayoutInstant : false, // needed?
29814     /**
29815      * @cfg {Boolean} isResizingContainer = not sure if this is used..
29816      */   
29817     isResizingContainer : true,
29818     /**
29819      * @cfg {Number} columnWidth  width of the columns 
29820      */   
29821     
29822     columnWidth : 0,
29823     
29824     /**
29825      * @cfg {Number} maxCols maximum number of columns
29826      */   
29827     
29828     maxCols: 0,
29829     /**
29830      * @cfg {Number} padHeight padding below box..
29831      */   
29832     
29833     padHeight : 10, 
29834     
29835     /**
29836      * @cfg {Boolean} isAutoInitial defalut true
29837      */   
29838     
29839     isAutoInitial : true, 
29840     
29841     // private?
29842     gutter : 0,
29843     
29844     containerWidth: 0,
29845     initialColumnWidth : 0,
29846     currentSize : null,
29847     
29848     colYs : null, // array.
29849     maxY : 0,
29850     padWidth: 10,
29851     
29852     
29853     tag: 'div',
29854     cls: '',
29855     bricks: null, //CompositeElement
29856     cols : 0, // array?
29857     // element : null, // wrapped now this.el
29858     _isLayoutInited : null, 
29859     
29860     
29861     getAutoCreate : function(){
29862         
29863         var cfg = {
29864             tag: this.tag,
29865             cls: 'blog-masonary-wrapper ' + this.cls,
29866             cn : {
29867                 cls : 'mas-boxes masonary'
29868             }
29869         };
29870         
29871         return cfg;
29872     },
29873     
29874     getChildContainer: function( )
29875     {
29876         if (this.boxesEl) {
29877             return this.boxesEl;
29878         }
29879         
29880         this.boxesEl = this.el.select('.mas-boxes').first();
29881         
29882         return this.boxesEl;
29883     },
29884     
29885     
29886     initEvents : function()
29887     {
29888         var _this = this;
29889         
29890         if(this.isAutoInitial){
29891             Roo.log('hook children rendered');
29892             this.on('childrenrendered', function() {
29893                 Roo.log('children rendered');
29894                 _this.initial();
29895             } ,this);
29896         }
29897         
29898     },
29899     
29900     initial : function()
29901     {
29902         this.reloadItems();
29903
29904         this.currentSize = this.el.getBox(true);
29905
29906         /// was window resize... - let's see if this works..
29907         Roo.EventManager.onWindowResize(this.resize, this); 
29908
29909         if(!this.isAutoInitial){
29910             this.layout();
29911             return;
29912         }
29913         
29914         this.layout.defer(500,this);
29915     },
29916     
29917     reloadItems: function()
29918     {
29919         this.bricks = this.el.select('.masonry-brick', true);
29920         
29921         this.bricks.each(function(b) {
29922             //Roo.log(b.getSize());
29923             if (!b.attr('originalwidth')) {
29924                 b.attr('originalwidth',  b.getSize().width);
29925             }
29926             
29927         });
29928         
29929         Roo.log(this.bricks.elements.length);
29930     },
29931     
29932     resize : function()
29933     {
29934         Roo.log('resize');
29935         var cs = this.el.getBox(true);
29936         
29937         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29938             Roo.log("no change in with or X");
29939             return;
29940         }
29941         this.currentSize = cs;
29942         this.layout();
29943     },
29944     
29945     layout : function()
29946     {
29947          Roo.log('layout');
29948         this._resetLayout();
29949         //this._manageStamps();
29950       
29951         // don't animate first layout
29952         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29953         this.layoutItems( isInstant );
29954       
29955         // flag for initalized
29956         this._isLayoutInited = true;
29957     },
29958     
29959     layoutItems : function( isInstant )
29960     {
29961         //var items = this._getItemsForLayout( this.items );
29962         // original code supports filtering layout items.. we just ignore it..
29963         
29964         this._layoutItems( this.bricks , isInstant );
29965       
29966         this._postLayout();
29967     },
29968     _layoutItems : function ( items , isInstant)
29969     {
29970        //this.fireEvent( 'layout', this, items );
29971     
29972
29973         if ( !items || !items.elements.length ) {
29974           // no items, emit event with empty array
29975             return;
29976         }
29977
29978         var queue = [];
29979         items.each(function(item) {
29980             Roo.log("layout item");
29981             Roo.log(item);
29982             // get x/y object from method
29983             var position = this._getItemLayoutPosition( item );
29984             // enqueue
29985             position.item = item;
29986             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29987             queue.push( position );
29988         }, this);
29989       
29990         this._processLayoutQueue( queue );
29991     },
29992     /** Sets position of item in DOM
29993     * @param {Element} item
29994     * @param {Number} x - horizontal position
29995     * @param {Number} y - vertical position
29996     * @param {Boolean} isInstant - disables transitions
29997     */
29998     _processLayoutQueue : function( queue )
29999     {
30000         for ( var i=0, len = queue.length; i < len; i++ ) {
30001             var obj = queue[i];
30002             obj.item.position('absolute');
30003             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30004         }
30005     },
30006       
30007     
30008     /**
30009     * Any logic you want to do after each layout,
30010     * i.e. size the container
30011     */
30012     _postLayout : function()
30013     {
30014         this.resizeContainer();
30015     },
30016     
30017     resizeContainer : function()
30018     {
30019         if ( !this.isResizingContainer ) {
30020             return;
30021         }
30022         var size = this._getContainerSize();
30023         if ( size ) {
30024             this.el.setSize(size.width,size.height);
30025             this.boxesEl.setSize(size.width,size.height);
30026         }
30027     },
30028     
30029     
30030     
30031     _resetLayout : function()
30032     {
30033         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30034         this.colWidth = this.el.getWidth();
30035         //this.gutter = this.el.getWidth(); 
30036         
30037         this.measureColumns();
30038
30039         // reset column Y
30040         var i = this.cols;
30041         this.colYs = [];
30042         while (i--) {
30043             this.colYs.push( 0 );
30044         }
30045     
30046         this.maxY = 0;
30047     },
30048
30049     measureColumns : function()
30050     {
30051         this.getContainerWidth();
30052       // if columnWidth is 0, default to outerWidth of first item
30053         if ( !this.columnWidth ) {
30054             var firstItem = this.bricks.first();
30055             Roo.log(firstItem);
30056             this.columnWidth  = this.containerWidth;
30057             if (firstItem && firstItem.attr('originalwidth') ) {
30058                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30059             }
30060             // columnWidth fall back to item of first element
30061             Roo.log("set column width?");
30062                         this.initialColumnWidth = this.columnWidth  ;
30063
30064             // if first elem has no width, default to size of container
30065             
30066         }
30067         
30068         
30069         if (this.initialColumnWidth) {
30070             this.columnWidth = this.initialColumnWidth;
30071         }
30072         
30073         
30074             
30075         // column width is fixed at the top - however if container width get's smaller we should
30076         // reduce it...
30077         
30078         // this bit calcs how man columns..
30079             
30080         var columnWidth = this.columnWidth += this.gutter;
30081       
30082         // calculate columns
30083         var containerWidth = this.containerWidth + this.gutter;
30084         
30085         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30086         // fix rounding errors, typically with gutters
30087         var excess = columnWidth - containerWidth % columnWidth;
30088         
30089         
30090         // if overshoot is less than a pixel, round up, otherwise floor it
30091         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30092         cols = Math[ mathMethod ]( cols );
30093         this.cols = Math.max( cols, 1 );
30094         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30095         
30096          // padding positioning..
30097         var totalColWidth = this.cols * this.columnWidth;
30098         var padavail = this.containerWidth - totalColWidth;
30099         // so for 2 columns - we need 3 'pads'
30100         
30101         var padNeeded = (1+this.cols) * this.padWidth;
30102         
30103         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30104         
30105         this.columnWidth += padExtra
30106         //this.padWidth = Math.floor(padavail /  ( this.cols));
30107         
30108         // adjust colum width so that padding is fixed??
30109         
30110         // we have 3 columns ... total = width * 3
30111         // we have X left over... that should be used by 
30112         
30113         //if (this.expandC) {
30114             
30115         //}
30116         
30117         
30118         
30119     },
30120     
30121     getContainerWidth : function()
30122     {
30123        /* // container is parent if fit width
30124         var container = this.isFitWidth ? this.element.parentNode : this.element;
30125         // check that this.size and size are there
30126         // IE8 triggers resize on body size change, so they might not be
30127         
30128         var size = getSize( container );  //FIXME
30129         this.containerWidth = size && size.innerWidth; //FIXME
30130         */
30131          
30132         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30133         
30134     },
30135     
30136     _getItemLayoutPosition : function( item )  // what is item?
30137     {
30138         // we resize the item to our columnWidth..
30139       
30140         item.setWidth(this.columnWidth);
30141         item.autoBoxAdjust  = false;
30142         
30143         var sz = item.getSize();
30144  
30145         // how many columns does this brick span
30146         var remainder = this.containerWidth % this.columnWidth;
30147         
30148         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30149         // round if off by 1 pixel, otherwise use ceil
30150         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30151         colSpan = Math.min( colSpan, this.cols );
30152         
30153         // normally this should be '1' as we dont' currently allow multi width columns..
30154         
30155         var colGroup = this._getColGroup( colSpan );
30156         // get the minimum Y value from the columns
30157         var minimumY = Math.min.apply( Math, colGroup );
30158         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30159         
30160         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30161          
30162         // position the brick
30163         var position = {
30164             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30165             y: this.currentSize.y + minimumY + this.padHeight
30166         };
30167         
30168         Roo.log(position);
30169         // apply setHeight to necessary columns
30170         var setHeight = minimumY + sz.height + this.padHeight;
30171         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30172         
30173         var setSpan = this.cols + 1 - colGroup.length;
30174         for ( var i = 0; i < setSpan; i++ ) {
30175           this.colYs[ shortColIndex + i ] = setHeight ;
30176         }
30177       
30178         return position;
30179     },
30180     
30181     /**
30182      * @param {Number} colSpan - number of columns the element spans
30183      * @returns {Array} colGroup
30184      */
30185     _getColGroup : function( colSpan )
30186     {
30187         if ( colSpan < 2 ) {
30188           // if brick spans only one column, use all the column Ys
30189           return this.colYs;
30190         }
30191       
30192         var colGroup = [];
30193         // how many different places could this brick fit horizontally
30194         var groupCount = this.cols + 1 - colSpan;
30195         // for each group potential horizontal position
30196         for ( var i = 0; i < groupCount; i++ ) {
30197           // make an array of colY values for that one group
30198           var groupColYs = this.colYs.slice( i, i + colSpan );
30199           // and get the max value of the array
30200           colGroup[i] = Math.max.apply( Math, groupColYs );
30201         }
30202         return colGroup;
30203     },
30204     /*
30205     _manageStamp : function( stamp )
30206     {
30207         var stampSize =  stamp.getSize();
30208         var offset = stamp.getBox();
30209         // get the columns that this stamp affects
30210         var firstX = this.isOriginLeft ? offset.x : offset.right;
30211         var lastX = firstX + stampSize.width;
30212         var firstCol = Math.floor( firstX / this.columnWidth );
30213         firstCol = Math.max( 0, firstCol );
30214         
30215         var lastCol = Math.floor( lastX / this.columnWidth );
30216         // lastCol should not go over if multiple of columnWidth #425
30217         lastCol -= lastX % this.columnWidth ? 0 : 1;
30218         lastCol = Math.min( this.cols - 1, lastCol );
30219         
30220         // set colYs to bottom of the stamp
30221         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30222             stampSize.height;
30223             
30224         for ( var i = firstCol; i <= lastCol; i++ ) {
30225           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30226         }
30227     },
30228     */
30229     
30230     _getContainerSize : function()
30231     {
30232         this.maxY = Math.max.apply( Math, this.colYs );
30233         var size = {
30234             height: this.maxY
30235         };
30236       
30237         if ( this.isFitWidth ) {
30238             size.width = this._getContainerFitWidth();
30239         }
30240       
30241         return size;
30242     },
30243     
30244     _getContainerFitWidth : function()
30245     {
30246         var unusedCols = 0;
30247         // count unused columns
30248         var i = this.cols;
30249         while ( --i ) {
30250           if ( this.colYs[i] !== 0 ) {
30251             break;
30252           }
30253           unusedCols++;
30254         }
30255         // fit container to columns that have been used
30256         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30257     },
30258     
30259     needsResizeLayout : function()
30260     {
30261         var previousWidth = this.containerWidth;
30262         this.getContainerWidth();
30263         return previousWidth !== this.containerWidth;
30264     }
30265  
30266 });
30267
30268  
30269
30270  /*
30271  * - LGPL
30272  *
30273  * element
30274  * 
30275  */
30276
30277 /**
30278  * @class Roo.bootstrap.MasonryBrick
30279  * @extends Roo.bootstrap.Component
30280  * Bootstrap MasonryBrick class
30281  * 
30282  * @constructor
30283  * Create a new MasonryBrick
30284  * @param {Object} config The config object
30285  */
30286
30287 Roo.bootstrap.MasonryBrick = function(config){
30288     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30289     
30290     this.addEvents({
30291         // raw events
30292         /**
30293          * @event click
30294          * When a MasonryBrick is clcik
30295          * @param {Roo.bootstrap.MasonryBrick} this
30296          * @param {Roo.EventObject} e
30297          */
30298         "click" : true
30299     });
30300 };
30301
30302 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30303     
30304     /**
30305      * @cfg {String} title
30306      */   
30307     title : '',
30308     /**
30309      * @cfg {String} html
30310      */   
30311     html : '',
30312     /**
30313      * @cfg {String} bgimage
30314      */   
30315     bgimage : '',
30316     /**
30317      * @cfg {String} videourl
30318      */   
30319     videourl : '',
30320     /**
30321      * @cfg {String} cls
30322      */   
30323     cls : '',
30324     /**
30325      * @cfg {String} href
30326      */   
30327     href : '',
30328     /**
30329      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30330      */   
30331     size : 'xs',
30332     
30333     /**
30334      * @cfg {String} (center|bottom) placetitle
30335      */   
30336     placetitle : '',
30337     
30338     getAutoCreate : function()
30339     {
30340         var cls = 'masonry-brick';
30341         
30342         if(this.href.length){
30343             cls += ' masonry-brick-link';
30344         }
30345         
30346         if(this.bgimage.length){
30347             cls += ' masonry-brick-image';
30348         }
30349         
30350         if(this.size){
30351             cls += ' masonry-' + this.size + '-brick';
30352         }
30353         
30354         if(this.placetitle.length){
30355             
30356             switch (this.placetitle) {
30357                 case 'center' :
30358                     cls += ' masonry-center-title';
30359                     break;
30360                 case 'bottom' :
30361                     cls += ' masonry-bottom-title';
30362                     break;
30363                 default:
30364                     break;
30365             }
30366             
30367         } else {
30368             if(!this.html.length && !this.bgimage.length){
30369                 cls += ' masonry-center-title';
30370             }
30371
30372             if(!this.html.length && this.bgimage.length){
30373                 cls += ' masonry-bottom-title';
30374             }
30375         }
30376         
30377         if(this.cls){
30378             cls += ' ' + this.cls;
30379         }
30380         
30381         var cfg = {
30382             tag: (this.href.length) ? 'a' : 'div',
30383             cls: cls,
30384             cn: [
30385                 {
30386                     tag: 'div',
30387                     cls: 'masonry-brick-paragraph',
30388                     cn: []
30389                 }
30390             ]
30391         };
30392         
30393         if(this.href.length){
30394             cfg.href = this.href;
30395         }
30396         
30397         var cn = cfg.cn[0].cn;
30398         
30399         if(this.title.length){
30400             cn.push({
30401                 tag: 'h4',
30402                 cls: 'masonry-brick-title',
30403                 html: this.title
30404             });
30405         }
30406         
30407         if(this.html.length){
30408             cn.push({
30409                 tag: 'p',
30410                 cls: 'masonry-brick-text',
30411                 html: this.html
30412             });
30413         }  
30414         if (!this.title.length && !this.html.length) {
30415             cfg.cn[0].cls += ' hide';
30416         }
30417         
30418         if(this.bgimage.length){
30419             cfg.cn.push({
30420                 tag: 'img',
30421                 cls: 'masonry-brick-image-view',
30422                 src: this.bgimage
30423             });
30424         }
30425         if(this.videourl.length){
30426             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30427             // youtube support only?
30428             cfg.cn.push({
30429                 tag: 'iframe',
30430                 cls: 'masonry-brick-image-view',
30431                 src: vurl,
30432                 frameborder : 0,
30433                 allowfullscreen : true
30434             });
30435             
30436             
30437         }
30438         return cfg;
30439         
30440     },
30441     
30442     initEvents: function() 
30443     {
30444         switch (this.size) {
30445             case 'xs' :
30446 //                this.intSize = 1;
30447                 this.x = 1;
30448                 this.y = 1;
30449                 break;
30450             case 'sm' :
30451 //                this.intSize = 2;
30452                 this.x = 2;
30453                 this.y = 2;
30454                 break;
30455             case 'md' :
30456             case 'md-left' :
30457             case 'md-right' :
30458 //                this.intSize = 3;
30459                 this.x = 3;
30460                 this.y = 3;
30461                 break;
30462             case 'tall' :
30463 //                this.intSize = 3;
30464                 this.x = 2;
30465                 this.y = 3;
30466                 break;
30467             case 'wide' :
30468 //                this.intSize = 3;
30469                 this.x = 3;
30470                 this.y = 2;
30471                 break;
30472             case 'wide-thin' :
30473 //                this.intSize = 3;
30474                 this.x = 3;
30475                 this.y = 1;
30476                 break;
30477                         
30478             default :
30479                 break;
30480         }
30481         
30482         
30483         
30484         if(Roo.isTouch){
30485             this.el.on('touchstart', this.onTouchStart, this);
30486             this.el.on('touchmove', this.onTouchMove, this);
30487             this.el.on('touchend', this.onTouchEnd, this);
30488             this.el.on('contextmenu', this.onContextMenu, this);
30489         } else {
30490             this.el.on('mouseenter'  ,this.enter, this);
30491             this.el.on('mouseleave', this.leave, this);
30492         }
30493         
30494         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30495             this.parent().bricks.push(this);   
30496         }
30497         
30498     },
30499     
30500     onClick: function(e, el)
30501     {
30502         if(!Roo.isTouch){
30503             return;
30504         }
30505         
30506         var time = this.endTimer - this.startTimer;
30507         
30508         //alert(time);
30509         
30510         if(time < 1000){
30511             return;
30512         }
30513         
30514         e.preventDefault();
30515     },
30516     
30517     enter: function(e, el)
30518     {
30519         e.preventDefault();
30520         
30521         if(this.bgimage.length && this.html.length){
30522             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30523         }
30524     },
30525     
30526     leave: function(e, el)
30527     {
30528         e.preventDefault();
30529         
30530         if(this.bgimage.length && this.html.length){
30531             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30532         }
30533     },
30534     
30535     onTouchStart: function(e, el)
30536     {
30537 //        e.preventDefault();
30538         
30539         this.touchmoved = false;
30540         
30541         if(!this.bgimage.length || !this.html.length){
30542             return;
30543         }
30544         
30545         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30546         
30547         this.timer = new Date().getTime();
30548         
30549     },
30550     
30551     onTouchMove: function(e, el)
30552     {
30553         this.touchmoved = true;
30554     },
30555     
30556     onContextMenu : function(e,el)
30557     {
30558         e.preventDefault();
30559         e.stopPropagation();
30560         return false;
30561     },
30562     
30563     onTouchEnd: function(e, el)
30564     {
30565 //        e.preventDefault();
30566         
30567         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30568         
30569             this.leave(e,el);
30570             
30571             return;
30572         }
30573         
30574         if(!this.bgimage.length || !this.html.length){
30575             
30576             if(this.href.length){
30577                 window.location.href = this.href;
30578             }
30579             
30580             return;
30581         }
30582         
30583         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30584         
30585         window.location.href = this.href;
30586     }
30587     
30588 });
30589
30590  
30591
30592  /*
30593  * - LGPL
30594  *
30595  * element
30596  * 
30597  */
30598
30599 /**
30600  * @class Roo.bootstrap.Brick
30601  * @extends Roo.bootstrap.Component
30602  * Bootstrap Brick class
30603  * 
30604  * @constructor
30605  * Create a new Brick
30606  * @param {Object} config The config object
30607  */
30608
30609 Roo.bootstrap.Brick = function(config){
30610     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30611     
30612     this.addEvents({
30613         // raw events
30614         /**
30615          * @event click
30616          * When a Brick is click
30617          * @param {Roo.bootstrap.Brick} this
30618          * @param {Roo.EventObject} e
30619          */
30620         "click" : true
30621     });
30622 };
30623
30624 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30625     
30626     /**
30627      * @cfg {String} title
30628      */   
30629     title : '',
30630     /**
30631      * @cfg {String} html
30632      */   
30633     html : '',
30634     /**
30635      * @cfg {String} bgimage
30636      */   
30637     bgimage : '',
30638     /**
30639      * @cfg {String} cls
30640      */   
30641     cls : '',
30642     /**
30643      * @cfg {String} href
30644      */   
30645     href : '',
30646     /**
30647      * @cfg {String} video
30648      */   
30649     video : '',
30650     /**
30651      * @cfg {Boolean} square
30652      */   
30653     square : true,
30654     
30655     getAutoCreate : function()
30656     {
30657         var cls = 'roo-brick';
30658         
30659         if(this.href.length){
30660             cls += ' roo-brick-link';
30661         }
30662         
30663         if(this.bgimage.length){
30664             cls += ' roo-brick-image';
30665         }
30666         
30667         if(!this.html.length && !this.bgimage.length){
30668             cls += ' roo-brick-center-title';
30669         }
30670         
30671         if(!this.html.length && this.bgimage.length){
30672             cls += ' roo-brick-bottom-title';
30673         }
30674         
30675         if(this.cls){
30676             cls += ' ' + this.cls;
30677         }
30678         
30679         var cfg = {
30680             tag: (this.href.length) ? 'a' : 'div',
30681             cls: cls,
30682             cn: [
30683                 {
30684                     tag: 'div',
30685                     cls: 'roo-brick-paragraph',
30686                     cn: []
30687                 }
30688             ]
30689         };
30690         
30691         if(this.href.length){
30692             cfg.href = this.href;
30693         }
30694         
30695         var cn = cfg.cn[0].cn;
30696         
30697         if(this.title.length){
30698             cn.push({
30699                 tag: 'h4',
30700                 cls: 'roo-brick-title',
30701                 html: this.title
30702             });
30703         }
30704         
30705         if(this.html.length){
30706             cn.push({
30707                 tag: 'p',
30708                 cls: 'roo-brick-text',
30709                 html: this.html
30710             });
30711         } else {
30712             cn.cls += ' hide';
30713         }
30714         
30715         if(this.bgimage.length){
30716             cfg.cn.push({
30717                 tag: 'img',
30718                 cls: 'roo-brick-image-view',
30719                 src: this.bgimage
30720             });
30721         }
30722         
30723         return cfg;
30724     },
30725     
30726     initEvents: function() 
30727     {
30728         if(this.title.length || this.html.length){
30729             this.el.on('mouseenter'  ,this.enter, this);
30730             this.el.on('mouseleave', this.leave, this);
30731         }
30732         
30733         
30734         Roo.EventManager.onWindowResize(this.resize, this); 
30735         
30736         this.resize();
30737     },
30738     
30739     resize : function()
30740     {
30741         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30742         
30743         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30744 //        paragraph.setHeight(paragraph.getWidth());
30745         
30746         if(this.bgimage.length){
30747             var image = this.el.select('.roo-brick-image-view', true).first();
30748             image.setWidth(paragraph.getWidth());
30749             image.setHeight(paragraph.getWidth());
30750         }
30751         
30752     },
30753     
30754     enter: function(e, el)
30755     {
30756         e.preventDefault();
30757         
30758         if(this.bgimage.length){
30759             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30760             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30761         }
30762     },
30763     
30764     leave: function(e, el)
30765     {
30766         e.preventDefault();
30767         
30768         if(this.bgimage.length){
30769             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30770             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30771         }
30772     }
30773     
30774 });
30775
30776  
30777
30778  /*
30779  * Based on:
30780  * Ext JS Library 1.1.1
30781  * Copyright(c) 2006-2007, Ext JS, LLC.
30782  *
30783  * Originally Released Under LGPL - original licence link has changed is not relivant.
30784  *
30785  * Fork - LGPL
30786  * <script type="text/javascript">
30787  */
30788
30789
30790 /**
30791  * @class Roo.bootstrap.SplitBar
30792  * @extends Roo.util.Observable
30793  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30794  * <br><br>
30795  * Usage:
30796  * <pre><code>
30797 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30798                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30799 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30800 split.minSize = 100;
30801 split.maxSize = 600;
30802 split.animate = true;
30803 split.on('moved', splitterMoved);
30804 </code></pre>
30805  * @constructor
30806  * Create a new SplitBar
30807  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
30808  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
30809  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30810  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
30811                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30812                         position of the SplitBar).
30813  */
30814 Roo.bootstrap.SplitBar = function(cfg){
30815     
30816     /** @private */
30817     
30818     //{
30819     //  dragElement : elm
30820     //  resizingElement: el,
30821         // optional..
30822     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30823     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
30824         // existingProxy ???
30825     //}
30826     
30827     this.el = Roo.get(cfg.dragElement, true);
30828     this.el.dom.unselectable = "on";
30829     /** @private */
30830     this.resizingEl = Roo.get(cfg.resizingElement, true);
30831
30832     /**
30833      * @private
30834      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30835      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30836      * @type Number
30837      */
30838     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30839     
30840     /**
30841      * The minimum size of the resizing element. (Defaults to 0)
30842      * @type Number
30843      */
30844     this.minSize = 0;
30845     
30846     /**
30847      * The maximum size of the resizing element. (Defaults to 2000)
30848      * @type Number
30849      */
30850     this.maxSize = 2000;
30851     
30852     /**
30853      * Whether to animate the transition to the new size
30854      * @type Boolean
30855      */
30856     this.animate = false;
30857     
30858     /**
30859      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30860      * @type Boolean
30861      */
30862     this.useShim = false;
30863     
30864     /** @private */
30865     this.shim = null;
30866     
30867     if(!cfg.existingProxy){
30868         /** @private */
30869         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30870     }else{
30871         this.proxy = Roo.get(cfg.existingProxy).dom;
30872     }
30873     /** @private */
30874     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30875     
30876     /** @private */
30877     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30878     
30879     /** @private */
30880     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30881     
30882     /** @private */
30883     this.dragSpecs = {};
30884     
30885     /**
30886      * @private The adapter to use to positon and resize elements
30887      */
30888     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30889     this.adapter.init(this);
30890     
30891     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30892         /** @private */
30893         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30894         this.el.addClass("roo-splitbar-h");
30895     }else{
30896         /** @private */
30897         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30898         this.el.addClass("roo-splitbar-v");
30899     }
30900     
30901     this.addEvents({
30902         /**
30903          * @event resize
30904          * Fires when the splitter is moved (alias for {@link #event-moved})
30905          * @param {Roo.bootstrap.SplitBar} this
30906          * @param {Number} newSize the new width or height
30907          */
30908         "resize" : true,
30909         /**
30910          * @event moved
30911          * Fires when the splitter is moved
30912          * @param {Roo.bootstrap.SplitBar} this
30913          * @param {Number} newSize the new width or height
30914          */
30915         "moved" : true,
30916         /**
30917          * @event beforeresize
30918          * Fires before the splitter is dragged
30919          * @param {Roo.bootstrap.SplitBar} this
30920          */
30921         "beforeresize" : true,
30922
30923         "beforeapply" : true
30924     });
30925
30926     Roo.util.Observable.call(this);
30927 };
30928
30929 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30930     onStartProxyDrag : function(x, y){
30931         this.fireEvent("beforeresize", this);
30932         if(!this.overlay){
30933             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
30934             o.unselectable();
30935             o.enableDisplayMode("block");
30936             // all splitbars share the same overlay
30937             Roo.bootstrap.SplitBar.prototype.overlay = o;
30938         }
30939         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30940         this.overlay.show();
30941         Roo.get(this.proxy).setDisplayed("block");
30942         var size = this.adapter.getElementSize(this);
30943         this.activeMinSize = this.getMinimumSize();;
30944         this.activeMaxSize = this.getMaximumSize();;
30945         var c1 = size - this.activeMinSize;
30946         var c2 = Math.max(this.activeMaxSize - size, 0);
30947         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30948             this.dd.resetConstraints();
30949             this.dd.setXConstraint(
30950                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
30951                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30952             );
30953             this.dd.setYConstraint(0, 0);
30954         }else{
30955             this.dd.resetConstraints();
30956             this.dd.setXConstraint(0, 0);
30957             this.dd.setYConstraint(
30958                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
30959                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30960             );
30961          }
30962         this.dragSpecs.startSize = size;
30963         this.dragSpecs.startPoint = [x, y];
30964         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30965     },
30966     
30967     /** 
30968      * @private Called after the drag operation by the DDProxy
30969      */
30970     onEndProxyDrag : function(e){
30971         Roo.get(this.proxy).setDisplayed(false);
30972         var endPoint = Roo.lib.Event.getXY(e);
30973         if(this.overlay){
30974             this.overlay.hide();
30975         }
30976         var newSize;
30977         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30978             newSize = this.dragSpecs.startSize + 
30979                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30980                     endPoint[0] - this.dragSpecs.startPoint[0] :
30981                     this.dragSpecs.startPoint[0] - endPoint[0]
30982                 );
30983         }else{
30984             newSize = this.dragSpecs.startSize + 
30985                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30986                     endPoint[1] - this.dragSpecs.startPoint[1] :
30987                     this.dragSpecs.startPoint[1] - endPoint[1]
30988                 );
30989         }
30990         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30991         if(newSize != this.dragSpecs.startSize){
30992             if(this.fireEvent('beforeapply', this, newSize) !== false){
30993                 this.adapter.setElementSize(this, newSize);
30994                 this.fireEvent("moved", this, newSize);
30995                 this.fireEvent("resize", this, newSize);
30996             }
30997         }
30998     },
30999     
31000     /**
31001      * Get the adapter this SplitBar uses
31002      * @return The adapter object
31003      */
31004     getAdapter : function(){
31005         return this.adapter;
31006     },
31007     
31008     /**
31009      * Set the adapter this SplitBar uses
31010      * @param {Object} adapter A SplitBar adapter object
31011      */
31012     setAdapter : function(adapter){
31013         this.adapter = adapter;
31014         this.adapter.init(this);
31015     },
31016     
31017     /**
31018      * Gets the minimum size for the resizing element
31019      * @return {Number} The minimum size
31020      */
31021     getMinimumSize : function(){
31022         return this.minSize;
31023     },
31024     
31025     /**
31026      * Sets the minimum size for the resizing element
31027      * @param {Number} minSize The minimum size
31028      */
31029     setMinimumSize : function(minSize){
31030         this.minSize = minSize;
31031     },
31032     
31033     /**
31034      * Gets the maximum size for the resizing element
31035      * @return {Number} The maximum size
31036      */
31037     getMaximumSize : function(){
31038         return this.maxSize;
31039     },
31040     
31041     /**
31042      * Sets the maximum size for the resizing element
31043      * @param {Number} maxSize The maximum size
31044      */
31045     setMaximumSize : function(maxSize){
31046         this.maxSize = maxSize;
31047     },
31048     
31049     /**
31050      * Sets the initialize size for the resizing element
31051      * @param {Number} size The initial size
31052      */
31053     setCurrentSize : function(size){
31054         var oldAnimate = this.animate;
31055         this.animate = false;
31056         this.adapter.setElementSize(this, size);
31057         this.animate = oldAnimate;
31058     },
31059     
31060     /**
31061      * Destroy this splitbar. 
31062      * @param {Boolean} removeEl True to remove the element
31063      */
31064     destroy : function(removeEl){
31065         if(this.shim){
31066             this.shim.remove();
31067         }
31068         this.dd.unreg();
31069         this.proxy.parentNode.removeChild(this.proxy);
31070         if(removeEl){
31071             this.el.remove();
31072         }
31073     }
31074 });
31075
31076 /**
31077  * @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.
31078  */
31079 Roo.bootstrap.SplitBar.createProxy = function(dir){
31080     var proxy = new Roo.Element(document.createElement("div"));
31081     proxy.unselectable();
31082     var cls = 'roo-splitbar-proxy';
31083     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31084     document.body.appendChild(proxy.dom);
31085     return proxy.dom;
31086 };
31087
31088 /** 
31089  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31090  * Default Adapter. It assumes the splitter and resizing element are not positioned
31091  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31092  */
31093 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31094 };
31095
31096 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31097     // do nothing for now
31098     init : function(s){
31099     
31100     },
31101     /**
31102      * Called before drag operations to get the current size of the resizing element. 
31103      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31104      */
31105      getElementSize : function(s){
31106         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31107             return s.resizingEl.getWidth();
31108         }else{
31109             return s.resizingEl.getHeight();
31110         }
31111     },
31112     
31113     /**
31114      * Called after drag operations to set the size of the resizing element.
31115      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31116      * @param {Number} newSize The new size to set
31117      * @param {Function} onComplete A function to be invoked when resizing is complete
31118      */
31119     setElementSize : function(s, newSize, onComplete){
31120         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31121             if(!s.animate){
31122                 s.resizingEl.setWidth(newSize);
31123                 if(onComplete){
31124                     onComplete(s, newSize);
31125                 }
31126             }else{
31127                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31128             }
31129         }else{
31130             
31131             if(!s.animate){
31132                 s.resizingEl.setHeight(newSize);
31133                 if(onComplete){
31134                     onComplete(s, newSize);
31135                 }
31136             }else{
31137                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31138             }
31139         }
31140     }
31141 };
31142
31143 /** 
31144  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31145  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31146  * Adapter that  moves the splitter element to align with the resized sizing element. 
31147  * Used with an absolute positioned SplitBar.
31148  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31149  * document.body, make sure you assign an id to the body element.
31150  */
31151 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31152     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31153     this.container = Roo.get(container);
31154 };
31155
31156 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31157     init : function(s){
31158         this.basic.init(s);
31159     },
31160     
31161     getElementSize : function(s){
31162         return this.basic.getElementSize(s);
31163     },
31164     
31165     setElementSize : function(s, newSize, onComplete){
31166         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31167     },
31168     
31169     moveSplitter : function(s){
31170         var yes = Roo.bootstrap.SplitBar;
31171         switch(s.placement){
31172             case yes.LEFT:
31173                 s.el.setX(s.resizingEl.getRight());
31174                 break;
31175             case yes.RIGHT:
31176                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31177                 break;
31178             case yes.TOP:
31179                 s.el.setY(s.resizingEl.getBottom());
31180                 break;
31181             case yes.BOTTOM:
31182                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31183                 break;
31184         }
31185     }
31186 };
31187
31188 /**
31189  * Orientation constant - Create a vertical SplitBar
31190  * @static
31191  * @type Number
31192  */
31193 Roo.bootstrap.SplitBar.VERTICAL = 1;
31194
31195 /**
31196  * Orientation constant - Create a horizontal SplitBar
31197  * @static
31198  * @type Number
31199  */
31200 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31201
31202 /**
31203  * Placement constant - The resizing element is to the left of the splitter element
31204  * @static
31205  * @type Number
31206  */
31207 Roo.bootstrap.SplitBar.LEFT = 1;
31208
31209 /**
31210  * Placement constant - The resizing element is to the right of the splitter element
31211  * @static
31212  * @type Number
31213  */
31214 Roo.bootstrap.SplitBar.RIGHT = 2;
31215
31216 /**
31217  * Placement constant - The resizing element is positioned above the splitter element
31218  * @static
31219  * @type Number
31220  */
31221 Roo.bootstrap.SplitBar.TOP = 3;
31222
31223 /**
31224  * Placement constant - The resizing element is positioned under splitter element
31225  * @static
31226  * @type Number
31227  */
31228 Roo.bootstrap.SplitBar.BOTTOM = 4;
31229 Roo.namespace("Roo.bootstrap.layout");/*
31230  * Based on:
31231  * Ext JS Library 1.1.1
31232  * Copyright(c) 2006-2007, Ext JS, LLC.
31233  *
31234  * Originally Released Under LGPL - original licence link has changed is not relivant.
31235  *
31236  * Fork - LGPL
31237  * <script type="text/javascript">
31238  */
31239  
31240 /**
31241  * @class Roo.bootstrap.layout.Manager
31242  * @extends Roo.bootstrap.Component
31243  * Base class for layout managers.
31244  */
31245 Roo.bootstrap.layout.Manager = function(config)
31246 {
31247     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31248     
31249     
31250      
31251     
31252     
31253     /** false to disable window resize monitoring @type Boolean */
31254     this.monitorWindowResize = true;
31255     this.regions = {};
31256     this.addEvents({
31257         /**
31258          * @event layout
31259          * Fires when a layout is performed. 
31260          * @param {Roo.LayoutManager} this
31261          */
31262         "layout" : true,
31263         /**
31264          * @event regionresized
31265          * Fires when the user resizes a region. 
31266          * @param {Roo.LayoutRegion} region The resized region
31267          * @param {Number} newSize The new size (width for east/west, height for north/south)
31268          */
31269         "regionresized" : true,
31270         /**
31271          * @event regioncollapsed
31272          * Fires when a region is collapsed. 
31273          * @param {Roo.LayoutRegion} region The collapsed region
31274          */
31275         "regioncollapsed" : true,
31276         /**
31277          * @event regionexpanded
31278          * Fires when a region is expanded.  
31279          * @param {Roo.LayoutRegion} region The expanded region
31280          */
31281         "regionexpanded" : true
31282     });
31283     this.updating = false;
31284     
31285     if (config.el) {
31286         this.el = Roo.get(config.el);
31287         this.initEvents();
31288     }
31289     
31290 };
31291
31292 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31293     
31294     
31295     regions : null,
31296     
31297     monitorWindowResize : true,
31298     
31299     
31300     updating : false,
31301     
31302     
31303     onRender : function(ct, position)
31304     {
31305         if(!this.el){
31306             this.el = Roo.get(ct);
31307             this.initEvents();
31308         }
31309     },
31310     
31311     
31312     initEvents: function()
31313     {
31314         
31315         
31316         // ie scrollbar fix
31317         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31318             document.body.scroll = "no";
31319         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31320             this.el.position('relative');
31321         }
31322         this.id = this.el.id;
31323         this.el.addClass("roo-layout-container");
31324         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31325         if(this.el.dom != document.body ) {
31326             this.el.on('resize', this.layout,this);
31327             this.el.on('show', this.layout,this);
31328         }
31329
31330     },
31331     
31332     /**
31333      * Returns true if this layout is currently being updated
31334      * @return {Boolean}
31335      */
31336     isUpdating : function(){
31337         return this.updating; 
31338     },
31339     
31340     /**
31341      * Suspend the LayoutManager from doing auto-layouts while
31342      * making multiple add or remove calls
31343      */
31344     beginUpdate : function(){
31345         this.updating = true;    
31346     },
31347     
31348     /**
31349      * Restore auto-layouts and optionally disable the manager from performing a layout
31350      * @param {Boolean} noLayout true to disable a layout update 
31351      */
31352     endUpdate : function(noLayout){
31353         this.updating = false;
31354         if(!noLayout){
31355             this.layout();
31356         }    
31357     },
31358     
31359     layout: function(){
31360         // abstract...
31361     },
31362     
31363     onRegionResized : function(region, newSize){
31364         this.fireEvent("regionresized", region, newSize);
31365         this.layout();
31366     },
31367     
31368     onRegionCollapsed : function(region){
31369         this.fireEvent("regioncollapsed", region);
31370     },
31371     
31372     onRegionExpanded : function(region){
31373         this.fireEvent("regionexpanded", region);
31374     },
31375         
31376     /**
31377      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31378      * performs box-model adjustments.
31379      * @return {Object} The size as an object {width: (the width), height: (the height)}
31380      */
31381     getViewSize : function()
31382     {
31383         var size;
31384         if(this.el.dom != document.body){
31385             size = this.el.getSize();
31386         }else{
31387             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31388         }
31389         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31390         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31391         return size;
31392     },
31393     
31394     /**
31395      * Returns the Element this layout is bound to.
31396      * @return {Roo.Element}
31397      */
31398     getEl : function(){
31399         return this.el;
31400     },
31401     
31402     /**
31403      * Returns the specified region.
31404      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31405      * @return {Roo.LayoutRegion}
31406      */
31407     getRegion : function(target){
31408         return this.regions[target.toLowerCase()];
31409     },
31410     
31411     onWindowResize : function(){
31412         if(this.monitorWindowResize){
31413             this.layout();
31414         }
31415     }
31416 });/*
31417  * Based on:
31418  * Ext JS Library 1.1.1
31419  * Copyright(c) 2006-2007, Ext JS, LLC.
31420  *
31421  * Originally Released Under LGPL - original licence link has changed is not relivant.
31422  *
31423  * Fork - LGPL
31424  * <script type="text/javascript">
31425  */
31426 /**
31427  * @class Roo.bootstrap.layout.Border
31428  * @extends Roo.bootstrap.layout.Manager
31429  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31430  * please see: examples/bootstrap/nested.html<br><br>
31431  
31432 <b>The container the layout is rendered into can be either the body element or any other element.
31433 If it is not the body element, the container needs to either be an absolute positioned element,
31434 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31435 the container size if it is not the body element.</b>
31436
31437 * @constructor
31438 * Create a new Border
31439 * @param {Object} config Configuration options
31440  */
31441 Roo.bootstrap.layout.Border = function(config){
31442     config = config || {};
31443     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31444     
31445     
31446     
31447     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31448         if(config[region]){
31449             config[region].region = region;
31450             this.addRegion(config[region]);
31451         }
31452     },this);
31453     
31454 };
31455
31456 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31457
31458 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31459     /**
31460      * Creates and adds a new region if it doesn't already exist.
31461      * @param {String} target The target region key (north, south, east, west or center).
31462      * @param {Object} config The regions config object
31463      * @return {BorderLayoutRegion} The new region
31464      */
31465     addRegion : function(config)
31466     {
31467         if(!this.regions[config.region]){
31468             var r = this.factory(config);
31469             this.bindRegion(r);
31470         }
31471         return this.regions[config.region];
31472     },
31473
31474     // private (kinda)
31475     bindRegion : function(r){
31476         this.regions[r.config.region] = r;
31477         
31478         r.on("visibilitychange",    this.layout, this);
31479         r.on("paneladded",          this.layout, this);
31480         r.on("panelremoved",        this.layout, this);
31481         r.on("invalidated",         this.layout, this);
31482         r.on("resized",             this.onRegionResized, this);
31483         r.on("collapsed",           this.onRegionCollapsed, this);
31484         r.on("expanded",            this.onRegionExpanded, this);
31485     },
31486
31487     /**
31488      * Performs a layout update.
31489      */
31490     layout : function()
31491     {
31492         if(this.updating) {
31493             return;
31494         }
31495         var size = this.getViewSize();
31496         var w = size.width;
31497         var h = size.height;
31498         var centerW = w;
31499         var centerH = h;
31500         var centerY = 0;
31501         var centerX = 0;
31502         //var x = 0, y = 0;
31503
31504         var rs = this.regions;
31505         var north = rs["north"];
31506         var south = rs["south"]; 
31507         var west = rs["west"];
31508         var east = rs["east"];
31509         var center = rs["center"];
31510         //if(this.hideOnLayout){ // not supported anymore
31511             //c.el.setStyle("display", "none");
31512         //}
31513         if(north && north.isVisible()){
31514             var b = north.getBox();
31515             var m = north.getMargins();
31516             b.width = w - (m.left+m.right);
31517             b.x = m.left;
31518             b.y = m.top;
31519             centerY = b.height + b.y + m.bottom;
31520             centerH -= centerY;
31521             north.updateBox(this.safeBox(b));
31522         }
31523         if(south && south.isVisible()){
31524             var b = south.getBox();
31525             var m = south.getMargins();
31526             b.width = w - (m.left+m.right);
31527             b.x = m.left;
31528             var totalHeight = (b.height + m.top + m.bottom);
31529             b.y = h - totalHeight + m.top;
31530             centerH -= totalHeight;
31531             south.updateBox(this.safeBox(b));
31532         }
31533         if(west && west.isVisible()){
31534             var b = west.getBox();
31535             var m = west.getMargins();
31536             b.height = centerH - (m.top+m.bottom);
31537             b.x = m.left;
31538             b.y = centerY + m.top;
31539             var totalWidth = (b.width + m.left + m.right);
31540             centerX += totalWidth;
31541             centerW -= totalWidth;
31542             west.updateBox(this.safeBox(b));
31543         }
31544         if(east && east.isVisible()){
31545             var b = east.getBox();
31546             var m = east.getMargins();
31547             b.height = centerH - (m.top+m.bottom);
31548             var totalWidth = (b.width + m.left + m.right);
31549             b.x = w - totalWidth + m.left;
31550             b.y = centerY + m.top;
31551             centerW -= totalWidth;
31552             east.updateBox(this.safeBox(b));
31553         }
31554         if(center){
31555             var m = center.getMargins();
31556             var centerBox = {
31557                 x: centerX + m.left,
31558                 y: centerY + m.top,
31559                 width: centerW - (m.left+m.right),
31560                 height: centerH - (m.top+m.bottom)
31561             };
31562             //if(this.hideOnLayout){
31563                 //center.el.setStyle("display", "block");
31564             //}
31565             center.updateBox(this.safeBox(centerBox));
31566         }
31567         this.el.repaint();
31568         this.fireEvent("layout", this);
31569     },
31570
31571     // private
31572     safeBox : function(box){
31573         box.width = Math.max(0, box.width);
31574         box.height = Math.max(0, box.height);
31575         return box;
31576     },
31577
31578     /**
31579      * Adds a ContentPanel (or subclass) to this layout.
31580      * @param {String} target The target region key (north, south, east, west or center).
31581      * @param {Roo.ContentPanel} panel The panel to add
31582      * @return {Roo.ContentPanel} The added panel
31583      */
31584     add : function(target, panel){
31585          
31586         target = target.toLowerCase();
31587         return this.regions[target].add(panel);
31588     },
31589
31590     /**
31591      * Remove a ContentPanel (or subclass) to this layout.
31592      * @param {String} target The target region key (north, south, east, west or center).
31593      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31594      * @return {Roo.ContentPanel} The removed panel
31595      */
31596     remove : function(target, panel){
31597         target = target.toLowerCase();
31598         return this.regions[target].remove(panel);
31599     },
31600
31601     /**
31602      * Searches all regions for a panel with the specified id
31603      * @param {String} panelId
31604      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31605      */
31606     findPanel : function(panelId){
31607         var rs = this.regions;
31608         for(var target in rs){
31609             if(typeof rs[target] != "function"){
31610                 var p = rs[target].getPanel(panelId);
31611                 if(p){
31612                     return p;
31613                 }
31614             }
31615         }
31616         return null;
31617     },
31618
31619     /**
31620      * Searches all regions for a panel with the specified id and activates (shows) it.
31621      * @param {String/ContentPanel} panelId The panels id or the panel itself
31622      * @return {Roo.ContentPanel} The shown panel or null
31623      */
31624     showPanel : function(panelId) {
31625       var rs = this.regions;
31626       for(var target in rs){
31627          var r = rs[target];
31628          if(typeof r != "function"){
31629             if(r.hasPanel(panelId)){
31630                return r.showPanel(panelId);
31631             }
31632          }
31633       }
31634       return null;
31635    },
31636
31637    /**
31638      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31639      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31640      */
31641    /*
31642     restoreState : function(provider){
31643         if(!provider){
31644             provider = Roo.state.Manager;
31645         }
31646         var sm = new Roo.LayoutStateManager();
31647         sm.init(this, provider);
31648     },
31649 */
31650  
31651  
31652     /**
31653      * Adds a xtype elements to the layout.
31654      * <pre><code>
31655
31656 layout.addxtype({
31657        xtype : 'ContentPanel',
31658        region: 'west',
31659        items: [ .... ]
31660    }
31661 );
31662
31663 layout.addxtype({
31664         xtype : 'NestedLayoutPanel',
31665         region: 'west',
31666         layout: {
31667            center: { },
31668            west: { }   
31669         },
31670         items : [ ... list of content panels or nested layout panels.. ]
31671    }
31672 );
31673 </code></pre>
31674      * @param {Object} cfg Xtype definition of item to add.
31675      */
31676     addxtype : function(cfg)
31677     {
31678         // basically accepts a pannel...
31679         // can accept a layout region..!?!?
31680         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31681         
31682         
31683         // theory?  children can only be panels??
31684         
31685         //if (!cfg.xtype.match(/Panel$/)) {
31686         //    return false;
31687         //}
31688         var ret = false;
31689         
31690         if (typeof(cfg.region) == 'undefined') {
31691             Roo.log("Failed to add Panel, region was not set");
31692             Roo.log(cfg);
31693             return false;
31694         }
31695         var region = cfg.region;
31696         delete cfg.region;
31697         
31698           
31699         var xitems = [];
31700         if (cfg.items) {
31701             xitems = cfg.items;
31702             delete cfg.items;
31703         }
31704         var nb = false;
31705         
31706         switch(cfg.xtype) 
31707         {
31708             case 'Content':  // ContentPanel (el, cfg)
31709             case 'Scroll':  // ContentPanel (el, cfg)
31710             case 'View': 
31711                 cfg.autoCreate = true;
31712                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31713                 //} else {
31714                 //    var el = this.el.createChild();
31715                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31716                 //}
31717                 
31718                 this.add(region, ret);
31719                 break;
31720             
31721             /*
31722             case 'TreePanel': // our new panel!
31723                 cfg.el = this.el.createChild();
31724                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31725                 this.add(region, ret);
31726                 break;
31727             */
31728             
31729             case 'Nest': 
31730                 // create a new Layout (which is  a Border Layout...
31731                 
31732                 var clayout = cfg.layout;
31733                 clayout.el  = this.el.createChild();
31734                 clayout.items   = clayout.items  || [];
31735                 
31736                 delete cfg.layout;
31737                 
31738                 // replace this exitems with the clayout ones..
31739                 xitems = clayout.items;
31740                  
31741                 // force background off if it's in center...
31742                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31743                     cfg.background = false;
31744                 }
31745                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31746                 
31747                 
31748                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31749                 //console.log('adding nested layout panel '  + cfg.toSource());
31750                 this.add(region, ret);
31751                 nb = {}; /// find first...
31752                 break;
31753             
31754             case 'Grid':
31755                 
31756                 // needs grid and region
31757                 
31758                 //var el = this.getRegion(region).el.createChild();
31759                 /*
31760                  *var el = this.el.createChild();
31761                 // create the grid first...
31762                 cfg.grid.container = el;
31763                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31764                 */
31765                 
31766                 if (region == 'center' && this.active ) {
31767                     cfg.background = false;
31768                 }
31769                 
31770                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31771                 
31772                 this.add(region, ret);
31773                 /*
31774                 if (cfg.background) {
31775                     // render grid on panel activation (if panel background)
31776                     ret.on('activate', function(gp) {
31777                         if (!gp.grid.rendered) {
31778                     //        gp.grid.render(el);
31779                         }
31780                     });
31781                 } else {
31782                   //  cfg.grid.render(el);
31783                 }
31784                 */
31785                 break;
31786            
31787            
31788             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31789                 // it was the old xcomponent building that caused this before.
31790                 // espeically if border is the top element in the tree.
31791                 ret = this;
31792                 break; 
31793                 
31794                     
31795                 
31796                 
31797                 
31798             default:
31799                 /*
31800                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31801                     
31802                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31803                     this.add(region, ret);
31804                 } else {
31805                 */
31806                     Roo.log(cfg);
31807                     throw "Can not add '" + cfg.xtype + "' to Border";
31808                     return null;
31809              
31810                                 
31811              
31812         }
31813         this.beginUpdate();
31814         // add children..
31815         var region = '';
31816         var abn = {};
31817         Roo.each(xitems, function(i)  {
31818             region = nb && i.region ? i.region : false;
31819             
31820             var add = ret.addxtype(i);
31821            
31822             if (region) {
31823                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31824                 if (!i.background) {
31825                     abn[region] = nb[region] ;
31826                 }
31827             }
31828             
31829         });
31830         this.endUpdate();
31831
31832         // make the last non-background panel active..
31833         //if (nb) { Roo.log(abn); }
31834         if (nb) {
31835             
31836             for(var r in abn) {
31837                 region = this.getRegion(r);
31838                 if (region) {
31839                     // tried using nb[r], but it does not work..
31840                      
31841                     region.showPanel(abn[r]);
31842                    
31843                 }
31844             }
31845         }
31846         return ret;
31847         
31848     },
31849     
31850     
31851 // private
31852     factory : function(cfg)
31853     {
31854         
31855         var validRegions = Roo.bootstrap.layout.Border.regions;
31856
31857         var target = cfg.region;
31858         cfg.mgr = this;
31859         
31860         var r = Roo.bootstrap.layout;
31861         Roo.log(target);
31862         switch(target){
31863             case "north":
31864                 return new r.North(cfg);
31865             case "south":
31866                 return new r.South(cfg);
31867             case "east":
31868                 return new r.East(cfg);
31869             case "west":
31870                 return new r.West(cfg);
31871             case "center":
31872                 return new r.Center(cfg);
31873         }
31874         throw 'Layout region "'+target+'" not supported.';
31875     }
31876     
31877     
31878 });
31879  /*
31880  * Based on:
31881  * Ext JS Library 1.1.1
31882  * Copyright(c) 2006-2007, Ext JS, LLC.
31883  *
31884  * Originally Released Under LGPL - original licence link has changed is not relivant.
31885  *
31886  * Fork - LGPL
31887  * <script type="text/javascript">
31888  */
31889  
31890 /**
31891  * @class Roo.bootstrap.layout.Basic
31892  * @extends Roo.util.Observable
31893  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31894  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31895  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31896  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
31897  * @cfg {string}   region  the region that it inhabits..
31898  * @cfg {bool}   skipConfig skip config?
31899  * 
31900
31901  */
31902 Roo.bootstrap.layout.Basic = function(config){
31903     
31904     this.mgr = config.mgr;
31905     
31906     this.position = config.region;
31907     
31908     var skipConfig = config.skipConfig;
31909     
31910     this.events = {
31911         /**
31912          * @scope Roo.BasicLayoutRegion
31913          */
31914         
31915         /**
31916          * @event beforeremove
31917          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31918          * @param {Roo.LayoutRegion} this
31919          * @param {Roo.ContentPanel} panel The panel
31920          * @param {Object} e The cancel event object
31921          */
31922         "beforeremove" : true,
31923         /**
31924          * @event invalidated
31925          * Fires when the layout for this region is changed.
31926          * @param {Roo.LayoutRegion} this
31927          */
31928         "invalidated" : true,
31929         /**
31930          * @event visibilitychange
31931          * Fires when this region is shown or hidden 
31932          * @param {Roo.LayoutRegion} this
31933          * @param {Boolean} visibility true or false
31934          */
31935         "visibilitychange" : true,
31936         /**
31937          * @event paneladded
31938          * Fires when a panel is added. 
31939          * @param {Roo.LayoutRegion} this
31940          * @param {Roo.ContentPanel} panel The panel
31941          */
31942         "paneladded" : true,
31943         /**
31944          * @event panelremoved
31945          * Fires when a panel is removed. 
31946          * @param {Roo.LayoutRegion} this
31947          * @param {Roo.ContentPanel} panel The panel
31948          */
31949         "panelremoved" : true,
31950         /**
31951          * @event beforecollapse
31952          * Fires when this region before collapse.
31953          * @param {Roo.LayoutRegion} this
31954          */
31955         "beforecollapse" : true,
31956         /**
31957          * @event collapsed
31958          * Fires when this region is collapsed.
31959          * @param {Roo.LayoutRegion} this
31960          */
31961         "collapsed" : true,
31962         /**
31963          * @event expanded
31964          * Fires when this region is expanded.
31965          * @param {Roo.LayoutRegion} this
31966          */
31967         "expanded" : true,
31968         /**
31969          * @event slideshow
31970          * Fires when this region is slid into view.
31971          * @param {Roo.LayoutRegion} this
31972          */
31973         "slideshow" : true,
31974         /**
31975          * @event slidehide
31976          * Fires when this region slides out of view. 
31977          * @param {Roo.LayoutRegion} this
31978          */
31979         "slidehide" : true,
31980         /**
31981          * @event panelactivated
31982          * Fires when a panel is activated. 
31983          * @param {Roo.LayoutRegion} this
31984          * @param {Roo.ContentPanel} panel The activated panel
31985          */
31986         "panelactivated" : true,
31987         /**
31988          * @event resized
31989          * Fires when the user resizes this region. 
31990          * @param {Roo.LayoutRegion} this
31991          * @param {Number} newSize The new size (width for east/west, height for north/south)
31992          */
31993         "resized" : true
31994     };
31995     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31996     this.panels = new Roo.util.MixedCollection();
31997     this.panels.getKey = this.getPanelId.createDelegate(this);
31998     this.box = null;
31999     this.activePanel = null;
32000     // ensure listeners are added...
32001     
32002     if (config.listeners || config.events) {
32003         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32004             listeners : config.listeners || {},
32005             events : config.events || {}
32006         });
32007     }
32008     
32009     if(skipConfig !== true){
32010         this.applyConfig(config);
32011     }
32012 };
32013
32014 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32015 {
32016     getPanelId : function(p){
32017         return p.getId();
32018     },
32019     
32020     applyConfig : function(config){
32021         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32022         this.config = config;
32023         
32024     },
32025     
32026     /**
32027      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32028      * the width, for horizontal (north, south) the height.
32029      * @param {Number} newSize The new width or height
32030      */
32031     resizeTo : function(newSize){
32032         var el = this.el ? this.el :
32033                  (this.activePanel ? this.activePanel.getEl() : null);
32034         if(el){
32035             switch(this.position){
32036                 case "east":
32037                 case "west":
32038                     el.setWidth(newSize);
32039                     this.fireEvent("resized", this, newSize);
32040                 break;
32041                 case "north":
32042                 case "south":
32043                     el.setHeight(newSize);
32044                     this.fireEvent("resized", this, newSize);
32045                 break;                
32046             }
32047         }
32048     },
32049     
32050     getBox : function(){
32051         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32052     },
32053     
32054     getMargins : function(){
32055         return this.margins;
32056     },
32057     
32058     updateBox : function(box){
32059         this.box = box;
32060         var el = this.activePanel.getEl();
32061         el.dom.style.left = box.x + "px";
32062         el.dom.style.top = box.y + "px";
32063         this.activePanel.setSize(box.width, box.height);
32064     },
32065     
32066     /**
32067      * Returns the container element for this region.
32068      * @return {Roo.Element}
32069      */
32070     getEl : function(){
32071         return this.activePanel;
32072     },
32073     
32074     /**
32075      * Returns true if this region is currently visible.
32076      * @return {Boolean}
32077      */
32078     isVisible : function(){
32079         return this.activePanel ? true : false;
32080     },
32081     
32082     setActivePanel : function(panel){
32083         panel = this.getPanel(panel);
32084         if(this.activePanel && this.activePanel != panel){
32085             this.activePanel.setActiveState(false);
32086             this.activePanel.getEl().setLeftTop(-10000,-10000);
32087         }
32088         this.activePanel = panel;
32089         panel.setActiveState(true);
32090         if(this.box){
32091             panel.setSize(this.box.width, this.box.height);
32092         }
32093         this.fireEvent("panelactivated", this, panel);
32094         this.fireEvent("invalidated");
32095     },
32096     
32097     /**
32098      * Show the specified panel.
32099      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32100      * @return {Roo.ContentPanel} The shown panel or null
32101      */
32102     showPanel : function(panel){
32103         panel = this.getPanel(panel);
32104         if(panel){
32105             this.setActivePanel(panel);
32106         }
32107         return panel;
32108     },
32109     
32110     /**
32111      * Get the active panel for this region.
32112      * @return {Roo.ContentPanel} The active panel or null
32113      */
32114     getActivePanel : function(){
32115         return this.activePanel;
32116     },
32117     
32118     /**
32119      * Add the passed ContentPanel(s)
32120      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32121      * @return {Roo.ContentPanel} The panel added (if only one was added)
32122      */
32123     add : function(panel){
32124         if(arguments.length > 1){
32125             for(var i = 0, len = arguments.length; i < len; i++) {
32126                 this.add(arguments[i]);
32127             }
32128             return null;
32129         }
32130         if(this.hasPanel(panel)){
32131             this.showPanel(panel);
32132             return panel;
32133         }
32134         var el = panel.getEl();
32135         if(el.dom.parentNode != this.mgr.el.dom){
32136             this.mgr.el.dom.appendChild(el.dom);
32137         }
32138         if(panel.setRegion){
32139             panel.setRegion(this);
32140         }
32141         this.panels.add(panel);
32142         el.setStyle("position", "absolute");
32143         if(!panel.background){
32144             this.setActivePanel(panel);
32145             if(this.config.initialSize && this.panels.getCount()==1){
32146                 this.resizeTo(this.config.initialSize);
32147             }
32148         }
32149         this.fireEvent("paneladded", this, panel);
32150         return panel;
32151     },
32152     
32153     /**
32154      * Returns true if the panel is in this region.
32155      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32156      * @return {Boolean}
32157      */
32158     hasPanel : function(panel){
32159         if(typeof panel == "object"){ // must be panel obj
32160             panel = panel.getId();
32161         }
32162         return this.getPanel(panel) ? true : false;
32163     },
32164     
32165     /**
32166      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32167      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32168      * @param {Boolean} preservePanel Overrides the config preservePanel option
32169      * @return {Roo.ContentPanel} The panel that was removed
32170      */
32171     remove : function(panel, preservePanel){
32172         panel = this.getPanel(panel);
32173         if(!panel){
32174             return null;
32175         }
32176         var e = {};
32177         this.fireEvent("beforeremove", this, panel, e);
32178         if(e.cancel === true){
32179             return null;
32180         }
32181         var panelId = panel.getId();
32182         this.panels.removeKey(panelId);
32183         return panel;
32184     },
32185     
32186     /**
32187      * Returns the panel specified or null if it's not in this region.
32188      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32189      * @return {Roo.ContentPanel}
32190      */
32191     getPanel : function(id){
32192         if(typeof id == "object"){ // must be panel obj
32193             return id;
32194         }
32195         return this.panels.get(id);
32196     },
32197     
32198     /**
32199      * Returns this regions position (north/south/east/west/center).
32200      * @return {String} 
32201      */
32202     getPosition: function(){
32203         return this.position;    
32204     }
32205 });/*
32206  * Based on:
32207  * Ext JS Library 1.1.1
32208  * Copyright(c) 2006-2007, Ext JS, LLC.
32209  *
32210  * Originally Released Under LGPL - original licence link has changed is not relivant.
32211  *
32212  * Fork - LGPL
32213  * <script type="text/javascript">
32214  */
32215  
32216 /**
32217  * @class Roo.bootstrap.layout.Region
32218  * @extends Roo.bootstrap.layout.Basic
32219  * This class represents a region in a layout manager.
32220  
32221  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32222  * @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})
32223  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32224  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32225  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32226  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32227  * @cfg {String}    title           The title for the region (overrides panel titles)
32228  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32229  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32230  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32231  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32232  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32233  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32234  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32235  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32236  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32237  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32238
32239  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32240  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32241  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32242  * @cfg {Number}    width           For East/West panels
32243  * @cfg {Number}    height          For North/South panels
32244  * @cfg {Boolean}   split           To show the splitter
32245  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32246  * 
32247  * @cfg {string}   cls             Extra CSS classes to add to region
32248  * 
32249  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32250  * @cfg {string}   region  the region that it inhabits..
32251  *
32252
32253  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32254  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32255
32256  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32257  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32258  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32259  */
32260 Roo.bootstrap.layout.Region = function(config)
32261 {
32262     this.applyConfig(config);
32263
32264     var mgr = config.mgr;
32265     var pos = config.region;
32266     config.skipConfig = true;
32267     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32268     
32269     if (mgr.el) {
32270         this.onRender(mgr.el);   
32271     }
32272      
32273     this.visible = true;
32274     this.collapsed = false;
32275 };
32276
32277 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32278
32279     position: '', // set by wrapper (eg. north/south etc..)
32280
32281     createBody : function(){
32282         /** This region's body element 
32283         * @type Roo.Element */
32284         this.bodyEl = this.el.createChild({
32285                 tag: "div",
32286                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32287         });
32288     },
32289
32290     onRender: function(ctr, pos)
32291     {
32292         var dh = Roo.DomHelper;
32293         /** This region's container element 
32294         * @type Roo.Element */
32295         this.el = dh.append(ctr.dom, {
32296                 tag: "div",
32297                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32298             }, true);
32299         /** This region's title element 
32300         * @type Roo.Element */
32301     
32302         this.titleEl = dh.append(this.el.dom,
32303             {
32304                     tag: "div",
32305                     unselectable: "on",
32306                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32307                     children:[
32308                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32309                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32310                     ]}, true);
32311         
32312         this.titleEl.enableDisplayMode();
32313         /** This region's title text element 
32314         * @type HTMLElement */
32315         this.titleTextEl = this.titleEl.dom.firstChild;
32316         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32317         /*
32318         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32319         this.closeBtn.enableDisplayMode();
32320         this.closeBtn.on("click", this.closeClicked, this);
32321         this.closeBtn.hide();
32322     */
32323         this.createBody(this.config);
32324         if(this.config.hideWhenEmpty){
32325             this.hide();
32326             this.on("paneladded", this.validateVisibility, this);
32327             this.on("panelremoved", this.validateVisibility, this);
32328         }
32329         if(this.autoScroll){
32330             this.bodyEl.setStyle("overflow", "auto");
32331         }else{
32332             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32333         }
32334         //if(c.titlebar !== false){
32335             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32336                 this.titleEl.hide();
32337             }else{
32338                 this.titleEl.show();
32339                 if(this.config.title){
32340                     this.titleTextEl.innerHTML = this.config.title;
32341                 }
32342             }
32343         //}
32344         if(this.config.collapsed){
32345             this.collapse(true);
32346         }
32347         if(this.config.hidden){
32348             this.hide();
32349         }
32350     },
32351     
32352     applyConfig : function(c)
32353     {
32354         /*
32355          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32356             var dh = Roo.DomHelper;
32357             if(c.titlebar !== false){
32358                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32359                 this.collapseBtn.on("click", this.collapse, this);
32360                 this.collapseBtn.enableDisplayMode();
32361                 /*
32362                 if(c.showPin === true || this.showPin){
32363                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32364                     this.stickBtn.enableDisplayMode();
32365                     this.stickBtn.on("click", this.expand, this);
32366                     this.stickBtn.hide();
32367                 }
32368                 
32369             }
32370             */
32371             /** This region's collapsed element
32372             * @type Roo.Element */
32373             /*
32374              *
32375             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32376                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32377             ]}, true);
32378             
32379             if(c.floatable !== false){
32380                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32381                this.collapsedEl.on("click", this.collapseClick, this);
32382             }
32383
32384             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32385                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32386                    id: "message", unselectable: "on", style:{"float":"left"}});
32387                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32388              }
32389             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32390             this.expandBtn.on("click", this.expand, this);
32391             
32392         }
32393         
32394         if(this.collapseBtn){
32395             this.collapseBtn.setVisible(c.collapsible == true);
32396         }
32397         
32398         this.cmargins = c.cmargins || this.cmargins ||
32399                          (this.position == "west" || this.position == "east" ?
32400                              {top: 0, left: 2, right:2, bottom: 0} :
32401                              {top: 2, left: 0, right:0, bottom: 2});
32402         */
32403         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32404         
32405         
32406         this.bottomTabs = c.tabPosition != "top";
32407         
32408         this.autoScroll = c.autoScroll || false;
32409         
32410         
32411        
32412         
32413         this.duration = c.duration || .30;
32414         this.slideDuration = c.slideDuration || .45;
32415         this.config = c;
32416        
32417     },
32418     /**
32419      * Returns true if this region is currently visible.
32420      * @return {Boolean}
32421      */
32422     isVisible : function(){
32423         return this.visible;
32424     },
32425
32426     /**
32427      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32428      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32429      */
32430     //setCollapsedTitle : function(title){
32431     //    title = title || "&#160;";
32432      //   if(this.collapsedTitleTextEl){
32433       //      this.collapsedTitleTextEl.innerHTML = title;
32434        // }
32435     //},
32436
32437     getBox : function(){
32438         var b;
32439       //  if(!this.collapsed){
32440             b = this.el.getBox(false, true);
32441        // }else{
32442           //  b = this.collapsedEl.getBox(false, true);
32443         //}
32444         return b;
32445     },
32446
32447     getMargins : function(){
32448         return this.margins;
32449         //return this.collapsed ? this.cmargins : this.margins;
32450     },
32451 /*
32452     highlight : function(){
32453         this.el.addClass("x-layout-panel-dragover");
32454     },
32455
32456     unhighlight : function(){
32457         this.el.removeClass("x-layout-panel-dragover");
32458     },
32459 */
32460     updateBox : function(box)
32461     {
32462         this.box = box;
32463         if(!this.collapsed){
32464             this.el.dom.style.left = box.x + "px";
32465             this.el.dom.style.top = box.y + "px";
32466             this.updateBody(box.width, box.height);
32467         }else{
32468             this.collapsedEl.dom.style.left = box.x + "px";
32469             this.collapsedEl.dom.style.top = box.y + "px";
32470             this.collapsedEl.setSize(box.width, box.height);
32471         }
32472         if(this.tabs){
32473             this.tabs.autoSizeTabs();
32474         }
32475     },
32476
32477     updateBody : function(w, h)
32478     {
32479         if(w !== null){
32480             this.el.setWidth(w);
32481             w -= this.el.getBorderWidth("rl");
32482             if(this.config.adjustments){
32483                 w += this.config.adjustments[0];
32484             }
32485         }
32486         if(h !== null){
32487             this.el.setHeight(h);
32488             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32489             h -= this.el.getBorderWidth("tb");
32490             if(this.config.adjustments){
32491                 h += this.config.adjustments[1];
32492             }
32493             this.bodyEl.setHeight(h);
32494             if(this.tabs){
32495                 h = this.tabs.syncHeight(h);
32496             }
32497         }
32498         if(this.panelSize){
32499             w = w !== null ? w : this.panelSize.width;
32500             h = h !== null ? h : this.panelSize.height;
32501         }
32502         if(this.activePanel){
32503             var el = this.activePanel.getEl();
32504             w = w !== null ? w : el.getWidth();
32505             h = h !== null ? h : el.getHeight();
32506             this.panelSize = {width: w, height: h};
32507             this.activePanel.setSize(w, h);
32508         }
32509         if(Roo.isIE && this.tabs){
32510             this.tabs.el.repaint();
32511         }
32512     },
32513
32514     /**
32515      * Returns the container element for this region.
32516      * @return {Roo.Element}
32517      */
32518     getEl : function(){
32519         return this.el;
32520     },
32521
32522     /**
32523      * Hides this region.
32524      */
32525     hide : function(){
32526         //if(!this.collapsed){
32527             this.el.dom.style.left = "-2000px";
32528             this.el.hide();
32529         //}else{
32530          //   this.collapsedEl.dom.style.left = "-2000px";
32531          //   this.collapsedEl.hide();
32532        // }
32533         this.visible = false;
32534         this.fireEvent("visibilitychange", this, false);
32535     },
32536
32537     /**
32538      * Shows this region if it was previously hidden.
32539      */
32540     show : function(){
32541         //if(!this.collapsed){
32542             this.el.show();
32543         //}else{
32544         //    this.collapsedEl.show();
32545        // }
32546         this.visible = true;
32547         this.fireEvent("visibilitychange", this, true);
32548     },
32549 /*
32550     closeClicked : function(){
32551         if(this.activePanel){
32552             this.remove(this.activePanel);
32553         }
32554     },
32555
32556     collapseClick : function(e){
32557         if(this.isSlid){
32558            e.stopPropagation();
32559            this.slideIn();
32560         }else{
32561            e.stopPropagation();
32562            this.slideOut();
32563         }
32564     },
32565 */
32566     /**
32567      * Collapses this region.
32568      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32569      */
32570     /*
32571     collapse : function(skipAnim, skipCheck = false){
32572         if(this.collapsed) {
32573             return;
32574         }
32575         
32576         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32577             
32578             this.collapsed = true;
32579             if(this.split){
32580                 this.split.el.hide();
32581             }
32582             if(this.config.animate && skipAnim !== true){
32583                 this.fireEvent("invalidated", this);
32584                 this.animateCollapse();
32585             }else{
32586                 this.el.setLocation(-20000,-20000);
32587                 this.el.hide();
32588                 this.collapsedEl.show();
32589                 this.fireEvent("collapsed", this);
32590                 this.fireEvent("invalidated", this);
32591             }
32592         }
32593         
32594     },
32595 */
32596     animateCollapse : function(){
32597         // overridden
32598     },
32599
32600     /**
32601      * Expands this region if it was previously collapsed.
32602      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32603      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32604      */
32605     /*
32606     expand : function(e, skipAnim){
32607         if(e) {
32608             e.stopPropagation();
32609         }
32610         if(!this.collapsed || this.el.hasActiveFx()) {
32611             return;
32612         }
32613         if(this.isSlid){
32614             this.afterSlideIn();
32615             skipAnim = true;
32616         }
32617         this.collapsed = false;
32618         if(this.config.animate && skipAnim !== true){
32619             this.animateExpand();
32620         }else{
32621             this.el.show();
32622             if(this.split){
32623                 this.split.el.show();
32624             }
32625             this.collapsedEl.setLocation(-2000,-2000);
32626             this.collapsedEl.hide();
32627             this.fireEvent("invalidated", this);
32628             this.fireEvent("expanded", this);
32629         }
32630     },
32631 */
32632     animateExpand : function(){
32633         // overridden
32634     },
32635
32636     initTabs : function()
32637     {
32638         this.bodyEl.setStyle("overflow", "hidden");
32639         var ts = new Roo.bootstrap.panel.Tabs({
32640                 el: this.bodyEl.dom,
32641                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32642                 disableTooltips: this.config.disableTabTips,
32643                 toolbar : this.config.toolbar
32644             });
32645         
32646         if(this.config.hideTabs){
32647             ts.stripWrap.setDisplayed(false);
32648         }
32649         this.tabs = ts;
32650         ts.resizeTabs = this.config.resizeTabs === true;
32651         ts.minTabWidth = this.config.minTabWidth || 40;
32652         ts.maxTabWidth = this.config.maxTabWidth || 250;
32653         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32654         ts.monitorResize = false;
32655         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32656         ts.bodyEl.addClass('roo-layout-tabs-body');
32657         this.panels.each(this.initPanelAsTab, this);
32658     },
32659
32660     initPanelAsTab : function(panel){
32661         var ti = this.tabs.addTab(
32662                     panel.getEl().id,
32663                     panel.getTitle(), null,
32664                     this.config.closeOnTab && panel.isClosable()
32665             );
32666         if(panel.tabTip !== undefined){
32667             ti.setTooltip(panel.tabTip);
32668         }
32669         ti.on("activate", function(){
32670               this.setActivePanel(panel);
32671         }, this);
32672         
32673         if(this.config.closeOnTab){
32674             ti.on("beforeclose", function(t, e){
32675                 e.cancel = true;
32676                 this.remove(panel);
32677             }, this);
32678         }
32679         return ti;
32680     },
32681
32682     updatePanelTitle : function(panel, title)
32683     {
32684         if(this.activePanel == panel){
32685             this.updateTitle(title);
32686         }
32687         if(this.tabs){
32688             var ti = this.tabs.getTab(panel.getEl().id);
32689             ti.setText(title);
32690             if(panel.tabTip !== undefined){
32691                 ti.setTooltip(panel.tabTip);
32692             }
32693         }
32694     },
32695
32696     updateTitle : function(title){
32697         if(this.titleTextEl && !this.config.title){
32698             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32699         }
32700     },
32701
32702     setActivePanel : function(panel)
32703     {
32704         panel = this.getPanel(panel);
32705         if(this.activePanel && this.activePanel != panel){
32706             this.activePanel.setActiveState(false);
32707         }
32708         this.activePanel = panel;
32709         panel.setActiveState(true);
32710         if(this.panelSize){
32711             panel.setSize(this.panelSize.width, this.panelSize.height);
32712         }
32713         if(this.closeBtn){
32714             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32715         }
32716         this.updateTitle(panel.getTitle());
32717         if(this.tabs){
32718             this.fireEvent("invalidated", this);
32719         }
32720         this.fireEvent("panelactivated", this, panel);
32721     },
32722
32723     /**
32724      * Shows the specified panel.
32725      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32726      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32727      */
32728     showPanel : function(panel)
32729     {
32730         panel = this.getPanel(panel);
32731         if(panel){
32732             if(this.tabs){
32733                 var tab = this.tabs.getTab(panel.getEl().id);
32734                 if(tab.isHidden()){
32735                     this.tabs.unhideTab(tab.id);
32736                 }
32737                 tab.activate();
32738             }else{
32739                 this.setActivePanel(panel);
32740             }
32741         }
32742         return panel;
32743     },
32744
32745     /**
32746      * Get the active panel for this region.
32747      * @return {Roo.ContentPanel} The active panel or null
32748      */
32749     getActivePanel : function(){
32750         return this.activePanel;
32751     },
32752
32753     validateVisibility : function(){
32754         if(this.panels.getCount() < 1){
32755             this.updateTitle("&#160;");
32756             this.closeBtn.hide();
32757             this.hide();
32758         }else{
32759             if(!this.isVisible()){
32760                 this.show();
32761             }
32762         }
32763     },
32764
32765     /**
32766      * Adds the passed ContentPanel(s) to this region.
32767      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32768      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32769      */
32770     add : function(panel){
32771         if(arguments.length > 1){
32772             for(var i = 0, len = arguments.length; i < len; i++) {
32773                 this.add(arguments[i]);
32774             }
32775             return null;
32776         }
32777         if(this.hasPanel(panel)){
32778             this.showPanel(panel);
32779             return panel;
32780         }
32781         panel.setRegion(this);
32782         this.panels.add(panel);
32783         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32784             this.bodyEl.dom.appendChild(panel.getEl().dom);
32785             if(panel.background !== true){
32786                 this.setActivePanel(panel);
32787             }
32788             this.fireEvent("paneladded", this, panel);
32789             return panel;
32790         }
32791         if(!this.tabs){
32792             this.initTabs();
32793         }else{
32794             this.initPanelAsTab(panel);
32795         }
32796         
32797         
32798         if(panel.background !== true){
32799             this.tabs.activate(panel.getEl().id);
32800         }
32801         this.fireEvent("paneladded", this, panel);
32802         return panel;
32803     },
32804
32805     /**
32806      * Hides the tab for the specified panel.
32807      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32808      */
32809     hidePanel : function(panel){
32810         if(this.tabs && (panel = this.getPanel(panel))){
32811             this.tabs.hideTab(panel.getEl().id);
32812         }
32813     },
32814
32815     /**
32816      * Unhides the tab for a previously hidden panel.
32817      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32818      */
32819     unhidePanel : function(panel){
32820         if(this.tabs && (panel = this.getPanel(panel))){
32821             this.tabs.unhideTab(panel.getEl().id);
32822         }
32823     },
32824
32825     clearPanels : function(){
32826         while(this.panels.getCount() > 0){
32827              this.remove(this.panels.first());
32828         }
32829     },
32830
32831     /**
32832      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32833      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32834      * @param {Boolean} preservePanel Overrides the config preservePanel option
32835      * @return {Roo.ContentPanel} The panel that was removed
32836      */
32837     remove : function(panel, preservePanel)
32838     {
32839         panel = this.getPanel(panel);
32840         if(!panel){
32841             return null;
32842         }
32843         var e = {};
32844         this.fireEvent("beforeremove", this, panel, e);
32845         if(e.cancel === true){
32846             return null;
32847         }
32848         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32849         var panelId = panel.getId();
32850         this.panels.removeKey(panelId);
32851         if(preservePanel){
32852             document.body.appendChild(panel.getEl().dom);
32853         }
32854         if(this.tabs){
32855             this.tabs.removeTab(panel.getEl().id);
32856         }else if (!preservePanel){
32857             this.bodyEl.dom.removeChild(panel.getEl().dom);
32858         }
32859         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32860             var p = this.panels.first();
32861             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32862             tempEl.appendChild(p.getEl().dom);
32863             this.bodyEl.update("");
32864             this.bodyEl.dom.appendChild(p.getEl().dom);
32865             tempEl = null;
32866             this.updateTitle(p.getTitle());
32867             this.tabs = null;
32868             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32869             this.setActivePanel(p);
32870         }
32871         panel.setRegion(null);
32872         if(this.activePanel == panel){
32873             this.activePanel = null;
32874         }
32875         if(this.config.autoDestroy !== false && preservePanel !== true){
32876             try{panel.destroy();}catch(e){}
32877         }
32878         this.fireEvent("panelremoved", this, panel);
32879         return panel;
32880     },
32881
32882     /**
32883      * Returns the TabPanel component used by this region
32884      * @return {Roo.TabPanel}
32885      */
32886     getTabs : function(){
32887         return this.tabs;
32888     },
32889
32890     createTool : function(parentEl, className){
32891         var btn = Roo.DomHelper.append(parentEl, {
32892             tag: "div",
32893             cls: "x-layout-tools-button",
32894             children: [ {
32895                 tag: "div",
32896                 cls: "roo-layout-tools-button-inner " + className,
32897                 html: "&#160;"
32898             }]
32899         }, true);
32900         btn.addClassOnOver("roo-layout-tools-button-over");
32901         return btn;
32902     }
32903 });/*
32904  * Based on:
32905  * Ext JS Library 1.1.1
32906  * Copyright(c) 2006-2007, Ext JS, LLC.
32907  *
32908  * Originally Released Under LGPL - original licence link has changed is not relivant.
32909  *
32910  * Fork - LGPL
32911  * <script type="text/javascript">
32912  */
32913  
32914
32915
32916 /**
32917  * @class Roo.SplitLayoutRegion
32918  * @extends Roo.LayoutRegion
32919  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32920  */
32921 Roo.bootstrap.layout.Split = function(config){
32922     this.cursor = config.cursor;
32923     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32924 };
32925
32926 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32927 {
32928     splitTip : "Drag to resize.",
32929     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32930     useSplitTips : false,
32931
32932     applyConfig : function(config){
32933         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32934     },
32935     
32936     onRender : function(ctr,pos) {
32937         
32938         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
32939         if(!this.config.split){
32940             return;
32941         }
32942         if(!this.split){
32943             
32944             var splitEl = Roo.DomHelper.append(ctr.dom,  {
32945                             tag: "div",
32946                             id: this.el.id + "-split",
32947                             cls: "roo-layout-split roo-layout-split-"+this.position,
32948                             html: "&#160;"
32949             });
32950             /** The SplitBar for this region 
32951             * @type Roo.SplitBar */
32952             // does not exist yet...
32953             Roo.log([this.position, this.orientation]);
32954             
32955             this.split = new Roo.bootstrap.SplitBar({
32956                 dragElement : splitEl,
32957                 resizingElement: this.el,
32958                 orientation : this.orientation
32959             });
32960             
32961             this.split.on("moved", this.onSplitMove, this);
32962             this.split.useShim = this.config.useShim === true;
32963             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32964             if(this.useSplitTips){
32965                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32966             }
32967             //if(config.collapsible){
32968             //    this.split.el.on("dblclick", this.collapse,  this);
32969             //}
32970         }
32971         if(typeof this.config.minSize != "undefined"){
32972             this.split.minSize = this.config.minSize;
32973         }
32974         if(typeof this.config.maxSize != "undefined"){
32975             this.split.maxSize = this.config.maxSize;
32976         }
32977         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
32978             this.hideSplitter();
32979         }
32980         
32981     },
32982
32983     getHMaxSize : function(){
32984          var cmax = this.config.maxSize || 10000;
32985          var center = this.mgr.getRegion("center");
32986          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32987     },
32988
32989     getVMaxSize : function(){
32990          var cmax = this.config.maxSize || 10000;
32991          var center = this.mgr.getRegion("center");
32992          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32993     },
32994
32995     onSplitMove : function(split, newSize){
32996         this.fireEvent("resized", this, newSize);
32997     },
32998     
32999     /** 
33000      * Returns the {@link Roo.SplitBar} for this region.
33001      * @return {Roo.SplitBar}
33002      */
33003     getSplitBar : function(){
33004         return this.split;
33005     },
33006     
33007     hide : function(){
33008         this.hideSplitter();
33009         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33010     },
33011
33012     hideSplitter : function(){
33013         if(this.split){
33014             this.split.el.setLocation(-2000,-2000);
33015             this.split.el.hide();
33016         }
33017     },
33018
33019     show : function(){
33020         if(this.split){
33021             this.split.el.show();
33022         }
33023         Roo.bootstrap.layout.Split.superclass.show.call(this);
33024     },
33025     
33026     beforeSlide: function(){
33027         if(Roo.isGecko){// firefox overflow auto bug workaround
33028             this.bodyEl.clip();
33029             if(this.tabs) {
33030                 this.tabs.bodyEl.clip();
33031             }
33032             if(this.activePanel){
33033                 this.activePanel.getEl().clip();
33034                 
33035                 if(this.activePanel.beforeSlide){
33036                     this.activePanel.beforeSlide();
33037                 }
33038             }
33039         }
33040     },
33041     
33042     afterSlide : function(){
33043         if(Roo.isGecko){// firefox overflow auto bug workaround
33044             this.bodyEl.unclip();
33045             if(this.tabs) {
33046                 this.tabs.bodyEl.unclip();
33047             }
33048             if(this.activePanel){
33049                 this.activePanel.getEl().unclip();
33050                 if(this.activePanel.afterSlide){
33051                     this.activePanel.afterSlide();
33052                 }
33053             }
33054         }
33055     },
33056
33057     initAutoHide : function(){
33058         if(this.autoHide !== false){
33059             if(!this.autoHideHd){
33060                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33061                 this.autoHideHd = {
33062                     "mouseout": function(e){
33063                         if(!e.within(this.el, true)){
33064                             st.delay(500);
33065                         }
33066                     },
33067                     "mouseover" : function(e){
33068                         st.cancel();
33069                     },
33070                     scope : this
33071                 };
33072             }
33073             this.el.on(this.autoHideHd);
33074         }
33075     },
33076
33077     clearAutoHide : function(){
33078         if(this.autoHide !== false){
33079             this.el.un("mouseout", this.autoHideHd.mouseout);
33080             this.el.un("mouseover", this.autoHideHd.mouseover);
33081         }
33082     },
33083
33084     clearMonitor : function(){
33085         Roo.get(document).un("click", this.slideInIf, this);
33086     },
33087
33088     // these names are backwards but not changed for compat
33089     slideOut : function(){
33090         if(this.isSlid || this.el.hasActiveFx()){
33091             return;
33092         }
33093         this.isSlid = true;
33094         if(this.collapseBtn){
33095             this.collapseBtn.hide();
33096         }
33097         this.closeBtnState = this.closeBtn.getStyle('display');
33098         this.closeBtn.hide();
33099         if(this.stickBtn){
33100             this.stickBtn.show();
33101         }
33102         this.el.show();
33103         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33104         this.beforeSlide();
33105         this.el.setStyle("z-index", 10001);
33106         this.el.slideIn(this.getSlideAnchor(), {
33107             callback: function(){
33108                 this.afterSlide();
33109                 this.initAutoHide();
33110                 Roo.get(document).on("click", this.slideInIf, this);
33111                 this.fireEvent("slideshow", this);
33112             },
33113             scope: this,
33114             block: true
33115         });
33116     },
33117
33118     afterSlideIn : function(){
33119         this.clearAutoHide();
33120         this.isSlid = false;
33121         this.clearMonitor();
33122         this.el.setStyle("z-index", "");
33123         if(this.collapseBtn){
33124             this.collapseBtn.show();
33125         }
33126         this.closeBtn.setStyle('display', this.closeBtnState);
33127         if(this.stickBtn){
33128             this.stickBtn.hide();
33129         }
33130         this.fireEvent("slidehide", this);
33131     },
33132
33133     slideIn : function(cb){
33134         if(!this.isSlid || this.el.hasActiveFx()){
33135             Roo.callback(cb);
33136             return;
33137         }
33138         this.isSlid = false;
33139         this.beforeSlide();
33140         this.el.slideOut(this.getSlideAnchor(), {
33141             callback: function(){
33142                 this.el.setLeftTop(-10000, -10000);
33143                 this.afterSlide();
33144                 this.afterSlideIn();
33145                 Roo.callback(cb);
33146             },
33147             scope: this,
33148             block: true
33149         });
33150     },
33151     
33152     slideInIf : function(e){
33153         if(!e.within(this.el)){
33154             this.slideIn();
33155         }
33156     },
33157
33158     animateCollapse : function(){
33159         this.beforeSlide();
33160         this.el.setStyle("z-index", 20000);
33161         var anchor = this.getSlideAnchor();
33162         this.el.slideOut(anchor, {
33163             callback : function(){
33164                 this.el.setStyle("z-index", "");
33165                 this.collapsedEl.slideIn(anchor, {duration:.3});
33166                 this.afterSlide();
33167                 this.el.setLocation(-10000,-10000);
33168                 this.el.hide();
33169                 this.fireEvent("collapsed", this);
33170             },
33171             scope: this,
33172             block: true
33173         });
33174     },
33175
33176     animateExpand : function(){
33177         this.beforeSlide();
33178         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33179         this.el.setStyle("z-index", 20000);
33180         this.collapsedEl.hide({
33181             duration:.1
33182         });
33183         this.el.slideIn(this.getSlideAnchor(), {
33184             callback : function(){
33185                 this.el.setStyle("z-index", "");
33186                 this.afterSlide();
33187                 if(this.split){
33188                     this.split.el.show();
33189                 }
33190                 this.fireEvent("invalidated", this);
33191                 this.fireEvent("expanded", this);
33192             },
33193             scope: this,
33194             block: true
33195         });
33196     },
33197
33198     anchors : {
33199         "west" : "left",
33200         "east" : "right",
33201         "north" : "top",
33202         "south" : "bottom"
33203     },
33204
33205     sanchors : {
33206         "west" : "l",
33207         "east" : "r",
33208         "north" : "t",
33209         "south" : "b"
33210     },
33211
33212     canchors : {
33213         "west" : "tl-tr",
33214         "east" : "tr-tl",
33215         "north" : "tl-bl",
33216         "south" : "bl-tl"
33217     },
33218
33219     getAnchor : function(){
33220         return this.anchors[this.position];
33221     },
33222
33223     getCollapseAnchor : function(){
33224         return this.canchors[this.position];
33225     },
33226
33227     getSlideAnchor : function(){
33228         return this.sanchors[this.position];
33229     },
33230
33231     getAlignAdj : function(){
33232         var cm = this.cmargins;
33233         switch(this.position){
33234             case "west":
33235                 return [0, 0];
33236             break;
33237             case "east":
33238                 return [0, 0];
33239             break;
33240             case "north":
33241                 return [0, 0];
33242             break;
33243             case "south":
33244                 return [0, 0];
33245             break;
33246         }
33247     },
33248
33249     getExpandAdj : function(){
33250         var c = this.collapsedEl, cm = this.cmargins;
33251         switch(this.position){
33252             case "west":
33253                 return [-(cm.right+c.getWidth()+cm.left), 0];
33254             break;
33255             case "east":
33256                 return [cm.right+c.getWidth()+cm.left, 0];
33257             break;
33258             case "north":
33259                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33260             break;
33261             case "south":
33262                 return [0, cm.top+cm.bottom+c.getHeight()];
33263             break;
33264         }
33265     }
33266 });/*
33267  * Based on:
33268  * Ext JS Library 1.1.1
33269  * Copyright(c) 2006-2007, Ext JS, LLC.
33270  *
33271  * Originally Released Under LGPL - original licence link has changed is not relivant.
33272  *
33273  * Fork - LGPL
33274  * <script type="text/javascript">
33275  */
33276 /*
33277  * These classes are private internal classes
33278  */
33279 Roo.bootstrap.layout.Center = function(config){
33280     config.region = "center";
33281     Roo.bootstrap.layout.Region.call(this, config);
33282     this.visible = true;
33283     this.minWidth = config.minWidth || 20;
33284     this.minHeight = config.minHeight || 20;
33285 };
33286
33287 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33288     hide : function(){
33289         // center panel can't be hidden
33290     },
33291     
33292     show : function(){
33293         // center panel can't be hidden
33294     },
33295     
33296     getMinWidth: function(){
33297         return this.minWidth;
33298     },
33299     
33300     getMinHeight: function(){
33301         return this.minHeight;
33302     }
33303 });
33304
33305
33306
33307
33308  
33309
33310
33311
33312
33313
33314 Roo.bootstrap.layout.North = function(config)
33315 {
33316     config.region = 'north';
33317     config.cursor = 'n-resize';
33318     
33319     Roo.bootstrap.layout.Split.call(this, config);
33320     
33321     
33322     if(this.split){
33323         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33324         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33325         this.split.el.addClass("roo-layout-split-v");
33326     }
33327     var size = config.initialSize || config.height;
33328     if(typeof size != "undefined"){
33329         this.el.setHeight(size);
33330     }
33331 };
33332 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33333 {
33334     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33335     
33336     
33337     
33338     getBox : function(){
33339         if(this.collapsed){
33340             return this.collapsedEl.getBox();
33341         }
33342         var box = this.el.getBox();
33343         if(this.split){
33344             box.height += this.split.el.getHeight();
33345         }
33346         return box;
33347     },
33348     
33349     updateBox : function(box){
33350         if(this.split && !this.collapsed){
33351             box.height -= this.split.el.getHeight();
33352             this.split.el.setLeft(box.x);
33353             this.split.el.setTop(box.y+box.height);
33354             this.split.el.setWidth(box.width);
33355         }
33356         if(this.collapsed){
33357             this.updateBody(box.width, null);
33358         }
33359         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33360     }
33361 });
33362
33363
33364
33365
33366
33367 Roo.bootstrap.layout.South = function(config){
33368     config.region = 'south';
33369     config.cursor = 's-resize';
33370     Roo.bootstrap.layout.Split.call(this, config);
33371     if(this.split){
33372         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33373         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33374         this.split.el.addClass("roo-layout-split-v");
33375     }
33376     var size = config.initialSize || config.height;
33377     if(typeof size != "undefined"){
33378         this.el.setHeight(size);
33379     }
33380 };
33381
33382 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33383     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33384     getBox : function(){
33385         if(this.collapsed){
33386             return this.collapsedEl.getBox();
33387         }
33388         var box = this.el.getBox();
33389         if(this.split){
33390             var sh = this.split.el.getHeight();
33391             box.height += sh;
33392             box.y -= sh;
33393         }
33394         return box;
33395     },
33396     
33397     updateBox : function(box){
33398         if(this.split && !this.collapsed){
33399             var sh = this.split.el.getHeight();
33400             box.height -= sh;
33401             box.y += sh;
33402             this.split.el.setLeft(box.x);
33403             this.split.el.setTop(box.y-sh);
33404             this.split.el.setWidth(box.width);
33405         }
33406         if(this.collapsed){
33407             this.updateBody(box.width, null);
33408         }
33409         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33410     }
33411 });
33412
33413 Roo.bootstrap.layout.East = function(config){
33414     config.region = "east";
33415     config.cursor = "e-resize";
33416     Roo.bootstrap.layout.Split.call(this, config);
33417     if(this.split){
33418         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33419         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33420         this.split.el.addClass("roo-layout-split-h");
33421     }
33422     var size = config.initialSize || config.width;
33423     if(typeof size != "undefined"){
33424         this.el.setWidth(size);
33425     }
33426 };
33427 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33428     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33429     getBox : function(){
33430         if(this.collapsed){
33431             return this.collapsedEl.getBox();
33432         }
33433         var box = this.el.getBox();
33434         if(this.split){
33435             var sw = this.split.el.getWidth();
33436             box.width += sw;
33437             box.x -= sw;
33438         }
33439         return box;
33440     },
33441
33442     updateBox : function(box){
33443         if(this.split && !this.collapsed){
33444             var sw = this.split.el.getWidth();
33445             box.width -= sw;
33446             this.split.el.setLeft(box.x);
33447             this.split.el.setTop(box.y);
33448             this.split.el.setHeight(box.height);
33449             box.x += sw;
33450         }
33451         if(this.collapsed){
33452             this.updateBody(null, box.height);
33453         }
33454         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33455     }
33456 });
33457
33458 Roo.bootstrap.layout.West = function(config){
33459     config.region = "west";
33460     config.cursor = "w-resize";
33461     
33462     Roo.bootstrap.layout.Split.call(this, config);
33463     if(this.split){
33464         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33465         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33466         this.split.el.addClass("roo-layout-split-h");
33467     }
33468     
33469 };
33470 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33471     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33472     
33473     onRender: function(ctr, pos)
33474     {
33475         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33476         var size = this.config.initialSize || this.config.width;
33477         if(typeof size != "undefined"){
33478             this.el.setWidth(size);
33479         }
33480     },
33481     
33482     getBox : function(){
33483         if(this.collapsed){
33484             return this.collapsedEl.getBox();
33485         }
33486         var box = this.el.getBox();
33487         if(this.split){
33488             box.width += this.split.el.getWidth();
33489         }
33490         return box;
33491     },
33492     
33493     updateBox : function(box){
33494         if(this.split && !this.collapsed){
33495             var sw = this.split.el.getWidth();
33496             box.width -= sw;
33497             this.split.el.setLeft(box.x+box.width);
33498             this.split.el.setTop(box.y);
33499             this.split.el.setHeight(box.height);
33500         }
33501         if(this.collapsed){
33502             this.updateBody(null, box.height);
33503         }
33504         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33505     }
33506 });
33507 Roo.namespace("Roo.bootstrap.panel");/*
33508  * Based on:
33509  * Ext JS Library 1.1.1
33510  * Copyright(c) 2006-2007, Ext JS, LLC.
33511  *
33512  * Originally Released Under LGPL - original licence link has changed is not relivant.
33513  *
33514  * Fork - LGPL
33515  * <script type="text/javascript">
33516  */
33517 /**
33518  * @class Roo.ContentPanel
33519  * @extends Roo.util.Observable
33520  * A basic ContentPanel element.
33521  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33522  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33523  * @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
33524  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33525  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33526  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33527  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33528  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33529  * @cfg {String} title          The title for this panel
33530  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33531  * @cfg {String} url            Calls {@link #setUrl} with this value
33532  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33533  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33534  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33535  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33536
33537  * @constructor
33538  * Create a new ContentPanel.
33539  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33540  * @param {String/Object} config A string to set only the title or a config object
33541  * @param {String} content (optional) Set the HTML content for this panel
33542  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33543  */
33544 Roo.bootstrap.panel.Content = function( config){
33545     
33546     var el = config.el;
33547     var content = config.content;
33548
33549     if(config.autoCreate){ // xtype is available if this is called from factory
33550         el = Roo.id();
33551     }
33552     this.el = Roo.get(el);
33553     if(!this.el && config && config.autoCreate){
33554         if(typeof config.autoCreate == "object"){
33555             if(!config.autoCreate.id){
33556                 config.autoCreate.id = config.id||el;
33557             }
33558             this.el = Roo.DomHelper.append(document.body,
33559                         config.autoCreate, true);
33560         }else{
33561             var elcfg =  {   tag: "div",
33562                             cls: "roo-layout-inactive-content",
33563                             id: config.id||el
33564                             };
33565             if (config.html) {
33566                 elcfg.html = config.html;
33567                 
33568             }
33569                         
33570             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33571         }
33572     } 
33573     this.closable = false;
33574     this.loaded = false;
33575     this.active = false;
33576    
33577       
33578     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33579         
33580         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33581         
33582         this.wrapEl = this.el.wrap();
33583         var ti = [];
33584         if (config.toolbar.items) {
33585             ti = config.toolbar.items ;
33586             delete config.toolbar.items ;
33587         }
33588         
33589         var nitems = [];
33590         this.toolbar.render(this.wrapEl, 'before');
33591         for(var i =0;i < ti.length;i++) {
33592           //  Roo.log(['add child', items[i]]);
33593             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33594         }
33595         this.toolbar.items = nitems;
33596         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33597         delete config.toolbar;
33598         
33599     }
33600     /*
33601     // xtype created footer. - not sure if will work as we normally have to render first..
33602     if (this.footer && !this.footer.el && this.footer.xtype) {
33603         if (!this.wrapEl) {
33604             this.wrapEl = this.el.wrap();
33605         }
33606     
33607         this.footer.container = this.wrapEl.createChild();
33608          
33609         this.footer = Roo.factory(this.footer, Roo);
33610         
33611     }
33612     */
33613     
33614      if(typeof config == "string"){
33615         this.title = config;
33616     }else{
33617         Roo.apply(this, config);
33618     }
33619     
33620     if(this.resizeEl){
33621         this.resizeEl = Roo.get(this.resizeEl, true);
33622     }else{
33623         this.resizeEl = this.el;
33624     }
33625     // handle view.xtype
33626     
33627  
33628     
33629     
33630     this.addEvents({
33631         /**
33632          * @event activate
33633          * Fires when this panel is activated. 
33634          * @param {Roo.ContentPanel} this
33635          */
33636         "activate" : true,
33637         /**
33638          * @event deactivate
33639          * Fires when this panel is activated. 
33640          * @param {Roo.ContentPanel} this
33641          */
33642         "deactivate" : true,
33643
33644         /**
33645          * @event resize
33646          * Fires when this panel is resized if fitToFrame is true.
33647          * @param {Roo.ContentPanel} this
33648          * @param {Number} width The width after any component adjustments
33649          * @param {Number} height The height after any component adjustments
33650          */
33651         "resize" : true,
33652         
33653          /**
33654          * @event render
33655          * Fires when this tab is created
33656          * @param {Roo.ContentPanel} this
33657          */
33658         "render" : true
33659         
33660         
33661         
33662     });
33663     
33664
33665     
33666     
33667     if(this.autoScroll){
33668         this.resizeEl.setStyle("overflow", "auto");
33669     } else {
33670         // fix randome scrolling
33671         //this.el.on('scroll', function() {
33672         //    Roo.log('fix random scolling');
33673         //    this.scrollTo('top',0); 
33674         //});
33675     }
33676     content = content || this.content;
33677     if(content){
33678         this.setContent(content);
33679     }
33680     if(config && config.url){
33681         this.setUrl(this.url, this.params, this.loadOnce);
33682     }
33683     
33684     
33685     
33686     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33687     
33688     if (this.view && typeof(this.view.xtype) != 'undefined') {
33689         this.view.el = this.el.appendChild(document.createElement("div"));
33690         this.view = Roo.factory(this.view); 
33691         this.view.render  &&  this.view.render(false, '');  
33692     }
33693     
33694     
33695     this.fireEvent('render', this);
33696 };
33697
33698 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33699     tabTip:'',
33700     setRegion : function(region){
33701         this.region = region;
33702         if(region){
33703            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33704         }else{
33705            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33706         } 
33707     },
33708     
33709     /**
33710      * Returns the toolbar for this Panel if one was configured. 
33711      * @return {Roo.Toolbar} 
33712      */
33713     getToolbar : function(){
33714         return this.toolbar;
33715     },
33716     
33717     setActiveState : function(active){
33718         this.active = active;
33719         if(!active){
33720             this.fireEvent("deactivate", this);
33721         }else{
33722             this.fireEvent("activate", this);
33723         }
33724     },
33725     /**
33726      * Updates this panel's element
33727      * @param {String} content The new content
33728      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33729     */
33730     setContent : function(content, loadScripts){
33731         this.el.update(content, loadScripts);
33732     },
33733
33734     ignoreResize : function(w, h){
33735         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33736             return true;
33737         }else{
33738             this.lastSize = {width: w, height: h};
33739             return false;
33740         }
33741     },
33742     /**
33743      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33744      * @return {Roo.UpdateManager} The UpdateManager
33745      */
33746     getUpdateManager : function(){
33747         return this.el.getUpdateManager();
33748     },
33749      /**
33750      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33751      * @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:
33752 <pre><code>
33753 panel.load({
33754     url: "your-url.php",
33755     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33756     callback: yourFunction,
33757     scope: yourObject, //(optional scope)
33758     discardUrl: false,
33759     nocache: false,
33760     text: "Loading...",
33761     timeout: 30,
33762     scripts: false
33763 });
33764 </code></pre>
33765      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33766      * 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.
33767      * @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}
33768      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33769      * @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.
33770      * @return {Roo.ContentPanel} this
33771      */
33772     load : function(){
33773         var um = this.el.getUpdateManager();
33774         um.update.apply(um, arguments);
33775         return this;
33776     },
33777
33778
33779     /**
33780      * 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.
33781      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33782      * @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)
33783      * @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)
33784      * @return {Roo.UpdateManager} The UpdateManager
33785      */
33786     setUrl : function(url, params, loadOnce){
33787         if(this.refreshDelegate){
33788             this.removeListener("activate", this.refreshDelegate);
33789         }
33790         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33791         this.on("activate", this.refreshDelegate);
33792         return this.el.getUpdateManager();
33793     },
33794     
33795     _handleRefresh : function(url, params, loadOnce){
33796         if(!loadOnce || !this.loaded){
33797             var updater = this.el.getUpdateManager();
33798             updater.update(url, params, this._setLoaded.createDelegate(this));
33799         }
33800     },
33801     
33802     _setLoaded : function(){
33803         this.loaded = true;
33804     }, 
33805     
33806     /**
33807      * Returns this panel's id
33808      * @return {String} 
33809      */
33810     getId : function(){
33811         return this.el.id;
33812     },
33813     
33814     /** 
33815      * Returns this panel's element - used by regiosn to add.
33816      * @return {Roo.Element} 
33817      */
33818     getEl : function(){
33819         return this.wrapEl || this.el;
33820     },
33821     
33822    
33823     
33824     adjustForComponents : function(width, height)
33825     {
33826         //Roo.log('adjustForComponents ');
33827         if(this.resizeEl != this.el){
33828             width -= this.el.getFrameWidth('lr');
33829             height -= this.el.getFrameWidth('tb');
33830         }
33831         if(this.toolbar){
33832             var te = this.toolbar.getEl();
33833             height -= te.getHeight();
33834             te.setWidth(width);
33835         }
33836         if(this.footer){
33837             var te = this.footer.getEl();
33838             Roo.log("footer:" + te.getHeight());
33839             
33840             height -= te.getHeight();
33841             te.setWidth(width);
33842         }
33843         
33844         
33845         if(this.adjustments){
33846             width += this.adjustments[0];
33847             height += this.adjustments[1];
33848         }
33849         return {"width": width, "height": height};
33850     },
33851     
33852     setSize : function(width, height){
33853         if(this.fitToFrame && !this.ignoreResize(width, height)){
33854             if(this.fitContainer && this.resizeEl != this.el){
33855                 this.el.setSize(width, height);
33856             }
33857             var size = this.adjustForComponents(width, height);
33858             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33859             this.fireEvent('resize', this, size.width, size.height);
33860         }
33861     },
33862     
33863     /**
33864      * Returns this panel's title
33865      * @return {String} 
33866      */
33867     getTitle : function(){
33868         return this.title;
33869     },
33870     
33871     /**
33872      * Set this panel's title
33873      * @param {String} title
33874      */
33875     setTitle : function(title){
33876         this.title = title;
33877         if(this.region){
33878             this.region.updatePanelTitle(this, title);
33879         }
33880     },
33881     
33882     /**
33883      * Returns true is this panel was configured to be closable
33884      * @return {Boolean} 
33885      */
33886     isClosable : function(){
33887         return this.closable;
33888     },
33889     
33890     beforeSlide : function(){
33891         this.el.clip();
33892         this.resizeEl.clip();
33893     },
33894     
33895     afterSlide : function(){
33896         this.el.unclip();
33897         this.resizeEl.unclip();
33898     },
33899     
33900     /**
33901      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33902      *   Will fail silently if the {@link #setUrl} method has not been called.
33903      *   This does not activate the panel, just updates its content.
33904      */
33905     refresh : function(){
33906         if(this.refreshDelegate){
33907            this.loaded = false;
33908            this.refreshDelegate();
33909         }
33910     },
33911     
33912     /**
33913      * Destroys this panel
33914      */
33915     destroy : function(){
33916         this.el.removeAllListeners();
33917         var tempEl = document.createElement("span");
33918         tempEl.appendChild(this.el.dom);
33919         tempEl.innerHTML = "";
33920         this.el.remove();
33921         this.el = null;
33922     },
33923     
33924     /**
33925      * form - if the content panel contains a form - this is a reference to it.
33926      * @type {Roo.form.Form}
33927      */
33928     form : false,
33929     /**
33930      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33931      *    This contains a reference to it.
33932      * @type {Roo.View}
33933      */
33934     view : false,
33935     
33936       /**
33937      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33938      * <pre><code>
33939
33940 layout.addxtype({
33941        xtype : 'Form',
33942        items: [ .... ]
33943    }
33944 );
33945
33946 </code></pre>
33947      * @param {Object} cfg Xtype definition of item to add.
33948      */
33949     
33950     
33951     getChildContainer: function () {
33952         return this.getEl();
33953     }
33954     
33955     
33956     /*
33957         var  ret = new Roo.factory(cfg);
33958         return ret;
33959         
33960         
33961         // add form..
33962         if (cfg.xtype.match(/^Form$/)) {
33963             
33964             var el;
33965             //if (this.footer) {
33966             //    el = this.footer.container.insertSibling(false, 'before');
33967             //} else {
33968                 el = this.el.createChild();
33969             //}
33970
33971             this.form = new  Roo.form.Form(cfg);
33972             
33973             
33974             if ( this.form.allItems.length) {
33975                 this.form.render(el.dom);
33976             }
33977             return this.form;
33978         }
33979         // should only have one of theses..
33980         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33981             // views.. should not be just added - used named prop 'view''
33982             
33983             cfg.el = this.el.appendChild(document.createElement("div"));
33984             // factory?
33985             
33986             var ret = new Roo.factory(cfg);
33987              
33988              ret.render && ret.render(false, ''); // render blank..
33989             this.view = ret;
33990             return ret;
33991         }
33992         return false;
33993     }
33994     \*/
33995 });
33996  
33997 /**
33998  * @class Roo.bootstrap.panel.Grid
33999  * @extends Roo.bootstrap.panel.Content
34000  * @constructor
34001  * Create a new GridPanel.
34002  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34003  * @param {Object} config A the config object
34004   
34005  */
34006
34007
34008
34009 Roo.bootstrap.panel.Grid = function(config)
34010 {
34011     
34012       
34013     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34014         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34015
34016     config.el = this.wrapper;
34017     //this.el = this.wrapper;
34018     
34019       if (config.container) {
34020         // ctor'ed from a Border/panel.grid
34021         
34022         
34023         this.wrapper.setStyle("overflow", "hidden");
34024         this.wrapper.addClass('roo-grid-container');
34025
34026     }
34027     
34028     
34029     if(config.toolbar){
34030         var tool_el = this.wrapper.createChild();    
34031         this.toolbar = Roo.factory(config.toolbar);
34032         var ti = [];
34033         if (config.toolbar.items) {
34034             ti = config.toolbar.items ;
34035             delete config.toolbar.items ;
34036         }
34037         
34038         var nitems = [];
34039         this.toolbar.render(tool_el);
34040         for(var i =0;i < ti.length;i++) {
34041           //  Roo.log(['add child', items[i]]);
34042             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34043         }
34044         this.toolbar.items = nitems;
34045         
34046         delete config.toolbar;
34047     }
34048     
34049     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34050     config.grid.scrollBody = true;;
34051     config.grid.monitorWindowResize = false; // turn off autosizing
34052     config.grid.autoHeight = false;
34053     config.grid.autoWidth = false;
34054     
34055     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34056     
34057     if (config.background) {
34058         // render grid on panel activation (if panel background)
34059         this.on('activate', function(gp) {
34060             if (!gp.grid.rendered) {
34061                 gp.grid.render(el);
34062                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34063
34064             }
34065         });
34066             
34067     } else {
34068         this.grid.render(this.wrapper);
34069         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34070
34071     }
34072     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34073     // ??? needed ??? config.el = this.wrapper;
34074     
34075     
34076     
34077   
34078     // xtype created footer. - not sure if will work as we normally have to render first..
34079     if (this.footer && !this.footer.el && this.footer.xtype) {
34080         
34081         var ctr = this.grid.getView().getFooterPanel(true);
34082         this.footer.dataSource = this.grid.dataSource;
34083         this.footer = Roo.factory(this.footer, Roo);
34084         this.footer.render(ctr);
34085         
34086     }
34087     
34088     
34089     
34090     
34091      
34092 };
34093
34094 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34095     getId : function(){
34096         return this.grid.id;
34097     },
34098     
34099     /**
34100      * Returns the grid for this panel
34101      * @return {Roo.bootstrap.Table} 
34102      */
34103     getGrid : function(){
34104         return this.grid;    
34105     },
34106     
34107     setSize : function(width, height){
34108         if(!this.ignoreResize(width, height)){
34109             var grid = this.grid;
34110             var size = this.adjustForComponents(width, height);
34111             var gridel = grid.getGridEl();
34112             gridel.setSize(size.width, size.height);
34113             /*
34114             var thd = grid.getGridEl().select('thead',true).first();
34115             var tbd = grid.getGridEl().select('tbody', true).first();
34116             if (tbd) {
34117                 tbd.setSize(width, height - thd.getHeight());
34118             }
34119             */
34120             grid.autoSize();
34121         }
34122     },
34123      
34124     
34125     
34126     beforeSlide : function(){
34127         this.grid.getView().scroller.clip();
34128     },
34129     
34130     afterSlide : function(){
34131         this.grid.getView().scroller.unclip();
34132     },
34133     
34134     destroy : function(){
34135         this.grid.destroy();
34136         delete this.grid;
34137         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34138     }
34139 });
34140
34141 /**
34142  * @class Roo.bootstrap.panel.Nest
34143  * @extends Roo.bootstrap.panel.Content
34144  * @constructor
34145  * Create a new Panel, that can contain a layout.Border.
34146  * 
34147  * 
34148  * @param {Roo.BorderLayout} layout The layout for this panel
34149  * @param {String/Object} config A string to set only the title or a config object
34150  */
34151 Roo.bootstrap.panel.Nest = function(config)
34152 {
34153     // construct with only one argument..
34154     /* FIXME - implement nicer consturctors
34155     if (layout.layout) {
34156         config = layout;
34157         layout = config.layout;
34158         delete config.layout;
34159     }
34160     if (layout.xtype && !layout.getEl) {
34161         // then layout needs constructing..
34162         layout = Roo.factory(layout, Roo);
34163     }
34164     */
34165     
34166     config.el =  config.layout.getEl();
34167     
34168     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34169     
34170     config.layout.monitorWindowResize = false; // turn off autosizing
34171     this.layout = config.layout;
34172     this.layout.getEl().addClass("roo-layout-nested-layout");
34173     
34174     
34175     
34176     
34177 };
34178
34179 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34180
34181     setSize : function(width, height){
34182         if(!this.ignoreResize(width, height)){
34183             var size = this.adjustForComponents(width, height);
34184             var el = this.layout.getEl();
34185             el.setSize(size.width, size.height);
34186             var touch = el.dom.offsetWidth;
34187             this.layout.layout();
34188             // ie requires a double layout on the first pass
34189             if(Roo.isIE && !this.initialized){
34190                 this.initialized = true;
34191                 this.layout.layout();
34192             }
34193         }
34194     },
34195     
34196     // activate all subpanels if not currently active..
34197     
34198     setActiveState : function(active){
34199         this.active = active;
34200         if(!active){
34201             this.fireEvent("deactivate", this);
34202             return;
34203         }
34204         
34205         this.fireEvent("activate", this);
34206         // not sure if this should happen before or after..
34207         if (!this.layout) {
34208             return; // should not happen..
34209         }
34210         var reg = false;
34211         for (var r in this.layout.regions) {
34212             reg = this.layout.getRegion(r);
34213             if (reg.getActivePanel()) {
34214                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34215                 reg.setActivePanel(reg.getActivePanel());
34216                 continue;
34217             }
34218             if (!reg.panels.length) {
34219                 continue;
34220             }
34221             reg.showPanel(reg.getPanel(0));
34222         }
34223         
34224         
34225         
34226         
34227     },
34228     
34229     /**
34230      * Returns the nested BorderLayout for this panel
34231      * @return {Roo.BorderLayout} 
34232      */
34233     getLayout : function(){
34234         return this.layout;
34235     },
34236     
34237      /**
34238      * Adds a xtype elements to the layout of the nested panel
34239      * <pre><code>
34240
34241 panel.addxtype({
34242        xtype : 'ContentPanel',
34243        region: 'west',
34244        items: [ .... ]
34245    }
34246 );
34247
34248 panel.addxtype({
34249         xtype : 'NestedLayoutPanel',
34250         region: 'west',
34251         layout: {
34252            center: { },
34253            west: { }   
34254         },
34255         items : [ ... list of content panels or nested layout panels.. ]
34256    }
34257 );
34258 </code></pre>
34259      * @param {Object} cfg Xtype definition of item to add.
34260      */
34261     addxtype : function(cfg) {
34262         return this.layout.addxtype(cfg);
34263     
34264     }
34265 });        /*
34266  * Based on:
34267  * Ext JS Library 1.1.1
34268  * Copyright(c) 2006-2007, Ext JS, LLC.
34269  *
34270  * Originally Released Under LGPL - original licence link has changed is not relivant.
34271  *
34272  * Fork - LGPL
34273  * <script type="text/javascript">
34274  */
34275 /**
34276  * @class Roo.TabPanel
34277  * @extends Roo.util.Observable
34278  * A lightweight tab container.
34279  * <br><br>
34280  * Usage:
34281  * <pre><code>
34282 // basic tabs 1, built from existing content
34283 var tabs = new Roo.TabPanel("tabs1");
34284 tabs.addTab("script", "View Script");
34285 tabs.addTab("markup", "View Markup");
34286 tabs.activate("script");
34287
34288 // more advanced tabs, built from javascript
34289 var jtabs = new Roo.TabPanel("jtabs");
34290 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34291
34292 // set up the UpdateManager
34293 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34294 var updater = tab2.getUpdateManager();
34295 updater.setDefaultUrl("ajax1.htm");
34296 tab2.on('activate', updater.refresh, updater, true);
34297
34298 // Use setUrl for Ajax loading
34299 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34300 tab3.setUrl("ajax2.htm", null, true);
34301
34302 // Disabled tab
34303 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34304 tab4.disable();
34305
34306 jtabs.activate("jtabs-1");
34307  * </code></pre>
34308  * @constructor
34309  * Create a new TabPanel.
34310  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34311  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34312  */
34313 Roo.bootstrap.panel.Tabs = function(config){
34314     /**
34315     * The container element for this TabPanel.
34316     * @type Roo.Element
34317     */
34318     this.el = Roo.get(config.el);
34319     delete config.el;
34320     if(config){
34321         if(typeof config == "boolean"){
34322             this.tabPosition = config ? "bottom" : "top";
34323         }else{
34324             Roo.apply(this, config);
34325         }
34326     }
34327     
34328     if(this.tabPosition == "bottom"){
34329         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34330         this.el.addClass("roo-tabs-bottom");
34331     }
34332     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34333     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34334     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34335     if(Roo.isIE){
34336         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34337     }
34338     if(this.tabPosition != "bottom"){
34339         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34340          * @type Roo.Element
34341          */
34342         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34343         this.el.addClass("roo-tabs-top");
34344     }
34345     this.items = [];
34346
34347     this.bodyEl.setStyle("position", "relative");
34348
34349     this.active = null;
34350     this.activateDelegate = this.activate.createDelegate(this);
34351
34352     this.addEvents({
34353         /**
34354          * @event tabchange
34355          * Fires when the active tab changes
34356          * @param {Roo.TabPanel} this
34357          * @param {Roo.TabPanelItem} activePanel The new active tab
34358          */
34359         "tabchange": true,
34360         /**
34361          * @event beforetabchange
34362          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34363          * @param {Roo.TabPanel} this
34364          * @param {Object} e Set cancel to true on this object to cancel the tab change
34365          * @param {Roo.TabPanelItem} tab The tab being changed to
34366          */
34367         "beforetabchange" : true
34368     });
34369
34370     Roo.EventManager.onWindowResize(this.onResize, this);
34371     this.cpad = this.el.getPadding("lr");
34372     this.hiddenCount = 0;
34373
34374
34375     // toolbar on the tabbar support...
34376     if (this.toolbar) {
34377         alert("no toolbar support yet");
34378         this.toolbar  = false;
34379         /*
34380         var tcfg = this.toolbar;
34381         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34382         this.toolbar = new Roo.Toolbar(tcfg);
34383         if (Roo.isSafari) {
34384             var tbl = tcfg.container.child('table', true);
34385             tbl.setAttribute('width', '100%');
34386         }
34387         */
34388         
34389     }
34390    
34391
34392
34393     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34394 };
34395
34396 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34397     /*
34398      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34399      */
34400     tabPosition : "top",
34401     /*
34402      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34403      */
34404     currentTabWidth : 0,
34405     /*
34406      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34407      */
34408     minTabWidth : 40,
34409     /*
34410      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34411      */
34412     maxTabWidth : 250,
34413     /*
34414      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34415      */
34416     preferredTabWidth : 175,
34417     /*
34418      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34419      */
34420     resizeTabs : false,
34421     /*
34422      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34423      */
34424     monitorResize : true,
34425     /*
34426      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34427      */
34428     toolbar : false,
34429
34430     /**
34431      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34432      * @param {String} id The id of the div to use <b>or create</b>
34433      * @param {String} text The text for the tab
34434      * @param {String} content (optional) Content to put in the TabPanelItem body
34435      * @param {Boolean} closable (optional) True to create a close icon on the tab
34436      * @return {Roo.TabPanelItem} The created TabPanelItem
34437      */
34438     addTab : function(id, text, content, closable)
34439     {
34440         var item = new Roo.bootstrap.panel.TabItem({
34441             panel: this,
34442             id : id,
34443             text : text,
34444             closable : closable
34445         });
34446         this.addTabItem(item);
34447         if(content){
34448             item.setContent(content);
34449         }
34450         return item;
34451     },
34452
34453     /**
34454      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34455      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34456      * @return {Roo.TabPanelItem}
34457      */
34458     getTab : function(id){
34459         return this.items[id];
34460     },
34461
34462     /**
34463      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34464      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34465      */
34466     hideTab : function(id){
34467         var t = this.items[id];
34468         if(!t.isHidden()){
34469            t.setHidden(true);
34470            this.hiddenCount++;
34471            this.autoSizeTabs();
34472         }
34473     },
34474
34475     /**
34476      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34477      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34478      */
34479     unhideTab : function(id){
34480         var t = this.items[id];
34481         if(t.isHidden()){
34482            t.setHidden(false);
34483            this.hiddenCount--;
34484            this.autoSizeTabs();
34485         }
34486     },
34487
34488     /**
34489      * Adds an existing {@link Roo.TabPanelItem}.
34490      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34491      */
34492     addTabItem : function(item){
34493         this.items[item.id] = item;
34494         this.items.push(item);
34495       //  if(this.resizeTabs){
34496     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34497   //         this.autoSizeTabs();
34498 //        }else{
34499 //            item.autoSize();
34500        // }
34501     },
34502
34503     /**
34504      * Removes a {@link Roo.TabPanelItem}.
34505      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34506      */
34507     removeTab : function(id){
34508         var items = this.items;
34509         var tab = items[id];
34510         if(!tab) { return; }
34511         var index = items.indexOf(tab);
34512         if(this.active == tab && items.length > 1){
34513             var newTab = this.getNextAvailable(index);
34514             if(newTab) {
34515                 newTab.activate();
34516             }
34517         }
34518         this.stripEl.dom.removeChild(tab.pnode.dom);
34519         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34520             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34521         }
34522         items.splice(index, 1);
34523         delete this.items[tab.id];
34524         tab.fireEvent("close", tab);
34525         tab.purgeListeners();
34526         this.autoSizeTabs();
34527     },
34528
34529     getNextAvailable : function(start){
34530         var items = this.items;
34531         var index = start;
34532         // look for a next tab that will slide over to
34533         // replace the one being removed
34534         while(index < items.length){
34535             var item = items[++index];
34536             if(item && !item.isHidden()){
34537                 return item;
34538             }
34539         }
34540         // if one isn't found select the previous tab (on the left)
34541         index = start;
34542         while(index >= 0){
34543             var item = items[--index];
34544             if(item && !item.isHidden()){
34545                 return item;
34546             }
34547         }
34548         return null;
34549     },
34550
34551     /**
34552      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34553      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34554      */
34555     disableTab : function(id){
34556         var tab = this.items[id];
34557         if(tab && this.active != tab){
34558             tab.disable();
34559         }
34560     },
34561
34562     /**
34563      * Enables a {@link Roo.TabPanelItem} that is disabled.
34564      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34565      */
34566     enableTab : function(id){
34567         var tab = this.items[id];
34568         tab.enable();
34569     },
34570
34571     /**
34572      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34573      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34574      * @return {Roo.TabPanelItem} The TabPanelItem.
34575      */
34576     activate : function(id){
34577         var tab = this.items[id];
34578         if(!tab){
34579             return null;
34580         }
34581         if(tab == this.active || tab.disabled){
34582             return tab;
34583         }
34584         var e = {};
34585         this.fireEvent("beforetabchange", this, e, tab);
34586         if(e.cancel !== true && !tab.disabled){
34587             if(this.active){
34588                 this.active.hide();
34589             }
34590             this.active = this.items[id];
34591             this.active.show();
34592             this.fireEvent("tabchange", this, this.active);
34593         }
34594         return tab;
34595     },
34596
34597     /**
34598      * Gets the active {@link Roo.TabPanelItem}.
34599      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34600      */
34601     getActiveTab : function(){
34602         return this.active;
34603     },
34604
34605     /**
34606      * Updates the tab body element to fit the height of the container element
34607      * for overflow scrolling
34608      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34609      */
34610     syncHeight : function(targetHeight){
34611         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34612         var bm = this.bodyEl.getMargins();
34613         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34614         this.bodyEl.setHeight(newHeight);
34615         return newHeight;
34616     },
34617
34618     onResize : function(){
34619         if(this.monitorResize){
34620             this.autoSizeTabs();
34621         }
34622     },
34623
34624     /**
34625      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34626      */
34627     beginUpdate : function(){
34628         this.updating = true;
34629     },
34630
34631     /**
34632      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34633      */
34634     endUpdate : function(){
34635         this.updating = false;
34636         this.autoSizeTabs();
34637     },
34638
34639     /**
34640      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34641      */
34642     autoSizeTabs : function(){
34643         var count = this.items.length;
34644         var vcount = count - this.hiddenCount;
34645         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34646             return;
34647         }
34648         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34649         var availWidth = Math.floor(w / vcount);
34650         var b = this.stripBody;
34651         if(b.getWidth() > w){
34652             var tabs = this.items;
34653             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34654             if(availWidth < this.minTabWidth){
34655                 /*if(!this.sleft){    // incomplete scrolling code
34656                     this.createScrollButtons();
34657                 }
34658                 this.showScroll();
34659                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34660             }
34661         }else{
34662             if(this.currentTabWidth < this.preferredTabWidth){
34663                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34664             }
34665         }
34666     },
34667
34668     /**
34669      * Returns the number of tabs in this TabPanel.
34670      * @return {Number}
34671      */
34672      getCount : function(){
34673          return this.items.length;
34674      },
34675
34676     /**
34677      * Resizes all the tabs to the passed width
34678      * @param {Number} The new width
34679      */
34680     setTabWidth : function(width){
34681         this.currentTabWidth = width;
34682         for(var i = 0, len = this.items.length; i < len; i++) {
34683                 if(!this.items[i].isHidden()) {
34684                 this.items[i].setWidth(width);
34685             }
34686         }
34687     },
34688
34689     /**
34690      * Destroys this TabPanel
34691      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34692      */
34693     destroy : function(removeEl){
34694         Roo.EventManager.removeResizeListener(this.onResize, this);
34695         for(var i = 0, len = this.items.length; i < len; i++){
34696             this.items[i].purgeListeners();
34697         }
34698         if(removeEl === true){
34699             this.el.update("");
34700             this.el.remove();
34701         }
34702     },
34703     
34704     createStrip : function(container)
34705     {
34706         var strip = document.createElement("nav");
34707         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34708         container.appendChild(strip);
34709         return strip;
34710     },
34711     
34712     createStripList : function(strip)
34713     {
34714         // div wrapper for retard IE
34715         // returns the "tr" element.
34716         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34717         //'<div class="x-tabs-strip-wrap">'+
34718           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34719           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34720         return strip.firstChild; //.firstChild.firstChild.firstChild;
34721     },
34722     createBody : function(container)
34723     {
34724         var body = document.createElement("div");
34725         Roo.id(body, "tab-body");
34726         //Roo.fly(body).addClass("x-tabs-body");
34727         Roo.fly(body).addClass("tab-content");
34728         container.appendChild(body);
34729         return body;
34730     },
34731     createItemBody :function(bodyEl, id){
34732         var body = Roo.getDom(id);
34733         if(!body){
34734             body = document.createElement("div");
34735             body.id = id;
34736         }
34737         //Roo.fly(body).addClass("x-tabs-item-body");
34738         Roo.fly(body).addClass("tab-pane");
34739          bodyEl.insertBefore(body, bodyEl.firstChild);
34740         return body;
34741     },
34742     /** @private */
34743     createStripElements :  function(stripEl, text, closable)
34744     {
34745         var td = document.createElement("li"); // was td..
34746         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34747         //stripEl.appendChild(td);
34748         /*if(closable){
34749             td.className = "x-tabs-closable";
34750             if(!this.closeTpl){
34751                 this.closeTpl = new Roo.Template(
34752                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34753                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34754                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34755                 );
34756             }
34757             var el = this.closeTpl.overwrite(td, {"text": text});
34758             var close = el.getElementsByTagName("div")[0];
34759             var inner = el.getElementsByTagName("em")[0];
34760             return {"el": el, "close": close, "inner": inner};
34761         } else {
34762         */
34763         // not sure what this is..
34764             if(!this.tabTpl){
34765                 //this.tabTpl = new Roo.Template(
34766                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34767                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34768                 //);
34769                 this.tabTpl = new Roo.Template(
34770                    '<a href="#">' +
34771                    '<span unselectable="on"' +
34772                             (this.disableTooltips ? '' : ' title="{text}"') +
34773                             ' >{text}</span></span></a>'
34774                 );
34775                 
34776             }
34777             var el = this.tabTpl.overwrite(td, {"text": text});
34778             var inner = el.getElementsByTagName("span")[0];
34779             return {"el": el, "inner": inner};
34780         //}
34781     }
34782         
34783     
34784 });
34785
34786 /**
34787  * @class Roo.TabPanelItem
34788  * @extends Roo.util.Observable
34789  * Represents an individual item (tab plus body) in a TabPanel.
34790  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34791  * @param {String} id The id of this TabPanelItem
34792  * @param {String} text The text for the tab of this TabPanelItem
34793  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34794  */
34795 Roo.bootstrap.panel.TabItem = function(config){
34796     /**
34797      * The {@link Roo.TabPanel} this TabPanelItem belongs to
34798      * @type Roo.TabPanel
34799      */
34800     this.tabPanel = config.panel;
34801     /**
34802      * The id for this TabPanelItem
34803      * @type String
34804      */
34805     this.id = config.id;
34806     /** @private */
34807     this.disabled = false;
34808     /** @private */
34809     this.text = config.text;
34810     /** @private */
34811     this.loaded = false;
34812     this.closable = config.closable;
34813
34814     /**
34815      * The body element for this TabPanelItem.
34816      * @type Roo.Element
34817      */
34818     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34819     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34820     this.bodyEl.setStyle("display", "block");
34821     this.bodyEl.setStyle("zoom", "1");
34822     //this.hideAction();
34823
34824     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34825     /** @private */
34826     this.el = Roo.get(els.el);
34827     this.inner = Roo.get(els.inner, true);
34828     this.textEl = Roo.get(this.el.dom.firstChild, true);
34829     this.pnode = Roo.get(els.el.parentNode, true);
34830     this.el.on("mousedown", this.onTabMouseDown, this);
34831     this.el.on("click", this.onTabClick, this);
34832     /** @private */
34833     if(config.closable){
34834         var c = Roo.get(els.close, true);
34835         c.dom.title = this.closeText;
34836         c.addClassOnOver("close-over");
34837         c.on("click", this.closeClick, this);
34838      }
34839
34840     this.addEvents({
34841          /**
34842          * @event activate
34843          * Fires when this tab becomes the active tab.
34844          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34845          * @param {Roo.TabPanelItem} this
34846          */
34847         "activate": true,
34848         /**
34849          * @event beforeclose
34850          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34851          * @param {Roo.TabPanelItem} this
34852          * @param {Object} e Set cancel to true on this object to cancel the close.
34853          */
34854         "beforeclose": true,
34855         /**
34856          * @event close
34857          * Fires when this tab is closed.
34858          * @param {Roo.TabPanelItem} this
34859          */
34860          "close": true,
34861         /**
34862          * @event deactivate
34863          * Fires when this tab is no longer the active tab.
34864          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34865          * @param {Roo.TabPanelItem} this
34866          */
34867          "deactivate" : true
34868     });
34869     this.hidden = false;
34870
34871     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34872 };
34873
34874 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34875            {
34876     purgeListeners : function(){
34877        Roo.util.Observable.prototype.purgeListeners.call(this);
34878        this.el.removeAllListeners();
34879     },
34880     /**
34881      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34882      */
34883     show : function(){
34884         this.pnode.addClass("active");
34885         this.showAction();
34886         if(Roo.isOpera){
34887             this.tabPanel.stripWrap.repaint();
34888         }
34889         this.fireEvent("activate", this.tabPanel, this);
34890     },
34891
34892     /**
34893      * Returns true if this tab is the active tab.
34894      * @return {Boolean}
34895      */
34896     isActive : function(){
34897         return this.tabPanel.getActiveTab() == this;
34898     },
34899
34900     /**
34901      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34902      */
34903     hide : function(){
34904         this.pnode.removeClass("active");
34905         this.hideAction();
34906         this.fireEvent("deactivate", this.tabPanel, this);
34907     },
34908
34909     hideAction : function(){
34910         this.bodyEl.hide();
34911         this.bodyEl.setStyle("position", "absolute");
34912         this.bodyEl.setLeft("-20000px");
34913         this.bodyEl.setTop("-20000px");
34914     },
34915
34916     showAction : function(){
34917         this.bodyEl.setStyle("position", "relative");
34918         this.bodyEl.setTop("");
34919         this.bodyEl.setLeft("");
34920         this.bodyEl.show();
34921     },
34922
34923     /**
34924      * Set the tooltip for the tab.
34925      * @param {String} tooltip The tab's tooltip
34926      */
34927     setTooltip : function(text){
34928         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34929             this.textEl.dom.qtip = text;
34930             this.textEl.dom.removeAttribute('title');
34931         }else{
34932             this.textEl.dom.title = text;
34933         }
34934     },
34935
34936     onTabClick : function(e){
34937         e.preventDefault();
34938         this.tabPanel.activate(this.id);
34939     },
34940
34941     onTabMouseDown : function(e){
34942         e.preventDefault();
34943         this.tabPanel.activate(this.id);
34944     },
34945 /*
34946     getWidth : function(){
34947         return this.inner.getWidth();
34948     },
34949
34950     setWidth : function(width){
34951         var iwidth = width - this.pnode.getPadding("lr");
34952         this.inner.setWidth(iwidth);
34953         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34954         this.pnode.setWidth(width);
34955     },
34956 */
34957     /**
34958      * Show or hide the tab
34959      * @param {Boolean} hidden True to hide or false to show.
34960      */
34961     setHidden : function(hidden){
34962         this.hidden = hidden;
34963         this.pnode.setStyle("display", hidden ? "none" : "");
34964     },
34965
34966     /**
34967      * Returns true if this tab is "hidden"
34968      * @return {Boolean}
34969      */
34970     isHidden : function(){
34971         return this.hidden;
34972     },
34973
34974     /**
34975      * Returns the text for this tab
34976      * @return {String}
34977      */
34978     getText : function(){
34979         return this.text;
34980     },
34981     /*
34982     autoSize : function(){
34983         //this.el.beginMeasure();
34984         this.textEl.setWidth(1);
34985         /*
34986          *  #2804 [new] Tabs in Roojs
34987          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34988          */
34989         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34990         //this.el.endMeasure();
34991     //},
34992
34993     /**
34994      * Sets the text for the tab (Note: this also sets the tooltip text)
34995      * @param {String} text The tab's text and tooltip
34996      */
34997     setText : function(text){
34998         this.text = text;
34999         this.textEl.update(text);
35000         this.setTooltip(text);
35001         //if(!this.tabPanel.resizeTabs){
35002         //    this.autoSize();
35003         //}
35004     },
35005     /**
35006      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35007      */
35008     activate : function(){
35009         this.tabPanel.activate(this.id);
35010     },
35011
35012     /**
35013      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35014      */
35015     disable : function(){
35016         if(this.tabPanel.active != this){
35017             this.disabled = true;
35018             this.pnode.addClass("disabled");
35019         }
35020     },
35021
35022     /**
35023      * Enables this TabPanelItem if it was previously disabled.
35024      */
35025     enable : function(){
35026         this.disabled = false;
35027         this.pnode.removeClass("disabled");
35028     },
35029
35030     /**
35031      * Sets the content for this TabPanelItem.
35032      * @param {String} content The content
35033      * @param {Boolean} loadScripts true to look for and load scripts
35034      */
35035     setContent : function(content, loadScripts){
35036         this.bodyEl.update(content, loadScripts);
35037     },
35038
35039     /**
35040      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35041      * @return {Roo.UpdateManager} The UpdateManager
35042      */
35043     getUpdateManager : function(){
35044         return this.bodyEl.getUpdateManager();
35045     },
35046
35047     /**
35048      * Set a URL to be used to load the content for this TabPanelItem.
35049      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35050      * @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)
35051      * @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)
35052      * @return {Roo.UpdateManager} The UpdateManager
35053      */
35054     setUrl : function(url, params, loadOnce){
35055         if(this.refreshDelegate){
35056             this.un('activate', this.refreshDelegate);
35057         }
35058         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35059         this.on("activate", this.refreshDelegate);
35060         return this.bodyEl.getUpdateManager();
35061     },
35062
35063     /** @private */
35064     _handleRefresh : function(url, params, loadOnce){
35065         if(!loadOnce || !this.loaded){
35066             var updater = this.bodyEl.getUpdateManager();
35067             updater.update(url, params, this._setLoaded.createDelegate(this));
35068         }
35069     },
35070
35071     /**
35072      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35073      *   Will fail silently if the setUrl method has not been called.
35074      *   This does not activate the panel, just updates its content.
35075      */
35076     refresh : function(){
35077         if(this.refreshDelegate){
35078            this.loaded = false;
35079            this.refreshDelegate();
35080         }
35081     },
35082
35083     /** @private */
35084     _setLoaded : function(){
35085         this.loaded = true;
35086     },
35087
35088     /** @private */
35089     closeClick : function(e){
35090         var o = {};
35091         e.stopEvent();
35092         this.fireEvent("beforeclose", this, o);
35093         if(o.cancel !== true){
35094             this.tabPanel.removeTab(this.id);
35095         }
35096     },
35097     /**
35098      * The text displayed in the tooltip for the close icon.
35099      * @type String
35100      */
35101     closeText : "Close this tab"
35102 });