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     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5717     if (this.sm) {
5718         this.sm.grid = this;
5719         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5720         this.sm = this.selModel;
5721         this.sm.xmodule = this.xmodule || false;
5722     }
5723     
5724     if (this.cm && typeof(this.cm.config) == 'undefined') {
5725         this.colModel = new Roo.grid.ColumnModel(this.cm);
5726         this.cm = this.colModel;
5727         this.cm.xmodule = this.xmodule || false;
5728     }
5729     if (this.store) {
5730         this.store= Roo.factory(this.store, Roo.data);
5731         this.ds = this.store;
5732         this.ds.xmodule = this.xmodule || false;
5733          
5734     }
5735     if (this.footer && this.store) {
5736         this.footer.dataSource = this.ds;
5737         this.footer = Roo.factory(this.footer);
5738     }
5739     
5740     /** @private */
5741     this.addEvents({
5742         /**
5743          * @event cellclick
5744          * Fires when a cell is clicked
5745          * @param {Roo.bootstrap.Table} this
5746          * @param {Roo.Element} el
5747          * @param {Number} rowIndex
5748          * @param {Number} columnIndex
5749          * @param {Roo.EventObject} e
5750          */
5751         "cellclick" : true,
5752         /**
5753          * @event celldblclick
5754          * Fires when a cell is double clicked
5755          * @param {Roo.bootstrap.Table} this
5756          * @param {Roo.Element} el
5757          * @param {Number} rowIndex
5758          * @param {Number} columnIndex
5759          * @param {Roo.EventObject} e
5760          */
5761         "celldblclick" : true,
5762         /**
5763          * @event rowclick
5764          * Fires when a row is clicked
5765          * @param {Roo.bootstrap.Table} this
5766          * @param {Roo.Element} el
5767          * @param {Number} rowIndex
5768          * @param {Roo.EventObject} e
5769          */
5770         "rowclick" : true,
5771         /**
5772          * @event rowdblclick
5773          * Fires when a row is double clicked
5774          * @param {Roo.bootstrap.Table} this
5775          * @param {Roo.Element} el
5776          * @param {Number} rowIndex
5777          * @param {Roo.EventObject} e
5778          */
5779         "rowdblclick" : true,
5780         /**
5781          * @event mouseover
5782          * Fires when a mouseover occur
5783          * @param {Roo.bootstrap.Table} this
5784          * @param {Roo.Element} el
5785          * @param {Number} rowIndex
5786          * @param {Number} columnIndex
5787          * @param {Roo.EventObject} e
5788          */
5789         "mouseover" : true,
5790         /**
5791          * @event mouseout
5792          * Fires when a mouseout occur
5793          * @param {Roo.bootstrap.Table} this
5794          * @param {Roo.Element} el
5795          * @param {Number} rowIndex
5796          * @param {Number} columnIndex
5797          * @param {Roo.EventObject} e
5798          */
5799         "mouseout" : true,
5800         /**
5801          * @event rowclass
5802          * Fires when a row is rendered, so you can change add a style to it.
5803          * @param {Roo.bootstrap.Table} this
5804          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5805          */
5806         'rowclass' : true,
5807           /**
5808          * @event rowsrendered
5809          * Fires when all the  rows have been rendered
5810          * @param {Roo.bootstrap.Table} this
5811          */
5812         'rowsrendered' : true,
5813         /**
5814          * @event contextmenu
5815          * The raw contextmenu event for the entire grid.
5816          * @param {Roo.EventObject} e
5817          */
5818         "contextmenu" : true,
5819         /**
5820          * @event rowcontextmenu
5821          * Fires when a row is right clicked
5822          * @param {Roo.bootstrap.Table} this
5823          * @param {Number} rowIndex
5824          * @param {Roo.EventObject} e
5825          */
5826         "rowcontextmenu" : true,
5827         /**
5828          * @event cellcontextmenu
5829          * Fires when a cell is right clicked
5830          * @param {Roo.bootstrap.Table} this
5831          * @param {Number} rowIndex
5832          * @param {Number} cellIndex
5833          * @param {Roo.EventObject} e
5834          */
5835          "cellcontextmenu" : true,
5836          /**
5837          * @event headercontextmenu
5838          * Fires when a header is right clicked
5839          * @param {Roo.bootstrap.Table} this
5840          * @param {Number} columnIndex
5841          * @param {Roo.EventObject} e
5842          */
5843         "headercontextmenu" : true
5844     });
5845 };
5846
5847 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5848     
5849     cls: false,
5850     align: false,
5851     bgcolor: false,
5852     border: false,
5853     cellpadding: false,
5854     cellspacing: false,
5855     frame: false,
5856     rules: false,
5857     sortable: false,
5858     summary: false,
5859     width: false,
5860     striped : false,
5861     scrollBody : false,
5862     bordered: false,
5863     hover:  false,
5864     condensed : false,
5865     responsive : false,
5866     sm : false,
5867     cm : false,
5868     store : false,
5869     loadMask : false,
5870     footerShow : true,
5871     headerShow : true,
5872   
5873     rowSelection : false,
5874     cellSelection : false,
5875     layout : false,
5876     
5877     // Roo.Element - the tbody
5878     mainBody: false,
5879     // Roo.Element - thead element
5880     mainHead: false,
5881     
5882     container: false, // used by gridpanel...
5883     
5884     getAutoCreate : function()
5885     {
5886         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5887         
5888         cfg = {
5889             tag: 'table',
5890             cls : 'table',
5891             cn : []
5892         };
5893         if (this.scrollBody) {
5894             cfg.cls += ' table-body-fixed';
5895         }    
5896         if (this.striped) {
5897             cfg.cls += ' table-striped';
5898         }
5899         
5900         if (this.hover) {
5901             cfg.cls += ' table-hover';
5902         }
5903         if (this.bordered) {
5904             cfg.cls += ' table-bordered';
5905         }
5906         if (this.condensed) {
5907             cfg.cls += ' table-condensed';
5908         }
5909         if (this.responsive) {
5910             cfg.cls += ' table-responsive';
5911         }
5912         
5913         if (this.cls) {
5914             cfg.cls+=  ' ' +this.cls;
5915         }
5916         
5917         // this lot should be simplifed...
5918         
5919         if (this.align) {
5920             cfg.align=this.align;
5921         }
5922         if (this.bgcolor) {
5923             cfg.bgcolor=this.bgcolor;
5924         }
5925         if (this.border) {
5926             cfg.border=this.border;
5927         }
5928         if (this.cellpadding) {
5929             cfg.cellpadding=this.cellpadding;
5930         }
5931         if (this.cellspacing) {
5932             cfg.cellspacing=this.cellspacing;
5933         }
5934         if (this.frame) {
5935             cfg.frame=this.frame;
5936         }
5937         if (this.rules) {
5938             cfg.rules=this.rules;
5939         }
5940         if (this.sortable) {
5941             cfg.sortable=this.sortable;
5942         }
5943         if (this.summary) {
5944             cfg.summary=this.summary;
5945         }
5946         if (this.width) {
5947             cfg.width=this.width;
5948         }
5949         if (this.layout) {
5950             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5951         }
5952         
5953         if(this.store || this.cm){
5954             if(this.headerShow){
5955                 cfg.cn.push(this.renderHeader());
5956             }
5957             
5958             cfg.cn.push(this.renderBody());
5959             
5960             if(this.footerShow){
5961                 cfg.cn.push(this.renderFooter());
5962             }
5963             // where does this come from?
5964             //cfg.cls+=  ' TableGrid';
5965         }
5966         
5967         return { cn : [ cfg ] };
5968     },
5969     
5970     initEvents : function()
5971     {   
5972         if(!this.store || !this.cm){
5973             return;
5974         }
5975         
5976         this.selModel.initEvents();
5977         
5978         //Roo.log('initEvents with ds!!!!');
5979         
5980         this.mainBody = this.el.select('tbody', true).first();
5981         this.mainHead = this.el.select('thead', true).first();
5982         
5983         
5984         
5985         
5986         var _this = this;
5987         
5988         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5989             e.on('click', _this.sort, _this);
5990         });
5991         
5992         this.el.on("click", this.onClick, this);
5993         this.el.on("dblclick", this.onDblClick, this);
5994         
5995         // why is this done????? = it breaks dialogs??
5996         //this.parent().el.setStyle('position', 'relative');
5997         
5998         
5999         if (this.footer) {
6000             this.footer.parentId = this.id;
6001             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6002         }
6003         
6004         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6005         
6006         this.store.on('load', this.onLoad, this);
6007         this.store.on('beforeload', this.onBeforeLoad, this);
6008         this.store.on('update', this.onUpdate, this);
6009         this.store.on('add', this.onAdd, this);
6010         this.store.on("clear", this.clear, this);
6011         
6012         this.el.on("contextmenu", this.onContextMenu, this);
6013         
6014         this.mainBody.on('scroll', this.onBodyScroll, this);
6015         
6016         
6017     },
6018     
6019     onContextMenu : function(e, t)
6020     {
6021         this.processEvent("contextmenu", e);
6022     },
6023     
6024     processEvent : function(name, e)
6025     {
6026         if (name != 'touchstart' ) {
6027             this.fireEvent(name, e);    
6028         }
6029         
6030         var t = e.getTarget();
6031         
6032         var cell = Roo.get(t);
6033         
6034         if(!cell){
6035             return;
6036         }
6037         
6038         if(cell.findParent('tfoot', false, true)){
6039             return;
6040         }
6041         
6042         if(cell.findParent('thead', false, true)){
6043             
6044             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6045                 cell = Roo.get(t).findParent('th', false, true);
6046                 if (!cell) {
6047                     Roo.log("failed to find th in thead?");
6048                     Roo.log(e.getTarget());
6049                     return;
6050                 }
6051             }
6052             
6053             var cellIndex = cell.dom.cellIndex;
6054             
6055             var ename = name == 'touchstart' ? 'click' : name;
6056             this.fireEvent("header" + ename, this, cellIndex, e);
6057             
6058             return;
6059         }
6060         
6061         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6062             cell = Roo.get(t).findParent('td', false, true);
6063             if (!cell) {
6064                 Roo.log("failed to find th in tbody?");
6065                 Roo.log(e.getTarget());
6066                 return;
6067             }
6068         }
6069         
6070         var row = cell.findParent('tr', false, true);
6071         var cellIndex = cell.dom.cellIndex;
6072         var rowIndex = row.dom.rowIndex - 1;
6073         
6074         if(row !== false){
6075             
6076             this.fireEvent("row" + name, this, rowIndex, e);
6077             
6078             if(cell !== false){
6079             
6080                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6081             }
6082         }
6083         
6084     },
6085     
6086     onMouseover : function(e, el)
6087     {
6088         var cell = Roo.get(el);
6089         
6090         if(!cell){
6091             return;
6092         }
6093         
6094         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6095             cell = cell.findParent('td', false, true);
6096         }
6097         
6098         var row = cell.findParent('tr', false, true);
6099         var cellIndex = cell.dom.cellIndex;
6100         var rowIndex = row.dom.rowIndex - 1; // start from 0
6101         
6102         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6103         
6104     },
6105     
6106     onMouseout : function(e, el)
6107     {
6108         var cell = Roo.get(el);
6109         
6110         if(!cell){
6111             return;
6112         }
6113         
6114         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6115             cell = cell.findParent('td', false, true);
6116         }
6117         
6118         var row = cell.findParent('tr', false, true);
6119         var cellIndex = cell.dom.cellIndex;
6120         var rowIndex = row.dom.rowIndex - 1; // start from 0
6121         
6122         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6123         
6124     },
6125     
6126     onClick : function(e, el)
6127     {
6128         var cell = Roo.get(el);
6129         
6130         if(!cell || (!this.cellSelection && !this.rowSelection)){
6131             return;
6132         }
6133         
6134         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6135             cell = cell.findParent('td', false, true);
6136         }
6137         
6138         if(!cell || typeof(cell) == 'undefined'){
6139             return;
6140         }
6141         
6142         var row = cell.findParent('tr', false, true);
6143         
6144         if(!row || typeof(row) == 'undefined'){
6145             return;
6146         }
6147         
6148         var cellIndex = cell.dom.cellIndex;
6149         var rowIndex = this.getRowIndex(row);
6150         
6151         // why??? - should these not be based on SelectionModel?
6152         if(this.cellSelection){
6153             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6154         }
6155         
6156         if(this.rowSelection){
6157             this.fireEvent('rowclick', this, row, rowIndex, e);
6158         }
6159         
6160         
6161     },
6162     
6163     onDblClick : function(e,el)
6164     {
6165         var cell = Roo.get(el);
6166         
6167         if(!cell || (!this.CellSelection && !this.RowSelection)){
6168             return;
6169         }
6170         
6171         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6172             cell = cell.findParent('td', false, true);
6173         }
6174         
6175         if(!cell || typeof(cell) == 'undefined'){
6176             return;
6177         }
6178         
6179         var row = cell.findParent('tr', false, true);
6180         
6181         if(!row || typeof(row) == 'undefined'){
6182             return;
6183         }
6184         
6185         var cellIndex = cell.dom.cellIndex;
6186         var rowIndex = this.getRowIndex(row);
6187         
6188         if(this.CellSelection){
6189             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6190         }
6191         
6192         if(this.RowSelection){
6193             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6194         }
6195     },
6196     
6197     sort : function(e,el)
6198     {
6199         var col = Roo.get(el);
6200         
6201         if(!col.hasClass('sortable')){
6202             return;
6203         }
6204         
6205         var sort = col.attr('sort');
6206         var dir = 'ASC';
6207         
6208         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6209             dir = 'DESC';
6210         }
6211         
6212         this.store.sortInfo = {field : sort, direction : dir};
6213         
6214         if (this.footer) {
6215             Roo.log("calling footer first");
6216             this.footer.onClick('first');
6217         } else {
6218         
6219             this.store.load({ params : { start : 0 } });
6220         }
6221     },
6222     
6223     renderHeader : function()
6224     {
6225         var header = {
6226             tag: 'thead',
6227             cn : []
6228         };
6229         
6230         var cm = this.cm;
6231         this.totalWidth = 0;
6232         
6233         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6234             
6235             var config = cm.config[i];
6236             
6237             var c = {
6238                 tag: 'th',
6239                 style : '',
6240                 html: cm.getColumnHeader(i)
6241             };
6242             
6243             var hh = '';
6244             
6245             if(typeof(config.sortable) != 'undefined' && config.sortable){
6246                 c.cls = 'sortable';
6247                 c.html = '<i class="glyphicon"></i>' + c.html;
6248             }
6249             
6250             if(typeof(config.lgHeader) != 'undefined'){
6251                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6252             }
6253             
6254             if(typeof(config.mdHeader) != 'undefined'){
6255                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6256             }
6257             
6258             if(typeof(config.smHeader) != 'undefined'){
6259                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6260             }
6261             
6262             if(typeof(config.xsHeader) != 'undefined'){
6263                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6264             }
6265             
6266             if(hh.length){
6267                 c.html = hh;
6268             }
6269             
6270             if(typeof(config.tooltip) != 'undefined'){
6271                 c.tooltip = config.tooltip;
6272             }
6273             
6274             if(typeof(config.colspan) != 'undefined'){
6275                 c.colspan = config.colspan;
6276             }
6277             
6278             if(typeof(config.hidden) != 'undefined' && config.hidden){
6279                 c.style += ' display:none;';
6280             }
6281             
6282             if(typeof(config.dataIndex) != 'undefined'){
6283                 c.sort = config.dataIndex;
6284             }
6285             
6286            
6287             
6288             if(typeof(config.align) != 'undefined' && config.align.length){
6289                 c.style += ' text-align:' + config.align + ';';
6290             }
6291             
6292             if(typeof(config.width) != 'undefined'){
6293                 c.style += ' width:' + config.width + 'px;';
6294                 this.totalWidth += config.width;
6295             } else {
6296                 this.totalWidth += 100; // assume minimum of 100 per column?
6297             }
6298             
6299             if(typeof(config.cls) != 'undefined'){
6300                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6301             }
6302             
6303             ['xs','sm','md','lg'].map(function(size){
6304                 
6305                 if(typeof(config[size]) == 'undefined'){
6306                     return;
6307                 }
6308                 
6309                 if (!config[size]) { // 0 = hidden
6310                     c.cls += ' hidden-' + size;
6311                     return;
6312                 }
6313                 
6314                 c.cls += ' col-' + size + '-' + config[size];
6315
6316             });
6317             
6318             header.cn.push(c)
6319         }
6320         
6321         return header;
6322     },
6323     
6324     renderBody : function()
6325     {
6326         var body = {
6327             tag: 'tbody',
6328             cn : [
6329                 {
6330                     tag: 'tr',
6331                     cn : [
6332                         {
6333                             tag : 'td',
6334                             colspan :  this.cm.getColumnCount()
6335                         }
6336                     ]
6337                 }
6338             ]
6339         };
6340         
6341         return body;
6342     },
6343     
6344     renderFooter : function()
6345     {
6346         var footer = {
6347             tag: 'tfoot',
6348             cn : [
6349                 {
6350                     tag: 'tr',
6351                     cn : [
6352                         {
6353                             tag : 'td',
6354                             colspan :  this.cm.getColumnCount()
6355                         }
6356                     ]
6357                 }
6358             ]
6359         };
6360         
6361         return footer;
6362     },
6363     
6364     
6365     
6366     onLoad : function()
6367     {
6368 //        Roo.log('ds onload');
6369         this.clear();
6370         
6371         var _this = this;
6372         var cm = this.cm;
6373         var ds = this.store;
6374         
6375         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6376             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6377             if (_this.store.sortInfo) {
6378                     
6379                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6380                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6381                 }
6382                 
6383                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6384                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6385                 }
6386             }
6387         });
6388         
6389         var tbody =  this.mainBody;
6390               
6391         if(ds.getCount() > 0){
6392             ds.data.each(function(d,rowIndex){
6393                 var row =  this.renderRow(cm, ds, rowIndex);
6394                 
6395                 tbody.createChild(row);
6396                 
6397                 var _this = this;
6398                 
6399                 if(row.cellObjects.length){
6400                     Roo.each(row.cellObjects, function(r){
6401                         _this.renderCellObject(r);
6402                     })
6403                 }
6404                 
6405             }, this);
6406         }
6407         
6408         Roo.each(this.el.select('tbody td', true).elements, function(e){
6409             e.on('mouseover', _this.onMouseover, _this);
6410         });
6411         
6412         Roo.each(this.el.select('tbody td', true).elements, function(e){
6413             e.on('mouseout', _this.onMouseout, _this);
6414         });
6415         this.fireEvent('rowsrendered', this);
6416         //if(this.loadMask){
6417         //    this.maskEl.hide();
6418         //}
6419         
6420         this.autoSize();
6421     },
6422     
6423     
6424     onUpdate : function(ds,record)
6425     {
6426         this.refreshRow(record);
6427     },
6428     
6429     onRemove : function(ds, record, index, isUpdate){
6430         if(isUpdate !== true){
6431             this.fireEvent("beforerowremoved", this, index, record);
6432         }
6433         var bt = this.mainBody.dom;
6434         
6435         var rows = this.el.select('tbody > tr', true).elements;
6436         
6437         if(typeof(rows[index]) != 'undefined'){
6438             bt.removeChild(rows[index].dom);
6439         }
6440         
6441 //        if(bt.rows[index]){
6442 //            bt.removeChild(bt.rows[index]);
6443 //        }
6444         
6445         if(isUpdate !== true){
6446             //this.stripeRows(index);
6447             //this.syncRowHeights(index, index);
6448             //this.layout();
6449             this.fireEvent("rowremoved", this, index, record);
6450         }
6451     },
6452     
6453     onAdd : function(ds, records, rowIndex)
6454     {
6455         //Roo.log('on Add called');
6456         // - note this does not handle multiple adding very well..
6457         var bt = this.mainBody.dom;
6458         for (var i =0 ; i < records.length;i++) {
6459             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6460             //Roo.log(records[i]);
6461             //Roo.log(this.store.getAt(rowIndex+i));
6462             this.insertRow(this.store, rowIndex + i, false);
6463             return;
6464         }
6465         
6466     },
6467     
6468     
6469     refreshRow : function(record){
6470         var ds = this.store, index;
6471         if(typeof record == 'number'){
6472             index = record;
6473             record = ds.getAt(index);
6474         }else{
6475             index = ds.indexOf(record);
6476         }
6477         this.insertRow(ds, index, true);
6478         this.onRemove(ds, record, index+1, true);
6479         //this.syncRowHeights(index, index);
6480         //this.layout();
6481         this.fireEvent("rowupdated", this, index, record);
6482     },
6483     
6484     insertRow : function(dm, rowIndex, isUpdate){
6485         
6486         if(!isUpdate){
6487             this.fireEvent("beforerowsinserted", this, rowIndex);
6488         }
6489             //var s = this.getScrollState();
6490         var row = this.renderRow(this.cm, this.store, rowIndex);
6491         // insert before rowIndex..
6492         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6493         
6494         var _this = this;
6495                 
6496         if(row.cellObjects.length){
6497             Roo.each(row.cellObjects, function(r){
6498                 _this.renderCellObject(r);
6499             })
6500         }
6501             
6502         if(!isUpdate){
6503             this.fireEvent("rowsinserted", this, rowIndex);
6504             //this.syncRowHeights(firstRow, lastRow);
6505             //this.stripeRows(firstRow);
6506             //this.layout();
6507         }
6508         
6509     },
6510     
6511     
6512     getRowDom : function(rowIndex)
6513     {
6514         var rows = this.el.select('tbody > tr', true).elements;
6515         
6516         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6517         
6518     },
6519     // returns the object tree for a tr..
6520   
6521     
6522     renderRow : function(cm, ds, rowIndex) 
6523     {
6524         
6525         var d = ds.getAt(rowIndex);
6526         
6527         var row = {
6528             tag : 'tr',
6529             cn : []
6530         };
6531             
6532         var cellObjects = [];
6533         
6534         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6535             var config = cm.config[i];
6536             
6537             var renderer = cm.getRenderer(i);
6538             var value = '';
6539             var id = false;
6540             
6541             if(typeof(renderer) !== 'undefined'){
6542                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6543             }
6544             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6545             // and are rendered into the cells after the row is rendered - using the id for the element.
6546             
6547             if(typeof(value) === 'object'){
6548                 id = Roo.id();
6549                 cellObjects.push({
6550                     container : id,
6551                     cfg : value 
6552                 })
6553             }
6554             
6555             var rowcfg = {
6556                 record: d,
6557                 rowIndex : rowIndex,
6558                 colIndex : i,
6559                 rowClass : ''
6560             };
6561
6562             this.fireEvent('rowclass', this, rowcfg);
6563             
6564             var td = {
6565                 tag: 'td',
6566                 cls : rowcfg.rowClass,
6567                 style: '',
6568                 html: (typeof(value) === 'object') ? '' : value
6569             };
6570             
6571             if (id) {
6572                 td.id = id;
6573             }
6574             
6575             if(typeof(config.colspan) != 'undefined'){
6576                 td.colspan = config.colspan;
6577             }
6578             
6579             if(typeof(config.hidden) != 'undefined' && config.hidden){
6580                 td.style += ' display:none;';
6581             }
6582             
6583             if(typeof(config.align) != 'undefined' && config.align.length){
6584                 td.style += ' text-align:' + config.align + ';';
6585             }
6586             
6587             if(typeof(config.width) != 'undefined'){
6588                 td.style += ' width:' +  config.width + 'px;';
6589             }
6590             
6591             if(typeof(config.cursor) != 'undefined'){
6592                 td.style += ' cursor:' +  config.cursor + ';';
6593             }
6594             
6595             if(typeof(config.cls) != 'undefined'){
6596                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6597             }
6598             
6599             ['xs','sm','md','lg'].map(function(size){
6600                 
6601                 if(typeof(config[size]) == 'undefined'){
6602                     return;
6603                 }
6604                 
6605                 if (!config[size]) { // 0 = hidden
6606                     td.cls += ' hidden-' + size;
6607                     return;
6608                 }
6609                 
6610                 td.cls += ' col-' + size + '-' + config[size];
6611
6612             });
6613              
6614             row.cn.push(td);
6615            
6616         }
6617         
6618         row.cellObjects = cellObjects;
6619         
6620         return row;
6621           
6622     },
6623     
6624     
6625     
6626     onBeforeLoad : function()
6627     {
6628         //Roo.log('ds onBeforeLoad');
6629         
6630         //this.clear();
6631         
6632         //if(this.loadMask){
6633         //    this.maskEl.show();
6634         //}
6635     },
6636      /**
6637      * Remove all rows
6638      */
6639     clear : function()
6640     {
6641         this.el.select('tbody', true).first().dom.innerHTML = '';
6642     },
6643     /**
6644      * Show or hide a row.
6645      * @param {Number} rowIndex to show or hide
6646      * @param {Boolean} state hide
6647      */
6648     setRowVisibility : function(rowIndex, state)
6649     {
6650         var bt = this.mainBody.dom;
6651         
6652         var rows = this.el.select('tbody > tr', true).elements;
6653         
6654         if(typeof(rows[rowIndex]) == 'undefined'){
6655             return;
6656         }
6657         rows[rowIndex].dom.style.display = state ? '' : 'none';
6658     },
6659     
6660     
6661     getSelectionModel : function(){
6662         if(!this.selModel){
6663             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6664         }
6665         return this.selModel;
6666     },
6667     /*
6668      * Render the Roo.bootstrap object from renderder
6669      */
6670     renderCellObject : function(r)
6671     {
6672         var _this = this;
6673         
6674         var t = r.cfg.render(r.container);
6675         
6676         if(r.cfg.cn){
6677             Roo.each(r.cfg.cn, function(c){
6678                 var child = {
6679                     container: t.getChildContainer(),
6680                     cfg: c
6681                 };
6682                 _this.renderCellObject(child);
6683             })
6684         }
6685     },
6686     
6687     getRowIndex : function(row)
6688     {
6689         var rowIndex = -1;
6690         
6691         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6692             if(el != row){
6693                 return;
6694             }
6695             
6696             rowIndex = index;
6697         });
6698         
6699         return rowIndex;
6700     },
6701      /**
6702      * Returns the grid's underlying element = used by panel.Grid
6703      * @return {Element} The element
6704      */
6705     getGridEl : function(){
6706         return this.el;
6707     },
6708      /**
6709      * Forces a resize - used by panel.Grid
6710      * @return {Element} The element
6711      */
6712     autoSize : function()
6713     {
6714         //var ctr = Roo.get(this.container.dom.parentElement);
6715         var ctr = Roo.get(this.el.dom);
6716         
6717         var thd = this.getGridEl().select('thead',true).first();
6718         var tbd = this.getGridEl().select('tbody', true).first();
6719         
6720         
6721         var cw = ctr.getWidth();
6722         
6723         if (tbd) {
6724             
6725             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6726             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6727             cw -= barsize;
6728         }
6729         cw = Math.max(cw, this.totalWidth);
6730         this.getGridEl().select('tr',true).setWidth(cw);
6731         // resize 'expandable coloumn?
6732         
6733         return; // we doe not have a view in this design..
6734         
6735     },
6736     onBodyScroll: function()
6737     {
6738         
6739         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6740         this.mainHead.setStyle({
6741                     'position' : 'relative',
6742                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6743         });
6744         
6745         
6746     }
6747 });
6748
6749  
6750
6751  /*
6752  * - LGPL
6753  *
6754  * table cell
6755  * 
6756  */
6757
6758 /**
6759  * @class Roo.bootstrap.TableCell
6760  * @extends Roo.bootstrap.Component
6761  * Bootstrap TableCell class
6762  * @cfg {String} html cell contain text
6763  * @cfg {String} cls cell class
6764  * @cfg {String} tag cell tag (td|th) default td
6765  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6766  * @cfg {String} align Aligns the content in a cell
6767  * @cfg {String} axis Categorizes cells
6768  * @cfg {String} bgcolor Specifies the background color of a cell
6769  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6770  * @cfg {Number} colspan Specifies the number of columns a cell should span
6771  * @cfg {String} headers Specifies one or more header cells a cell is related to
6772  * @cfg {Number} height Sets the height of a cell
6773  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6774  * @cfg {Number} rowspan Sets the number of rows a cell should span
6775  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6776  * @cfg {String} valign Vertical aligns the content in a cell
6777  * @cfg {Number} width Specifies the width of a cell
6778  * 
6779  * @constructor
6780  * Create a new TableCell
6781  * @param {Object} config The config object
6782  */
6783
6784 Roo.bootstrap.TableCell = function(config){
6785     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6786 };
6787
6788 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6789     
6790     html: false,
6791     cls: false,
6792     tag: false,
6793     abbr: false,
6794     align: false,
6795     axis: false,
6796     bgcolor: false,
6797     charoff: false,
6798     colspan: false,
6799     headers: false,
6800     height: false,
6801     nowrap: false,
6802     rowspan: false,
6803     scope: false,
6804     valign: false,
6805     width: false,
6806     
6807     
6808     getAutoCreate : function(){
6809         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6810         
6811         cfg = {
6812             tag: 'td'
6813         };
6814         
6815         if(this.tag){
6816             cfg.tag = this.tag;
6817         }
6818         
6819         if (this.html) {
6820             cfg.html=this.html
6821         }
6822         if (this.cls) {
6823             cfg.cls=this.cls
6824         }
6825         if (this.abbr) {
6826             cfg.abbr=this.abbr
6827         }
6828         if (this.align) {
6829             cfg.align=this.align
6830         }
6831         if (this.axis) {
6832             cfg.axis=this.axis
6833         }
6834         if (this.bgcolor) {
6835             cfg.bgcolor=this.bgcolor
6836         }
6837         if (this.charoff) {
6838             cfg.charoff=this.charoff
6839         }
6840         if (this.colspan) {
6841             cfg.colspan=this.colspan
6842         }
6843         if (this.headers) {
6844             cfg.headers=this.headers
6845         }
6846         if (this.height) {
6847             cfg.height=this.height
6848         }
6849         if (this.nowrap) {
6850             cfg.nowrap=this.nowrap
6851         }
6852         if (this.rowspan) {
6853             cfg.rowspan=this.rowspan
6854         }
6855         if (this.scope) {
6856             cfg.scope=this.scope
6857         }
6858         if (this.valign) {
6859             cfg.valign=this.valign
6860         }
6861         if (this.width) {
6862             cfg.width=this.width
6863         }
6864         
6865         
6866         return cfg;
6867     }
6868    
6869 });
6870
6871  
6872
6873  /*
6874  * - LGPL
6875  *
6876  * table row
6877  * 
6878  */
6879
6880 /**
6881  * @class Roo.bootstrap.TableRow
6882  * @extends Roo.bootstrap.Component
6883  * Bootstrap TableRow class
6884  * @cfg {String} cls row class
6885  * @cfg {String} align Aligns the content in a table row
6886  * @cfg {String} bgcolor Specifies a background color for a table row
6887  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6888  * @cfg {String} valign Vertical aligns the content in a table row
6889  * 
6890  * @constructor
6891  * Create a new TableRow
6892  * @param {Object} config The config object
6893  */
6894
6895 Roo.bootstrap.TableRow = function(config){
6896     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6897 };
6898
6899 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6900     
6901     cls: false,
6902     align: false,
6903     bgcolor: false,
6904     charoff: false,
6905     valign: false,
6906     
6907     getAutoCreate : function(){
6908         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6909         
6910         cfg = {
6911             tag: 'tr'
6912         };
6913             
6914         if(this.cls){
6915             cfg.cls = this.cls;
6916         }
6917         if(this.align){
6918             cfg.align = this.align;
6919         }
6920         if(this.bgcolor){
6921             cfg.bgcolor = this.bgcolor;
6922         }
6923         if(this.charoff){
6924             cfg.charoff = this.charoff;
6925         }
6926         if(this.valign){
6927             cfg.valign = this.valign;
6928         }
6929         
6930         return cfg;
6931     }
6932    
6933 });
6934
6935  
6936
6937  /*
6938  * - LGPL
6939  *
6940  * table body
6941  * 
6942  */
6943
6944 /**
6945  * @class Roo.bootstrap.TableBody
6946  * @extends Roo.bootstrap.Component
6947  * Bootstrap TableBody class
6948  * @cfg {String} cls element class
6949  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6950  * @cfg {String} align Aligns the content inside the element
6951  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6952  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6953  * 
6954  * @constructor
6955  * Create a new TableBody
6956  * @param {Object} config The config object
6957  */
6958
6959 Roo.bootstrap.TableBody = function(config){
6960     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6961 };
6962
6963 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6964     
6965     cls: false,
6966     tag: false,
6967     align: false,
6968     charoff: false,
6969     valign: false,
6970     
6971     getAutoCreate : function(){
6972         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6973         
6974         cfg = {
6975             tag: 'tbody'
6976         };
6977             
6978         if (this.cls) {
6979             cfg.cls=this.cls
6980         }
6981         if(this.tag){
6982             cfg.tag = this.tag;
6983         }
6984         
6985         if(this.align){
6986             cfg.align = this.align;
6987         }
6988         if(this.charoff){
6989             cfg.charoff = this.charoff;
6990         }
6991         if(this.valign){
6992             cfg.valign = this.valign;
6993         }
6994         
6995         return cfg;
6996     }
6997     
6998     
6999 //    initEvents : function()
7000 //    {
7001 //        
7002 //        if(!this.store){
7003 //            return;
7004 //        }
7005 //        
7006 //        this.store = Roo.factory(this.store, Roo.data);
7007 //        this.store.on('load', this.onLoad, this);
7008 //        
7009 //        this.store.load();
7010 //        
7011 //    },
7012 //    
7013 //    onLoad: function () 
7014 //    {   
7015 //        this.fireEvent('load', this);
7016 //    }
7017 //    
7018 //   
7019 });
7020
7021  
7022
7023  /*
7024  * Based on:
7025  * Ext JS Library 1.1.1
7026  * Copyright(c) 2006-2007, Ext JS, LLC.
7027  *
7028  * Originally Released Under LGPL - original licence link has changed is not relivant.
7029  *
7030  * Fork - LGPL
7031  * <script type="text/javascript">
7032  */
7033
7034 // as we use this in bootstrap.
7035 Roo.namespace('Roo.form');
7036  /**
7037  * @class Roo.form.Action
7038  * Internal Class used to handle form actions
7039  * @constructor
7040  * @param {Roo.form.BasicForm} el The form element or its id
7041  * @param {Object} config Configuration options
7042  */
7043
7044  
7045  
7046 // define the action interface
7047 Roo.form.Action = function(form, options){
7048     this.form = form;
7049     this.options = options || {};
7050 };
7051 /**
7052  * Client Validation Failed
7053  * @const 
7054  */
7055 Roo.form.Action.CLIENT_INVALID = 'client';
7056 /**
7057  * Server Validation Failed
7058  * @const 
7059  */
7060 Roo.form.Action.SERVER_INVALID = 'server';
7061  /**
7062  * Connect to Server Failed
7063  * @const 
7064  */
7065 Roo.form.Action.CONNECT_FAILURE = 'connect';
7066 /**
7067  * Reading Data from Server Failed
7068  * @const 
7069  */
7070 Roo.form.Action.LOAD_FAILURE = 'load';
7071
7072 Roo.form.Action.prototype = {
7073     type : 'default',
7074     failureType : undefined,
7075     response : undefined,
7076     result : undefined,
7077
7078     // interface method
7079     run : function(options){
7080
7081     },
7082
7083     // interface method
7084     success : function(response){
7085
7086     },
7087
7088     // interface method
7089     handleResponse : function(response){
7090
7091     },
7092
7093     // default connection failure
7094     failure : function(response){
7095         
7096         this.response = response;
7097         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7098         this.form.afterAction(this, false);
7099     },
7100
7101     processResponse : function(response){
7102         this.response = response;
7103         if(!response.responseText){
7104             return true;
7105         }
7106         this.result = this.handleResponse(response);
7107         return this.result;
7108     },
7109
7110     // utility functions used internally
7111     getUrl : function(appendParams){
7112         var url = this.options.url || this.form.url || this.form.el.dom.action;
7113         if(appendParams){
7114             var p = this.getParams();
7115             if(p){
7116                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7117             }
7118         }
7119         return url;
7120     },
7121
7122     getMethod : function(){
7123         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7124     },
7125
7126     getParams : function(){
7127         var bp = this.form.baseParams;
7128         var p = this.options.params;
7129         if(p){
7130             if(typeof p == "object"){
7131                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7132             }else if(typeof p == 'string' && bp){
7133                 p += '&' + Roo.urlEncode(bp);
7134             }
7135         }else if(bp){
7136             p = Roo.urlEncode(bp);
7137         }
7138         return p;
7139     },
7140
7141     createCallback : function(){
7142         return {
7143             success: this.success,
7144             failure: this.failure,
7145             scope: this,
7146             timeout: (this.form.timeout*1000),
7147             upload: this.form.fileUpload ? this.success : undefined
7148         };
7149     }
7150 };
7151
7152 Roo.form.Action.Submit = function(form, options){
7153     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7154 };
7155
7156 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7157     type : 'submit',
7158
7159     haveProgress : false,
7160     uploadComplete : false,
7161     
7162     // uploadProgress indicator.
7163     uploadProgress : function()
7164     {
7165         if (!this.form.progressUrl) {
7166             return;
7167         }
7168         
7169         if (!this.haveProgress) {
7170             Roo.MessageBox.progress("Uploading", "Uploading");
7171         }
7172         if (this.uploadComplete) {
7173            Roo.MessageBox.hide();
7174            return;
7175         }
7176         
7177         this.haveProgress = true;
7178    
7179         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7180         
7181         var c = new Roo.data.Connection();
7182         c.request({
7183             url : this.form.progressUrl,
7184             params: {
7185                 id : uid
7186             },
7187             method: 'GET',
7188             success : function(req){
7189                //console.log(data);
7190                 var rdata = false;
7191                 var edata;
7192                 try  {
7193                    rdata = Roo.decode(req.responseText)
7194                 } catch (e) {
7195                     Roo.log("Invalid data from server..");
7196                     Roo.log(edata);
7197                     return;
7198                 }
7199                 if (!rdata || !rdata.success) {
7200                     Roo.log(rdata);
7201                     Roo.MessageBox.alert(Roo.encode(rdata));
7202                     return;
7203                 }
7204                 var data = rdata.data;
7205                 
7206                 if (this.uploadComplete) {
7207                    Roo.MessageBox.hide();
7208                    return;
7209                 }
7210                    
7211                 if (data){
7212                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7213                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7214                     );
7215                 }
7216                 this.uploadProgress.defer(2000,this);
7217             },
7218        
7219             failure: function(data) {
7220                 Roo.log('progress url failed ');
7221                 Roo.log(data);
7222             },
7223             scope : this
7224         });
7225            
7226     },
7227     
7228     
7229     run : function()
7230     {
7231         // run get Values on the form, so it syncs any secondary forms.
7232         this.form.getValues();
7233         
7234         var o = this.options;
7235         var method = this.getMethod();
7236         var isPost = method == 'POST';
7237         if(o.clientValidation === false || this.form.isValid()){
7238             
7239             if (this.form.progressUrl) {
7240                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7241                     (new Date() * 1) + '' + Math.random());
7242                     
7243             } 
7244             
7245             
7246             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7247                 form:this.form.el.dom,
7248                 url:this.getUrl(!isPost),
7249                 method: method,
7250                 params:isPost ? this.getParams() : null,
7251                 isUpload: this.form.fileUpload
7252             }));
7253             
7254             this.uploadProgress();
7255
7256         }else if (o.clientValidation !== false){ // client validation failed
7257             this.failureType = Roo.form.Action.CLIENT_INVALID;
7258             this.form.afterAction(this, false);
7259         }
7260     },
7261
7262     success : function(response)
7263     {
7264         this.uploadComplete= true;
7265         if (this.haveProgress) {
7266             Roo.MessageBox.hide();
7267         }
7268         
7269         
7270         var result = this.processResponse(response);
7271         if(result === true || result.success){
7272             this.form.afterAction(this, true);
7273             return;
7274         }
7275         if(result.errors){
7276             this.form.markInvalid(result.errors);
7277             this.failureType = Roo.form.Action.SERVER_INVALID;
7278         }
7279         this.form.afterAction(this, false);
7280     },
7281     failure : function(response)
7282     {
7283         this.uploadComplete= true;
7284         if (this.haveProgress) {
7285             Roo.MessageBox.hide();
7286         }
7287         
7288         this.response = response;
7289         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7290         this.form.afterAction(this, false);
7291     },
7292     
7293     handleResponse : function(response){
7294         if(this.form.errorReader){
7295             var rs = this.form.errorReader.read(response);
7296             var errors = [];
7297             if(rs.records){
7298                 for(var i = 0, len = rs.records.length; i < len; i++) {
7299                     var r = rs.records[i];
7300                     errors[i] = r.data;
7301                 }
7302             }
7303             if(errors.length < 1){
7304                 errors = null;
7305             }
7306             return {
7307                 success : rs.success,
7308                 errors : errors
7309             };
7310         }
7311         var ret = false;
7312         try {
7313             ret = Roo.decode(response.responseText);
7314         } catch (e) {
7315             ret = {
7316                 success: false,
7317                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7318                 errors : []
7319             };
7320         }
7321         return ret;
7322         
7323     }
7324 });
7325
7326
7327 Roo.form.Action.Load = function(form, options){
7328     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7329     this.reader = this.form.reader;
7330 };
7331
7332 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7333     type : 'load',
7334
7335     run : function(){
7336         
7337         Roo.Ajax.request(Roo.apply(
7338                 this.createCallback(), {
7339                     method:this.getMethod(),
7340                     url:this.getUrl(false),
7341                     params:this.getParams()
7342         }));
7343     },
7344
7345     success : function(response){
7346         
7347         var result = this.processResponse(response);
7348         if(result === true || !result.success || !result.data){
7349             this.failureType = Roo.form.Action.LOAD_FAILURE;
7350             this.form.afterAction(this, false);
7351             return;
7352         }
7353         this.form.clearInvalid();
7354         this.form.setValues(result.data);
7355         this.form.afterAction(this, true);
7356     },
7357
7358     handleResponse : function(response){
7359         if(this.form.reader){
7360             var rs = this.form.reader.read(response);
7361             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7362             return {
7363                 success : rs.success,
7364                 data : data
7365             };
7366         }
7367         return Roo.decode(response.responseText);
7368     }
7369 });
7370
7371 Roo.form.Action.ACTION_TYPES = {
7372     'load' : Roo.form.Action.Load,
7373     'submit' : Roo.form.Action.Submit
7374 };/*
7375  * - LGPL
7376  *
7377  * form
7378  * 
7379  */
7380
7381 /**
7382  * @class Roo.bootstrap.Form
7383  * @extends Roo.bootstrap.Component
7384  * Bootstrap Form class
7385  * @cfg {String} method  GET | POST (default POST)
7386  * @cfg {String} labelAlign top | left (default top)
7387  * @cfg {String} align left  | right - for navbars
7388  * @cfg {Boolean} loadMask load mask when submit (default true)
7389
7390  * 
7391  * @constructor
7392  * Create a new Form
7393  * @param {Object} config The config object
7394  */
7395
7396
7397 Roo.bootstrap.Form = function(config){
7398     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7399     this.addEvents({
7400         /**
7401          * @event clientvalidation
7402          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7403          * @param {Form} this
7404          * @param {Boolean} valid true if the form has passed client-side validation
7405          */
7406         clientvalidation: true,
7407         /**
7408          * @event beforeaction
7409          * Fires before any action is performed. Return false to cancel the action.
7410          * @param {Form} this
7411          * @param {Action} action The action to be performed
7412          */
7413         beforeaction: true,
7414         /**
7415          * @event actionfailed
7416          * Fires when an action fails.
7417          * @param {Form} this
7418          * @param {Action} action The action that failed
7419          */
7420         actionfailed : true,
7421         /**
7422          * @event actioncomplete
7423          * Fires when an action is completed.
7424          * @param {Form} this
7425          * @param {Action} action The action that completed
7426          */
7427         actioncomplete : true
7428     });
7429     
7430 };
7431
7432 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7433       
7434      /**
7435      * @cfg {String} method
7436      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7437      */
7438     method : 'POST',
7439     /**
7440      * @cfg {String} url
7441      * The URL to use for form actions if one isn't supplied in the action options.
7442      */
7443     /**
7444      * @cfg {Boolean} fileUpload
7445      * Set to true if this form is a file upload.
7446      */
7447      
7448     /**
7449      * @cfg {Object} baseParams
7450      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7451      */
7452       
7453     /**
7454      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7455      */
7456     timeout: 30,
7457     /**
7458      * @cfg {Sting} align (left|right) for navbar forms
7459      */
7460     align : 'left',
7461
7462     // private
7463     activeAction : null,
7464  
7465     /**
7466      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7467      * element by passing it or its id or mask the form itself by passing in true.
7468      * @type Mixed
7469      */
7470     waitMsgTarget : false,
7471     
7472     loadMask : true,
7473     
7474     getAutoCreate : function(){
7475         
7476         var cfg = {
7477             tag: 'form',
7478             method : this.method || 'POST',
7479             id : this.id || Roo.id(),
7480             cls : ''
7481         };
7482         if (this.parent().xtype.match(/^Nav/)) {
7483             cfg.cls = 'navbar-form navbar-' + this.align;
7484             
7485         }
7486         
7487         if (this.labelAlign == 'left' ) {
7488             cfg.cls += ' form-horizontal';
7489         }
7490         
7491         
7492         return cfg;
7493     },
7494     initEvents : function()
7495     {
7496         this.el.on('submit', this.onSubmit, this);
7497         // this was added as random key presses on the form where triggering form submit.
7498         this.el.on('keypress', function(e) {
7499             if (e.getCharCode() != 13) {
7500                 return true;
7501             }
7502             // we might need to allow it for textareas.. and some other items.
7503             // check e.getTarget().
7504             
7505             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7506                 return true;
7507             }
7508         
7509             Roo.log("keypress blocked");
7510             
7511             e.preventDefault();
7512             return false;
7513         });
7514         
7515     },
7516     // private
7517     onSubmit : function(e){
7518         e.stopEvent();
7519     },
7520     
7521      /**
7522      * Returns true if client-side validation on the form is successful.
7523      * @return Boolean
7524      */
7525     isValid : function(){
7526         var items = this.getItems();
7527         var valid = true;
7528         items.each(function(f){
7529            if(!f.validate()){
7530                valid = false;
7531                
7532            }
7533         });
7534         return valid;
7535     },
7536     /**
7537      * Returns true if any fields in this form have changed since their original load.
7538      * @return Boolean
7539      */
7540     isDirty : function(){
7541         var dirty = false;
7542         var items = this.getItems();
7543         items.each(function(f){
7544            if(f.isDirty()){
7545                dirty = true;
7546                return false;
7547            }
7548            return true;
7549         });
7550         return dirty;
7551     },
7552      /**
7553      * Performs a predefined action (submit or load) or custom actions you define on this form.
7554      * @param {String} actionName The name of the action type
7555      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7556      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7557      * accept other config options):
7558      * <pre>
7559 Property          Type             Description
7560 ----------------  ---------------  ----------------------------------------------------------------------------------
7561 url               String           The url for the action (defaults to the form's url)
7562 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7563 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7564 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7565                                    validate the form on the client (defaults to false)
7566      * </pre>
7567      * @return {BasicForm} this
7568      */
7569     doAction : function(action, options){
7570         if(typeof action == 'string'){
7571             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7572         }
7573         if(this.fireEvent('beforeaction', this, action) !== false){
7574             this.beforeAction(action);
7575             action.run.defer(100, action);
7576         }
7577         return this;
7578     },
7579     
7580     // private
7581     beforeAction : function(action){
7582         var o = action.options;
7583         
7584         if(this.loadMask){
7585             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7586         }
7587         // not really supported yet.. ??
7588         
7589         //if(this.waitMsgTarget === true){
7590         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7591         //}else if(this.waitMsgTarget){
7592         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7593         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7594         //}else {
7595         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7596        // }
7597          
7598     },
7599
7600     // private
7601     afterAction : function(action, success){
7602         this.activeAction = null;
7603         var o = action.options;
7604         
7605         //if(this.waitMsgTarget === true){
7606             this.el.unmask();
7607         //}else if(this.waitMsgTarget){
7608         //    this.waitMsgTarget.unmask();
7609         //}else{
7610         //    Roo.MessageBox.updateProgress(1);
7611         //    Roo.MessageBox.hide();
7612        // }
7613         // 
7614         if(success){
7615             if(o.reset){
7616                 this.reset();
7617             }
7618             Roo.callback(o.success, o.scope, [this, action]);
7619             this.fireEvent('actioncomplete', this, action);
7620             
7621         }else{
7622             
7623             // failure condition..
7624             // we have a scenario where updates need confirming.
7625             // eg. if a locking scenario exists..
7626             // we look for { errors : { needs_confirm : true }} in the response.
7627             if (
7628                 (typeof(action.result) != 'undefined')  &&
7629                 (typeof(action.result.errors) != 'undefined')  &&
7630                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7631            ){
7632                 var _t = this;
7633                 Roo.log("not supported yet");
7634                  /*
7635                 
7636                 Roo.MessageBox.confirm(
7637                     "Change requires confirmation",
7638                     action.result.errorMsg,
7639                     function(r) {
7640                         if (r != 'yes') {
7641                             return;
7642                         }
7643                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7644                     }
7645                     
7646                 );
7647                 */
7648                 
7649                 
7650                 return;
7651             }
7652             
7653             Roo.callback(o.failure, o.scope, [this, action]);
7654             // show an error message if no failed handler is set..
7655             if (!this.hasListener('actionfailed')) {
7656                 Roo.log("need to add dialog support");
7657                 /*
7658                 Roo.MessageBox.alert("Error",
7659                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7660                         action.result.errorMsg :
7661                         "Saving Failed, please check your entries or try again"
7662                 );
7663                 */
7664             }
7665             
7666             this.fireEvent('actionfailed', this, action);
7667         }
7668         
7669     },
7670     /**
7671      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7672      * @param {String} id The value to search for
7673      * @return Field
7674      */
7675     findField : function(id){
7676         var items = this.getItems();
7677         var field = items.get(id);
7678         if(!field){
7679              items.each(function(f){
7680                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7681                     field = f;
7682                     return false;
7683                 }
7684                 return true;
7685             });
7686         }
7687         return field || null;
7688     },
7689      /**
7690      * Mark fields in this form invalid in bulk.
7691      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7692      * @return {BasicForm} this
7693      */
7694     markInvalid : function(errors){
7695         if(errors instanceof Array){
7696             for(var i = 0, len = errors.length; i < len; i++){
7697                 var fieldError = errors[i];
7698                 var f = this.findField(fieldError.id);
7699                 if(f){
7700                     f.markInvalid(fieldError.msg);
7701                 }
7702             }
7703         }else{
7704             var field, id;
7705             for(id in errors){
7706                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7707                     field.markInvalid(errors[id]);
7708                 }
7709             }
7710         }
7711         //Roo.each(this.childForms || [], function (f) {
7712         //    f.markInvalid(errors);
7713         //});
7714         
7715         return this;
7716     },
7717
7718     /**
7719      * Set values for fields in this form in bulk.
7720      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7721      * @return {BasicForm} this
7722      */
7723     setValues : function(values){
7724         if(values instanceof Array){ // array of objects
7725             for(var i = 0, len = values.length; i < len; i++){
7726                 var v = values[i];
7727                 var f = this.findField(v.id);
7728                 if(f){
7729                     f.setValue(v.value);
7730                     if(this.trackResetOnLoad){
7731                         f.originalValue = f.getValue();
7732                     }
7733                 }
7734             }
7735         }else{ // object hash
7736             var field, id;
7737             for(id in values){
7738                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7739                     
7740                     if (field.setFromData && 
7741                         field.valueField && 
7742                         field.displayField &&
7743                         // combos' with local stores can 
7744                         // be queried via setValue()
7745                         // to set their value..
7746                         (field.store && !field.store.isLocal)
7747                         ) {
7748                         // it's a combo
7749                         var sd = { };
7750                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7751                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7752                         field.setFromData(sd);
7753                         
7754                     } else {
7755                         field.setValue(values[id]);
7756                     }
7757                     
7758                     
7759                     if(this.trackResetOnLoad){
7760                         field.originalValue = field.getValue();
7761                     }
7762                 }
7763             }
7764         }
7765          
7766         //Roo.each(this.childForms || [], function (f) {
7767         //    f.setValues(values);
7768         //});
7769                 
7770         return this;
7771     },
7772
7773     /**
7774      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7775      * they are returned as an array.
7776      * @param {Boolean} asString
7777      * @return {Object}
7778      */
7779     getValues : function(asString){
7780         //if (this.childForms) {
7781             // copy values from the child forms
7782         //    Roo.each(this.childForms, function (f) {
7783         //        this.setValues(f.getValues());
7784         //    }, this);
7785         //}
7786         
7787         
7788         
7789         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7790         if(asString === true){
7791             return fs;
7792         }
7793         return Roo.urlDecode(fs);
7794     },
7795     
7796     /**
7797      * Returns the fields in this form as an object with key/value pairs. 
7798      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7799      * @return {Object}
7800      */
7801     getFieldValues : function(with_hidden)
7802     {
7803         var items = this.getItems();
7804         var ret = {};
7805         items.each(function(f){
7806             if (!f.getName()) {
7807                 return;
7808             }
7809             var v = f.getValue();
7810             if (f.inputType =='radio') {
7811                 if (typeof(ret[f.getName()]) == 'undefined') {
7812                     ret[f.getName()] = ''; // empty..
7813                 }
7814                 
7815                 if (!f.el.dom.checked) {
7816                     return;
7817                     
7818                 }
7819                 v = f.el.dom.value;
7820                 
7821             }
7822             
7823             // not sure if this supported any more..
7824             if ((typeof(v) == 'object') && f.getRawValue) {
7825                 v = f.getRawValue() ; // dates..
7826             }
7827             // combo boxes where name != hiddenName...
7828             if (f.name != f.getName()) {
7829                 ret[f.name] = f.getRawValue();
7830             }
7831             ret[f.getName()] = v;
7832         });
7833         
7834         return ret;
7835     },
7836
7837     /**
7838      * Clears all invalid messages in this form.
7839      * @return {BasicForm} this
7840      */
7841     clearInvalid : function(){
7842         var items = this.getItems();
7843         
7844         items.each(function(f){
7845            f.clearInvalid();
7846         });
7847         
7848         
7849         
7850         return this;
7851     },
7852
7853     /**
7854      * Resets this form.
7855      * @return {BasicForm} this
7856      */
7857     reset : function(){
7858         var items = this.getItems();
7859         items.each(function(f){
7860             f.reset();
7861         });
7862         
7863         Roo.each(this.childForms || [], function (f) {
7864             f.reset();
7865         });
7866        
7867         
7868         return this;
7869     },
7870     getItems : function()
7871     {
7872         var r=new Roo.util.MixedCollection(false, function(o){
7873             return o.id || (o.id = Roo.id());
7874         });
7875         var iter = function(el) {
7876             if (el.inputEl) {
7877                 r.add(el);
7878             }
7879             if (!el.items) {
7880                 return;
7881             }
7882             Roo.each(el.items,function(e) {
7883                 iter(e);
7884             });
7885             
7886             
7887         };
7888         
7889         iter(this);
7890         return r;
7891         
7892         
7893         
7894         
7895     }
7896     
7897 });
7898
7899  
7900 /*
7901  * Based on:
7902  * Ext JS Library 1.1.1
7903  * Copyright(c) 2006-2007, Ext JS, LLC.
7904  *
7905  * Originally Released Under LGPL - original licence link has changed is not relivant.
7906  *
7907  * Fork - LGPL
7908  * <script type="text/javascript">
7909  */
7910 /**
7911  * @class Roo.form.VTypes
7912  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7913  * @singleton
7914  */
7915 Roo.form.VTypes = function(){
7916     // closure these in so they are only created once.
7917     var alpha = /^[a-zA-Z_]+$/;
7918     var alphanum = /^[a-zA-Z0-9_]+$/;
7919     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7920     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7921
7922     // All these messages and functions are configurable
7923     return {
7924         /**
7925          * The function used to validate email addresses
7926          * @param {String} value The email address
7927          */
7928         'email' : function(v){
7929             return email.test(v);
7930         },
7931         /**
7932          * The error text to display when the email validation function returns false
7933          * @type String
7934          */
7935         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7936         /**
7937          * The keystroke filter mask to be applied on email input
7938          * @type RegExp
7939          */
7940         'emailMask' : /[a-z0-9_\.\-@]/i,
7941
7942         /**
7943          * The function used to validate URLs
7944          * @param {String} value The URL
7945          */
7946         'url' : function(v){
7947             return url.test(v);
7948         },
7949         /**
7950          * The error text to display when the url validation function returns false
7951          * @type String
7952          */
7953         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7954         
7955         /**
7956          * The function used to validate alpha values
7957          * @param {String} value The value
7958          */
7959         'alpha' : function(v){
7960             return alpha.test(v);
7961         },
7962         /**
7963          * The error text to display when the alpha validation function returns false
7964          * @type String
7965          */
7966         'alphaText' : 'This field should only contain letters and _',
7967         /**
7968          * The keystroke filter mask to be applied on alpha input
7969          * @type RegExp
7970          */
7971         'alphaMask' : /[a-z_]/i,
7972
7973         /**
7974          * The function used to validate alphanumeric values
7975          * @param {String} value The value
7976          */
7977         'alphanum' : function(v){
7978             return alphanum.test(v);
7979         },
7980         /**
7981          * The error text to display when the alphanumeric validation function returns false
7982          * @type String
7983          */
7984         'alphanumText' : 'This field should only contain letters, numbers and _',
7985         /**
7986          * The keystroke filter mask to be applied on alphanumeric input
7987          * @type RegExp
7988          */
7989         'alphanumMask' : /[a-z0-9_]/i
7990     };
7991 }();/*
7992  * - LGPL
7993  *
7994  * Input
7995  * 
7996  */
7997
7998 /**
7999  * @class Roo.bootstrap.Input
8000  * @extends Roo.bootstrap.Component
8001  * Bootstrap Input class
8002  * @cfg {Boolean} disabled is it disabled
8003  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8004  * @cfg {String} name name of the input
8005  * @cfg {string} fieldLabel - the label associated
8006  * @cfg {string} placeholder - placeholder to put in text.
8007  * @cfg {string}  before - input group add on before
8008  * @cfg {string} after - input group add on after
8009  * @cfg {string} size - (lg|sm) or leave empty..
8010  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8011  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8012  * @cfg {Number} md colspan out of 12 for computer-sized screens
8013  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8014  * @cfg {string} value default value of the input
8015  * @cfg {Number} labelWidth set the width of label (0-12)
8016  * @cfg {String} labelAlign (top|left)
8017  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8018  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8019  * @cfg {String} indicatorpos (left|right) default left
8020
8021  * @cfg {String} align (left|center|right) Default left
8022  * @cfg {Boolean} forceFeedback (true|false) Default false
8023  * 
8024  * 
8025  * 
8026  * 
8027  * @constructor
8028  * Create a new Input
8029  * @param {Object} config The config object
8030  */
8031
8032 Roo.bootstrap.Input = function(config){
8033     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8034    
8035         this.addEvents({
8036             /**
8037              * @event focus
8038              * Fires when this field receives input focus.
8039              * @param {Roo.form.Field} this
8040              */
8041             focus : true,
8042             /**
8043              * @event blur
8044              * Fires when this field loses input focus.
8045              * @param {Roo.form.Field} this
8046              */
8047             blur : true,
8048             /**
8049              * @event specialkey
8050              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8051              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8052              * @param {Roo.form.Field} this
8053              * @param {Roo.EventObject} e The event object
8054              */
8055             specialkey : true,
8056             /**
8057              * @event change
8058              * Fires just before the field blurs if the field value has changed.
8059              * @param {Roo.form.Field} this
8060              * @param {Mixed} newValue The new value
8061              * @param {Mixed} oldValue The original value
8062              */
8063             change : true,
8064             /**
8065              * @event invalid
8066              * Fires after the field has been marked as invalid.
8067              * @param {Roo.form.Field} this
8068              * @param {String} msg The validation message
8069              */
8070             invalid : true,
8071             /**
8072              * @event valid
8073              * Fires after the field has been validated with no errors.
8074              * @param {Roo.form.Field} this
8075              */
8076             valid : true,
8077              /**
8078              * @event keyup
8079              * Fires after the key up
8080              * @param {Roo.form.Field} this
8081              * @param {Roo.EventObject}  e The event Object
8082              */
8083             keyup : true
8084         });
8085 };
8086
8087 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8088      /**
8089      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8090       automatic validation (defaults to "keyup").
8091      */
8092     validationEvent : "keyup",
8093      /**
8094      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8095      */
8096     validateOnBlur : true,
8097     /**
8098      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8099      */
8100     validationDelay : 250,
8101      /**
8102      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8103      */
8104     focusClass : "x-form-focus",  // not needed???
8105     
8106        
8107     /**
8108      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8109      */
8110     invalidClass : "has-warning",
8111     
8112     /**
8113      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8114      */
8115     validClass : "has-success",
8116     
8117     /**
8118      * @cfg {Boolean} hasFeedback (true|false) default true
8119      */
8120     hasFeedback : true,
8121     
8122     /**
8123      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8124      */
8125     invalidFeedbackClass : "glyphicon-warning-sign",
8126     
8127     /**
8128      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8129      */
8130     validFeedbackClass : "glyphicon-ok",
8131     
8132     /**
8133      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8134      */
8135     selectOnFocus : false,
8136     
8137      /**
8138      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8139      */
8140     maskRe : null,
8141        /**
8142      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8143      */
8144     vtype : null,
8145     
8146       /**
8147      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8148      */
8149     disableKeyFilter : false,
8150     
8151        /**
8152      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8153      */
8154     disabled : false,
8155      /**
8156      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8157      */
8158     allowBlank : true,
8159     /**
8160      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8161      */
8162     blankText : "This field is required",
8163     
8164      /**
8165      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8166      */
8167     minLength : 0,
8168     /**
8169      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8170      */
8171     maxLength : Number.MAX_VALUE,
8172     /**
8173      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8174      */
8175     minLengthText : "The minimum length for this field is {0}",
8176     /**
8177      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8178      */
8179     maxLengthText : "The maximum length for this field is {0}",
8180   
8181     
8182     /**
8183      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8184      * If available, this function will be called only after the basic validators all return true, and will be passed the
8185      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8186      */
8187     validator : null,
8188     /**
8189      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8190      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8191      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8192      */
8193     regex : null,
8194     /**
8195      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8196      */
8197     regexText : "",
8198     
8199     autocomplete: false,
8200     
8201     
8202     fieldLabel : '',
8203     inputType : 'text',
8204     
8205     name : false,
8206     placeholder: false,
8207     before : false,
8208     after : false,
8209     size : false,
8210     hasFocus : false,
8211     preventMark: false,
8212     isFormField : true,
8213     value : '',
8214     labelWidth : 2,
8215     labelAlign : false,
8216     readOnly : false,
8217     align : false,
8218     formatedValue : false,
8219     forceFeedback : false,
8220     
8221     indicatorpos : 'left',
8222     
8223     parentLabelAlign : function()
8224     {
8225         var parent = this;
8226         while (parent.parent()) {
8227             parent = parent.parent();
8228             if (typeof(parent.labelAlign) !='undefined') {
8229                 return parent.labelAlign;
8230             }
8231         }
8232         return 'left';
8233         
8234     },
8235     
8236     getAutoCreate : function()
8237     {
8238         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8239         
8240         var id = Roo.id();
8241         
8242         var cfg = {};
8243         
8244         if(this.inputType != 'hidden'){
8245             cfg.cls = 'form-group' //input-group
8246         }
8247         
8248         var input =  {
8249             tag: 'input',
8250             id : id,
8251             type : this.inputType,
8252             value : this.value,
8253             cls : 'form-control',
8254             placeholder : this.placeholder || '',
8255             autocomplete : this.autocomplete || 'new-password'
8256         };
8257         
8258         if(this.align){
8259             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8260         }
8261         
8262         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8263             input.maxLength = this.maxLength;
8264         }
8265         
8266         if (this.disabled) {
8267             input.disabled=true;
8268         }
8269         
8270         if (this.readOnly) {
8271             input.readonly=true;
8272         }
8273         
8274         if (this.name) {
8275             input.name = this.name;
8276         }
8277         
8278         if (this.size) {
8279             input.cls += ' input-' + this.size;
8280         }
8281         
8282         var settings=this;
8283         ['xs','sm','md','lg'].map(function(size){
8284             if (settings[size]) {
8285                 cfg.cls += ' col-' + size + '-' + settings[size];
8286             }
8287         });
8288         
8289         var inputblock = input;
8290         
8291         var feedback = {
8292             tag: 'span',
8293             cls: 'glyphicon form-control-feedback'
8294         };
8295             
8296         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8297             
8298             inputblock = {
8299                 cls : 'has-feedback',
8300                 cn :  [
8301                     input,
8302                     feedback
8303                 ] 
8304             };  
8305         }
8306         
8307         if (this.before || this.after) {
8308             
8309             inputblock = {
8310                 cls : 'input-group',
8311                 cn :  [] 
8312             };
8313             
8314             if (this.before && typeof(this.before) == 'string') {
8315                 
8316                 inputblock.cn.push({
8317                     tag :'span',
8318                     cls : 'roo-input-before input-group-addon',
8319                     html : this.before
8320                 });
8321             }
8322             if (this.before && typeof(this.before) == 'object') {
8323                 this.before = Roo.factory(this.before);
8324                 
8325                 inputblock.cn.push({
8326                     tag :'span',
8327                     cls : 'roo-input-before input-group-' +
8328                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8329                 });
8330             }
8331             
8332             inputblock.cn.push(input);
8333             
8334             if (this.after && typeof(this.after) == 'string') {
8335                 inputblock.cn.push({
8336                     tag :'span',
8337                     cls : 'roo-input-after input-group-addon',
8338                     html : this.after
8339                 });
8340             }
8341             if (this.after && typeof(this.after) == 'object') {
8342                 this.after = Roo.factory(this.after);
8343                 
8344                 inputblock.cn.push({
8345                     tag :'span',
8346                     cls : 'roo-input-after input-group-' +
8347                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8348                 });
8349             }
8350             
8351             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8352                 inputblock.cls += ' has-feedback';
8353                 inputblock.cn.push(feedback);
8354             }
8355         };
8356         
8357         if (align ==='left' && this.fieldLabel.length) {
8358             
8359             cfg.cn = [
8360                 {
8361                     tag : 'i',
8362                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8363                     tooltip : 'This field is required'
8364                 },
8365                 {
8366                     tag: 'label',
8367                     'for' :  id,
8368                     cls : 'control-label col-sm-' + this.labelWidth,
8369                     html : this.fieldLabel
8370
8371                 },
8372                 {
8373                     cls : "col-sm-" + (12 - this.labelWidth), 
8374                     cn: [
8375                         inputblock
8376                     ]
8377                 }
8378
8379             ];
8380             
8381             if(this.indicatorpos == 'right'){
8382                 cfg.cn = [
8383                     {
8384                         tag: 'label',
8385                         'for' :  id,
8386                         cls : 'control-label col-sm-' + this.labelWidth,
8387                         html : this.fieldLabel
8388
8389                     },
8390                     {
8391                         tag : 'i',
8392                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8393                         tooltip : 'This field is required'
8394                     },
8395                     {
8396                         cls : "col-sm-" + (12 - this.labelWidth), 
8397                         cn: [
8398                             inputblock
8399                         ]
8400                     }
8401
8402                 ];
8403             }
8404             
8405         } else if ( this.fieldLabel.length) {
8406                 
8407             cfg.cn = [
8408                 {
8409                     tag : 'i',
8410                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8411                     tooltip : 'This field is required'
8412                 },
8413                 {
8414                     tag: 'label',
8415                    //cls : 'input-group-addon',
8416                     html : this.fieldLabel
8417
8418                 },
8419
8420                inputblock
8421
8422            ];
8423            
8424            if(this.indicatorpos == 'right'){
8425                 
8426                 cfg.cn = [
8427                     {
8428                         tag: 'label',
8429                        //cls : 'input-group-addon',
8430                         html : this.fieldLabel
8431
8432                     },
8433                     {
8434                         tag : 'i',
8435                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8436                         tooltip : 'This field is required'
8437                     },
8438
8439                    inputblock
8440
8441                ];
8442
8443             }
8444
8445         } else {
8446             
8447             cfg.cn = [
8448
8449                     inputblock
8450
8451             ];
8452                 
8453                 
8454         };
8455         
8456         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8457            cfg.cls += ' navbar-form';
8458         }
8459         
8460         if (this.parentType === 'NavGroup') {
8461            cfg.cls += ' navbar-form';
8462            cfg.tag = 'li';
8463         }
8464         
8465         return cfg;
8466         
8467     },
8468     /**
8469      * return the real input element.
8470      */
8471     inputEl: function ()
8472     {
8473         return this.el.select('input.form-control',true).first();
8474     },
8475     
8476     tooltipEl : function()
8477     {
8478         return this.inputEl();
8479     },
8480     
8481     indicatorEl : function()
8482     {
8483         var indicator = this.el.select('i.roo-required-indicator',true).first();
8484         
8485         if(!indicator){
8486             return false;
8487         }
8488         
8489         return indicator;
8490         
8491     },
8492     
8493     setDisabled : function(v)
8494     {
8495         var i  = this.inputEl().dom;
8496         if (!v) {
8497             i.removeAttribute('disabled');
8498             return;
8499             
8500         }
8501         i.setAttribute('disabled','true');
8502     },
8503     initEvents : function()
8504     {
8505           
8506         this.inputEl().on("keydown" , this.fireKey,  this);
8507         this.inputEl().on("focus", this.onFocus,  this);
8508         this.inputEl().on("blur", this.onBlur,  this);
8509         
8510         this.inputEl().relayEvent('keyup', this);
8511         
8512         this.indicator = this.indicatorEl();
8513         
8514         if(this.indicator){
8515             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8516             this.indicator.hide();
8517         }
8518  
8519         // reference to original value for reset
8520         this.originalValue = this.getValue();
8521         //Roo.form.TextField.superclass.initEvents.call(this);
8522         if(this.validationEvent == 'keyup'){
8523             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8524             this.inputEl().on('keyup', this.filterValidation, this);
8525         }
8526         else if(this.validationEvent !== false){
8527             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8528         }
8529         
8530         if(this.selectOnFocus){
8531             this.on("focus", this.preFocus, this);
8532             
8533         }
8534         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8535             this.inputEl().on("keypress", this.filterKeys, this);
8536         }
8537        /* if(this.grow){
8538             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8539             this.el.on("click", this.autoSize,  this);
8540         }
8541         */
8542         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8543             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8544         }
8545         
8546         if (typeof(this.before) == 'object') {
8547             this.before.render(this.el.select('.roo-input-before',true).first());
8548         }
8549         if (typeof(this.after) == 'object') {
8550             this.after.render(this.el.select('.roo-input-after',true).first());
8551         }
8552         
8553         
8554     },
8555     filterValidation : function(e){
8556         if(!e.isNavKeyPress()){
8557             this.validationTask.delay(this.validationDelay);
8558         }
8559     },
8560      /**
8561      * Validates the field value
8562      * @return {Boolean} True if the value is valid, else false
8563      */
8564     validate : function(){
8565         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8566         if(this.disabled || this.validateValue(this.getRawValue())){
8567             this.markValid();
8568             return true;
8569         }
8570         
8571         this.markInvalid();
8572         return false;
8573     },
8574     
8575     
8576     /**
8577      * Validates a value according to the field's validation rules and marks the field as invalid
8578      * if the validation fails
8579      * @param {Mixed} value The value to validate
8580      * @return {Boolean} True if the value is valid, else false
8581      */
8582     validateValue : function(value){
8583         if(value.length < 1)  { // if it's blank
8584             if(this.allowBlank){
8585                 return true;
8586             }
8587             return false;
8588         }
8589         
8590         if(value.length < this.minLength){
8591             return false;
8592         }
8593         if(value.length > this.maxLength){
8594             return false;
8595         }
8596         if(this.vtype){
8597             var vt = Roo.form.VTypes;
8598             if(!vt[this.vtype](value, this)){
8599                 return false;
8600             }
8601         }
8602         if(typeof this.validator == "function"){
8603             var msg = this.validator(value);
8604             if(msg !== true){
8605                 return false;
8606             }
8607         }
8608         
8609         if(this.regex && !this.regex.test(value)){
8610             return false;
8611         }
8612         
8613         return true;
8614     },
8615
8616     
8617     
8618      // private
8619     fireKey : function(e){
8620         //Roo.log('field ' + e.getKey());
8621         if(e.isNavKeyPress()){
8622             this.fireEvent("specialkey", this, e);
8623         }
8624     },
8625     focus : function (selectText){
8626         if(this.rendered){
8627             this.inputEl().focus();
8628             if(selectText === true){
8629                 this.inputEl().dom.select();
8630             }
8631         }
8632         return this;
8633     } ,
8634     
8635     onFocus : function(){
8636         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8637            // this.el.addClass(this.focusClass);
8638         }
8639         if(!this.hasFocus){
8640             this.hasFocus = true;
8641             this.startValue = this.getValue();
8642             this.fireEvent("focus", this);
8643         }
8644     },
8645     
8646     beforeBlur : Roo.emptyFn,
8647
8648     
8649     // private
8650     onBlur : function(){
8651         this.beforeBlur();
8652         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8653             //this.el.removeClass(this.focusClass);
8654         }
8655         this.hasFocus = false;
8656         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8657             this.validate();
8658         }
8659         var v = this.getValue();
8660         if(String(v) !== String(this.startValue)){
8661             this.fireEvent('change', this, v, this.startValue);
8662         }
8663         this.fireEvent("blur", this);
8664     },
8665     
8666     /**
8667      * Resets the current field value to the originally loaded value and clears any validation messages
8668      */
8669     reset : function(){
8670         this.setValue(this.originalValue);
8671         this.validate();
8672     },
8673      /**
8674      * Returns the name of the field
8675      * @return {Mixed} name The name field
8676      */
8677     getName: function(){
8678         return this.name;
8679     },
8680      /**
8681      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8682      * @return {Mixed} value The field value
8683      */
8684     getValue : function(){
8685         
8686         var v = this.inputEl().getValue();
8687         
8688         return v;
8689     },
8690     /**
8691      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8692      * @return {Mixed} value The field value
8693      */
8694     getRawValue : function(){
8695         var v = this.inputEl().getValue();
8696         
8697         return v;
8698     },
8699     
8700     /**
8701      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8702      * @param {Mixed} value The value to set
8703      */
8704     setRawValue : function(v){
8705         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8706     },
8707     
8708     selectText : function(start, end){
8709         var v = this.getRawValue();
8710         if(v.length > 0){
8711             start = start === undefined ? 0 : start;
8712             end = end === undefined ? v.length : end;
8713             var d = this.inputEl().dom;
8714             if(d.setSelectionRange){
8715                 d.setSelectionRange(start, end);
8716             }else if(d.createTextRange){
8717                 var range = d.createTextRange();
8718                 range.moveStart("character", start);
8719                 range.moveEnd("character", v.length-end);
8720                 range.select();
8721             }
8722         }
8723     },
8724     
8725     /**
8726      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8727      * @param {Mixed} value The value to set
8728      */
8729     setValue : function(v){
8730         this.value = v;
8731         if(this.rendered){
8732             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8733             this.validate();
8734         }
8735     },
8736     
8737     /*
8738     processValue : function(value){
8739         if(this.stripCharsRe){
8740             var newValue = value.replace(this.stripCharsRe, '');
8741             if(newValue !== value){
8742                 this.setRawValue(newValue);
8743                 return newValue;
8744             }
8745         }
8746         return value;
8747     },
8748   */
8749     preFocus : function(){
8750         
8751         if(this.selectOnFocus){
8752             this.inputEl().dom.select();
8753         }
8754     },
8755     filterKeys : function(e){
8756         var k = e.getKey();
8757         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8758             return;
8759         }
8760         var c = e.getCharCode(), cc = String.fromCharCode(c);
8761         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8762             return;
8763         }
8764         if(!this.maskRe.test(cc)){
8765             e.stopEvent();
8766         }
8767     },
8768      /**
8769      * Clear any invalid styles/messages for this field
8770      */
8771     clearInvalid : function(){
8772         
8773         if(!this.el || this.preventMark){ // not rendered
8774             return;
8775         }
8776         
8777         if(this.indicator){
8778             this.indicator.hide();
8779         }
8780         
8781         this.el.removeClass(this.invalidClass);
8782         
8783         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8784             
8785             var feedback = this.el.select('.form-control-feedback', true).first();
8786             
8787             if(feedback){
8788                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8789             }
8790             
8791         }
8792         
8793         this.fireEvent('valid', this);
8794     },
8795     
8796      /**
8797      * Mark this field as valid
8798      */
8799     markValid : function()
8800     {
8801         if(!this.el  || this.preventMark){ // not rendered
8802             return;
8803         }
8804         
8805         this.el.removeClass([this.invalidClass, this.validClass]);
8806         
8807         var feedback = this.el.select('.form-control-feedback', true).first();
8808             
8809         if(feedback){
8810             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8811         }
8812
8813         if(this.disabled || this.allowBlank){
8814             return;
8815         }
8816         
8817         if(this.indicator){
8818             this.indicator.hide();
8819         }
8820         
8821         this.el.addClass(this.validClass);
8822         
8823         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8824             
8825             var feedback = this.el.select('.form-control-feedback', true).first();
8826             
8827             if(feedback){
8828                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8829                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8830             }
8831             
8832         }
8833         
8834         this.fireEvent('valid', this);
8835     },
8836     
8837      /**
8838      * Mark this field as invalid
8839      * @param {String} msg The validation message
8840      */
8841     markInvalid : function(msg)
8842     {
8843         if(!this.el  || this.preventMark){ // not rendered
8844             return;
8845         }
8846         
8847         this.el.removeClass([this.invalidClass, this.validClass]);
8848         
8849         var feedback = this.el.select('.form-control-feedback', true).first();
8850             
8851         if(feedback){
8852             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8853         }
8854
8855         if(this.disabled || this.allowBlank){
8856             return;
8857         }
8858         
8859         if(this.indicator){
8860             this.indicator.show();
8861         }
8862         
8863         this.el.addClass(this.invalidClass);
8864         
8865         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8866             
8867             var feedback = this.el.select('.form-control-feedback', true).first();
8868             
8869             if(feedback){
8870                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8871                 
8872                 if(this.getValue().length || this.forceFeedback){
8873                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8874                 }
8875                 
8876             }
8877             
8878         }
8879         
8880         this.fireEvent('invalid', this, msg);
8881     },
8882     // private
8883     SafariOnKeyDown : function(event)
8884     {
8885         // this is a workaround for a password hang bug on chrome/ webkit.
8886         
8887         var isSelectAll = false;
8888         
8889         if(this.inputEl().dom.selectionEnd > 0){
8890             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8891         }
8892         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8893             event.preventDefault();
8894             this.setValue('');
8895             return;
8896         }
8897         
8898         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8899             
8900             event.preventDefault();
8901             // this is very hacky as keydown always get's upper case.
8902             //
8903             var cc = String.fromCharCode(event.getCharCode());
8904             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8905             
8906         }
8907     },
8908     adjustWidth : function(tag, w){
8909         tag = tag.toLowerCase();
8910         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8911             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8912                 if(tag == 'input'){
8913                     return w + 2;
8914                 }
8915                 if(tag == 'textarea'){
8916                     return w-2;
8917                 }
8918             }else if(Roo.isOpera){
8919                 if(tag == 'input'){
8920                     return w + 2;
8921                 }
8922                 if(tag == 'textarea'){
8923                     return w-2;
8924                 }
8925             }
8926         }
8927         return w;
8928     }
8929     
8930 });
8931
8932  
8933 /*
8934  * - LGPL
8935  *
8936  * Input
8937  * 
8938  */
8939
8940 /**
8941  * @class Roo.bootstrap.TextArea
8942  * @extends Roo.bootstrap.Input
8943  * Bootstrap TextArea class
8944  * @cfg {Number} cols Specifies the visible width of a text area
8945  * @cfg {Number} rows Specifies the visible number of lines in a text area
8946  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8947  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8948  * @cfg {string} html text
8949  * 
8950  * @constructor
8951  * Create a new TextArea
8952  * @param {Object} config The config object
8953  */
8954
8955 Roo.bootstrap.TextArea = function(config){
8956     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8957    
8958 };
8959
8960 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8961      
8962     cols : false,
8963     rows : 5,
8964     readOnly : false,
8965     warp : 'soft',
8966     resize : false,
8967     value: false,
8968     html: false,
8969     
8970     getAutoCreate : function(){
8971         
8972         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8973         
8974         var id = Roo.id();
8975         
8976         var cfg = {};
8977         
8978         var input =  {
8979             tag: 'textarea',
8980             id : id,
8981             warp : this.warp,
8982             rows : this.rows,
8983             value : this.value || '',
8984             html: this.html || '',
8985             cls : 'form-control',
8986             placeholder : this.placeholder || '' 
8987             
8988         };
8989         
8990         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8991             input.maxLength = this.maxLength;
8992         }
8993         
8994         if(this.resize){
8995             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8996         }
8997         
8998         if(this.cols){
8999             input.cols = this.cols;
9000         }
9001         
9002         if (this.readOnly) {
9003             input.readonly = true;
9004         }
9005         
9006         if (this.name) {
9007             input.name = this.name;
9008         }
9009         
9010         if (this.size) {
9011             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9012         }
9013         
9014         var settings=this;
9015         ['xs','sm','md','lg'].map(function(size){
9016             if (settings[size]) {
9017                 cfg.cls += ' col-' + size + '-' + settings[size];
9018             }
9019         });
9020         
9021         var inputblock = input;
9022         
9023         if(this.hasFeedback && !this.allowBlank){
9024             
9025             var feedback = {
9026                 tag: 'span',
9027                 cls: 'glyphicon form-control-feedback'
9028             };
9029
9030             inputblock = {
9031                 cls : 'has-feedback',
9032                 cn :  [
9033                     input,
9034                     feedback
9035                 ] 
9036             };  
9037         }
9038         
9039         
9040         if (this.before || this.after) {
9041             
9042             inputblock = {
9043                 cls : 'input-group',
9044                 cn :  [] 
9045             };
9046             if (this.before) {
9047                 inputblock.cn.push({
9048                     tag :'span',
9049                     cls : 'input-group-addon',
9050                     html : this.before
9051                 });
9052             }
9053             
9054             inputblock.cn.push(input);
9055             
9056             if(this.hasFeedback && !this.allowBlank){
9057                 inputblock.cls += ' has-feedback';
9058                 inputblock.cn.push(feedback);
9059             }
9060             
9061             if (this.after) {
9062                 inputblock.cn.push({
9063                     tag :'span',
9064                     cls : 'input-group-addon',
9065                     html : this.after
9066                 });
9067             }
9068             
9069         }
9070         
9071         if (align ==='left' && this.fieldLabel.length) {
9072 //                Roo.log("left and has label");
9073                 cfg.cn = [
9074                     
9075                     {
9076                         tag: 'label',
9077                         'for' :  id,
9078                         cls : 'control-label col-sm-' + this.labelWidth,
9079                         html : this.fieldLabel
9080                         
9081                     },
9082                     {
9083                         cls : "col-sm-" + (12 - this.labelWidth), 
9084                         cn: [
9085                             inputblock
9086                         ]
9087                     }
9088                     
9089                 ];
9090         } else if ( this.fieldLabel.length) {
9091 //                Roo.log(" label");
9092                  cfg.cn = [
9093                    
9094                     {
9095                         tag: 'label',
9096                         //cls : 'input-group-addon',
9097                         html : this.fieldLabel
9098                         
9099                     },
9100                     
9101                     inputblock
9102                     
9103                 ];
9104
9105         } else {
9106             
9107 //                   Roo.log(" no label && no align");
9108                 cfg.cn = [
9109                     
9110                         inputblock
9111                     
9112                 ];
9113                 
9114                 
9115         }
9116         
9117         if (this.disabled) {
9118             input.disabled=true;
9119         }
9120         
9121         return cfg;
9122         
9123     },
9124     /**
9125      * return the real textarea element.
9126      */
9127     inputEl: function ()
9128     {
9129         return this.el.select('textarea.form-control',true).first();
9130     },
9131     
9132     /**
9133      * Clear any invalid styles/messages for this field
9134      */
9135     clearInvalid : function()
9136     {
9137         
9138         if(!this.el || this.preventMark){ // not rendered
9139             return;
9140         }
9141         
9142         var label = this.el.select('label', true).first();
9143         var icon = this.el.select('i.fa-star', true).first();
9144         
9145         if(label && icon){
9146             icon.remove();
9147         }
9148         
9149         this.el.removeClass(this.invalidClass);
9150         
9151         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9152             
9153             var feedback = this.el.select('.form-control-feedback', true).first();
9154             
9155             if(feedback){
9156                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9157             }
9158             
9159         }
9160         
9161         this.fireEvent('valid', this);
9162     },
9163     
9164      /**
9165      * Mark this field as valid
9166      */
9167     markValid : function()
9168     {
9169         if(!this.el  || this.preventMark){ // not rendered
9170             return;
9171         }
9172         
9173         this.el.removeClass([this.invalidClass, this.validClass]);
9174         
9175         var feedback = this.el.select('.form-control-feedback', true).first();
9176             
9177         if(feedback){
9178             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9179         }
9180
9181         if(this.disabled || this.allowBlank){
9182             return;
9183         }
9184         
9185         var label = this.el.select('label', true).first();
9186         var icon = this.el.select('i.fa-star', true).first();
9187         
9188         if(label && icon){
9189             icon.remove();
9190         }
9191         
9192         this.el.addClass(this.validClass);
9193         
9194         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9195             
9196             var feedback = this.el.select('.form-control-feedback', true).first();
9197             
9198             if(feedback){
9199                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9200                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9201             }
9202             
9203         }
9204         
9205         this.fireEvent('valid', this);
9206     },
9207     
9208      /**
9209      * Mark this field as invalid
9210      * @param {String} msg The validation message
9211      */
9212     markInvalid : function(msg)
9213     {
9214         if(!this.el  || this.preventMark){ // not rendered
9215             return;
9216         }
9217         
9218         this.el.removeClass([this.invalidClass, this.validClass]);
9219         
9220         var feedback = this.el.select('.form-control-feedback', true).first();
9221             
9222         if(feedback){
9223             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9224         }
9225
9226         if(this.disabled || this.allowBlank){
9227             return;
9228         }
9229         
9230         var label = this.el.select('label', true).first();
9231         var icon = this.el.select('i.fa-star', true).first();
9232         
9233         if(!this.getValue().length && label && !icon){
9234             this.el.createChild({
9235                 tag : 'i',
9236                 cls : 'text-danger fa fa-lg fa-star',
9237                 tooltip : 'This field is required',
9238                 style : 'margin-right:5px;'
9239             }, label, true);
9240         }
9241
9242         this.el.addClass(this.invalidClass);
9243         
9244         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9245             
9246             var feedback = this.el.select('.form-control-feedback', true).first();
9247             
9248             if(feedback){
9249                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9250                 
9251                 if(this.getValue().length || this.forceFeedback){
9252                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9253                 }
9254                 
9255             }
9256             
9257         }
9258         
9259         this.fireEvent('invalid', this, msg);
9260     }
9261 });
9262
9263  
9264 /*
9265  * - LGPL
9266  *
9267  * trigger field - base class for combo..
9268  * 
9269  */
9270  
9271 /**
9272  * @class Roo.bootstrap.TriggerField
9273  * @extends Roo.bootstrap.Input
9274  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9275  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9276  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9277  * for which you can provide a custom implementation.  For example:
9278  * <pre><code>
9279 var trigger = new Roo.bootstrap.TriggerField();
9280 trigger.onTriggerClick = myTriggerFn;
9281 trigger.applyTo('my-field');
9282 </code></pre>
9283  *
9284  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9285  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9286  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9287  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9288  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9289
9290  * @constructor
9291  * Create a new TriggerField.
9292  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9293  * to the base TextField)
9294  */
9295 Roo.bootstrap.TriggerField = function(config){
9296     this.mimicing = false;
9297     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9298 };
9299
9300 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9301     /**
9302      * @cfg {String} triggerClass A CSS class to apply to the trigger
9303      */
9304      /**
9305      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9306      */
9307     hideTrigger:false,
9308
9309     /**
9310      * @cfg {Boolean} removable (true|false) special filter default false
9311      */
9312     removable : false,
9313     
9314     /** @cfg {Boolean} grow @hide */
9315     /** @cfg {Number} growMin @hide */
9316     /** @cfg {Number} growMax @hide */
9317
9318     /**
9319      * @hide 
9320      * @method
9321      */
9322     autoSize: Roo.emptyFn,
9323     // private
9324     monitorTab : true,
9325     // private
9326     deferHeight : true,
9327
9328     
9329     actionMode : 'wrap',
9330     
9331     caret : false,
9332     
9333     
9334     getAutoCreate : function(){
9335        
9336         var align = this.labelAlign || this.parentLabelAlign();
9337         
9338         var id = Roo.id();
9339         
9340         var cfg = {
9341             cls: 'form-group' //input-group
9342         };
9343         
9344         
9345         var input =  {
9346             tag: 'input',
9347             id : id,
9348             type : this.inputType,
9349             cls : 'form-control',
9350             autocomplete: 'new-password',
9351             placeholder : this.placeholder || '' 
9352             
9353         };
9354         if (this.name) {
9355             input.name = this.name;
9356         }
9357         if (this.size) {
9358             input.cls += ' input-' + this.size;
9359         }
9360         
9361         if (this.disabled) {
9362             input.disabled=true;
9363         }
9364         
9365         var inputblock = input;
9366         
9367         if(this.hasFeedback && !this.allowBlank){
9368             
9369             var feedback = {
9370                 tag: 'span',
9371                 cls: 'glyphicon form-control-feedback'
9372             };
9373             
9374             if(this.removable && !this.editable && !this.tickable){
9375                 inputblock = {
9376                     cls : 'has-feedback',
9377                     cn :  [
9378                         inputblock,
9379                         {
9380                             tag: 'button',
9381                             html : 'x',
9382                             cls : 'roo-combo-removable-btn close'
9383                         },
9384                         feedback
9385                     ] 
9386                 };
9387             } else {
9388                 inputblock = {
9389                     cls : 'has-feedback',
9390                     cn :  [
9391                         inputblock,
9392                         feedback
9393                     ] 
9394                 };
9395             }
9396
9397         } else {
9398             if(this.removable && !this.editable && !this.tickable){
9399                 inputblock = {
9400                     cls : 'roo-removable',
9401                     cn :  [
9402                         inputblock,
9403                         {
9404                             tag: 'button',
9405                             html : 'x',
9406                             cls : 'roo-combo-removable-btn close'
9407                         }
9408                     ] 
9409                 };
9410             }
9411         }
9412         
9413         if (this.before || this.after) {
9414             
9415             inputblock = {
9416                 cls : 'input-group',
9417                 cn :  [] 
9418             };
9419             if (this.before) {
9420                 inputblock.cn.push({
9421                     tag :'span',
9422                     cls : 'input-group-addon',
9423                     html : this.before
9424                 });
9425             }
9426             
9427             inputblock.cn.push(input);
9428             
9429             if(this.hasFeedback && !this.allowBlank){
9430                 inputblock.cls += ' has-feedback';
9431                 inputblock.cn.push(feedback);
9432             }
9433             
9434             if (this.after) {
9435                 inputblock.cn.push({
9436                     tag :'span',
9437                     cls : 'input-group-addon',
9438                     html : this.after
9439                 });
9440             }
9441             
9442         };
9443         
9444         var box = {
9445             tag: 'div',
9446             cn: [
9447                 {
9448                     tag: 'input',
9449                     type : 'hidden',
9450                     cls: 'form-hidden-field'
9451                 },
9452                 inputblock
9453             ]
9454             
9455         };
9456         
9457         if(this.multiple){
9458             box = {
9459                 tag: 'div',
9460                 cn: [
9461                     {
9462                         tag: 'input',
9463                         type : 'hidden',
9464                         cls: 'form-hidden-field'
9465                     },
9466                     {
9467                         tag: 'ul',
9468                         cls: 'roo-select2-choices',
9469                         cn:[
9470                             {
9471                                 tag: 'li',
9472                                 cls: 'roo-select2-search-field',
9473                                 cn: [
9474
9475                                     inputblock
9476                                 ]
9477                             }
9478                         ]
9479                     }
9480                 ]
9481             }
9482         };
9483         
9484         var combobox = {
9485             cls: 'roo-select2-container input-group',
9486             cn: [
9487                 box
9488 //                {
9489 //                    tag: 'ul',
9490 //                    cls: 'typeahead typeahead-long dropdown-menu',
9491 //                    style: 'display:none'
9492 //                }
9493             ]
9494         };
9495         
9496         if(!this.multiple && this.showToggleBtn){
9497             
9498             var caret = {
9499                         tag: 'span',
9500                         cls: 'caret'
9501              };
9502             if (this.caret != false) {
9503                 caret = {
9504                      tag: 'i',
9505                      cls: 'fa fa-' + this.caret
9506                 };
9507                 
9508             }
9509             
9510             combobox.cn.push({
9511                 tag :'span',
9512                 cls : 'input-group-addon btn dropdown-toggle',
9513                 cn : [
9514                     caret,
9515                     {
9516                         tag: 'span',
9517                         cls: 'combobox-clear',
9518                         cn  : [
9519                             {
9520                                 tag : 'i',
9521                                 cls: 'icon-remove'
9522                             }
9523                         ]
9524                     }
9525                 ]
9526
9527             })
9528         }
9529         
9530         if(this.multiple){
9531             combobox.cls += ' roo-select2-container-multi';
9532         }
9533         
9534         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9535             
9536 //                Roo.log("left and has label");
9537             cfg.cn = [
9538                 {
9539                     tag : 'i',
9540                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9541                     tooltip : 'This field is required'
9542                 },
9543                 {
9544                     tag: 'label',
9545                     'for' :  id,
9546                     cls : 'control-label col-sm-' + this.labelWidth,
9547                     html : this.fieldLabel
9548
9549                 },
9550                 {
9551                     cls : "col-sm-" + (12 - this.labelWidth), 
9552                     cn: [
9553                         combobox
9554                     ]
9555                 }
9556
9557             ];
9558             
9559             if(this.indicatorpos == 'right'){
9560                 cfg.cn = [
9561                     {
9562                         tag: 'label',
9563                         'for' :  id,
9564                         cls : 'control-label col-sm-' + this.labelWidth,
9565                         html : this.fieldLabel
9566
9567                     },
9568                     {
9569                         tag : 'i',
9570                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9571                         tooltip : 'This field is required'
9572                     },
9573                     {
9574                         cls : "col-sm-" + (12 - this.labelWidth), 
9575                         cn: [
9576                             combobox
9577                         ]
9578                     }
9579
9580                 ];
9581             }
9582             
9583         } else if ( this.fieldLabel.length) {
9584 //                Roo.log(" label");
9585             cfg.cn = [
9586                 {
9587                    tag : 'i',
9588                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9589                    tooltip : 'This field is required'
9590                },
9591                {
9592                    tag: 'label',
9593                    //cls : 'input-group-addon',
9594                    html : this.fieldLabel
9595
9596                },
9597
9598                combobox
9599
9600             ];
9601             
9602             if(this.indicatorpos == 'right'){
9603                 
9604                 cfg.cn = [
9605                     {
9606                        tag: 'label',
9607                        //cls : 'input-group-addon',
9608                        html : this.fieldLabel
9609
9610                     },
9611                     {
9612                        tag : 'i',
9613                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9614                        tooltip : 'This field is required'
9615                     },
9616                     
9617                     combobox
9618
9619                 ];
9620
9621             }
9622
9623         } else {
9624             
9625 //                Roo.log(" no label && no align");
9626                 cfg = combobox
9627                      
9628                 
9629         }
9630          
9631         var settings=this;
9632         ['xs','sm','md','lg'].map(function(size){
9633             if (settings[size]) {
9634                 cfg.cls += ' col-' + size + '-' + settings[size];
9635             }
9636         });
9637         
9638         return cfg;
9639         
9640     },
9641     
9642     
9643     
9644     // private
9645     onResize : function(w, h){
9646 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9647 //        if(typeof w == 'number'){
9648 //            var x = w - this.trigger.getWidth();
9649 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9650 //            this.trigger.setStyle('left', x+'px');
9651 //        }
9652     },
9653
9654     // private
9655     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9656
9657     // private
9658     getResizeEl : function(){
9659         return this.inputEl();
9660     },
9661
9662     // private
9663     getPositionEl : function(){
9664         return this.inputEl();
9665     },
9666
9667     // private
9668     alignErrorIcon : function(){
9669         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9670     },
9671
9672     // private
9673     initEvents : function(){
9674         
9675         this.createList();
9676         
9677         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9678         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9679         if(!this.multiple && this.showToggleBtn){
9680             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9681             if(this.hideTrigger){
9682                 this.trigger.setDisplayed(false);
9683             }
9684             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9685         }
9686         
9687         if(this.multiple){
9688             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9689         }
9690         
9691         if(this.removable && !this.editable && !this.tickable){
9692             var close = this.closeTriggerEl();
9693             
9694             if(close){
9695                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9696                 close.on('click', this.removeBtnClick, this, close);
9697             }
9698         }
9699         
9700         //this.trigger.addClassOnOver('x-form-trigger-over');
9701         //this.trigger.addClassOnClick('x-form-trigger-click');
9702         
9703         //if(!this.width){
9704         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9705         //}
9706     },
9707     
9708     closeTriggerEl : function()
9709     {
9710         var close = this.el.select('.roo-combo-removable-btn', true).first();
9711         return close ? close : false;
9712     },
9713     
9714     removeBtnClick : function(e, h, el)
9715     {
9716         e.preventDefault();
9717         
9718         if(this.fireEvent("remove", this) !== false){
9719             this.reset();
9720             this.fireEvent("afterremove", this)
9721         }
9722     },
9723     
9724     createList : function()
9725     {
9726         this.list = Roo.get(document.body).createChild({
9727             tag: 'ul',
9728             cls: 'typeahead typeahead-long dropdown-menu',
9729             style: 'display:none'
9730         });
9731         
9732         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9733         
9734     },
9735
9736     // private
9737     initTrigger : function(){
9738        
9739     },
9740
9741     // private
9742     onDestroy : function(){
9743         if(this.trigger){
9744             this.trigger.removeAllListeners();
9745           //  this.trigger.remove();
9746         }
9747         //if(this.wrap){
9748         //    this.wrap.remove();
9749         //}
9750         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9751     },
9752
9753     // private
9754     onFocus : function(){
9755         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9756         /*
9757         if(!this.mimicing){
9758             this.wrap.addClass('x-trigger-wrap-focus');
9759             this.mimicing = true;
9760             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9761             if(this.monitorTab){
9762                 this.el.on("keydown", this.checkTab, this);
9763             }
9764         }
9765         */
9766     },
9767
9768     // private
9769     checkTab : function(e){
9770         if(e.getKey() == e.TAB){
9771             this.triggerBlur();
9772         }
9773     },
9774
9775     // private
9776     onBlur : function(){
9777         // do nothing
9778     },
9779
9780     // private
9781     mimicBlur : function(e, t){
9782         /*
9783         if(!this.wrap.contains(t) && this.validateBlur()){
9784             this.triggerBlur();
9785         }
9786         */
9787     },
9788
9789     // private
9790     triggerBlur : function(){
9791         this.mimicing = false;
9792         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9793         if(this.monitorTab){
9794             this.el.un("keydown", this.checkTab, this);
9795         }
9796         //this.wrap.removeClass('x-trigger-wrap-focus');
9797         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9798     },
9799
9800     // private
9801     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9802     validateBlur : function(e, t){
9803         return true;
9804     },
9805
9806     // private
9807     onDisable : function(){
9808         this.inputEl().dom.disabled = true;
9809         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9810         //if(this.wrap){
9811         //    this.wrap.addClass('x-item-disabled');
9812         //}
9813     },
9814
9815     // private
9816     onEnable : function(){
9817         this.inputEl().dom.disabled = false;
9818         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9819         //if(this.wrap){
9820         //    this.el.removeClass('x-item-disabled');
9821         //}
9822     },
9823
9824     // private
9825     onShow : function(){
9826         var ae = this.getActionEl();
9827         
9828         if(ae){
9829             ae.dom.style.display = '';
9830             ae.dom.style.visibility = 'visible';
9831         }
9832     },
9833
9834     // private
9835     
9836     onHide : function(){
9837         var ae = this.getActionEl();
9838         ae.dom.style.display = 'none';
9839     },
9840
9841     /**
9842      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9843      * by an implementing function.
9844      * @method
9845      * @param {EventObject} e
9846      */
9847     onTriggerClick : Roo.emptyFn
9848 });
9849  /*
9850  * Based on:
9851  * Ext JS Library 1.1.1
9852  * Copyright(c) 2006-2007, Ext JS, LLC.
9853  *
9854  * Originally Released Under LGPL - original licence link has changed is not relivant.
9855  *
9856  * Fork - LGPL
9857  * <script type="text/javascript">
9858  */
9859
9860
9861 /**
9862  * @class Roo.data.SortTypes
9863  * @singleton
9864  * Defines the default sorting (casting?) comparison functions used when sorting data.
9865  */
9866 Roo.data.SortTypes = {
9867     /**
9868      * Default sort that does nothing
9869      * @param {Mixed} s The value being converted
9870      * @return {Mixed} The comparison value
9871      */
9872     none : function(s){
9873         return s;
9874     },
9875     
9876     /**
9877      * The regular expression used to strip tags
9878      * @type {RegExp}
9879      * @property
9880      */
9881     stripTagsRE : /<\/?[^>]+>/gi,
9882     
9883     /**
9884      * Strips all HTML tags to sort on text only
9885      * @param {Mixed} s The value being converted
9886      * @return {String} The comparison value
9887      */
9888     asText : function(s){
9889         return String(s).replace(this.stripTagsRE, "");
9890     },
9891     
9892     /**
9893      * Strips all HTML tags to sort on text only - Case insensitive
9894      * @param {Mixed} s The value being converted
9895      * @return {String} The comparison value
9896      */
9897     asUCText : function(s){
9898         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9899     },
9900     
9901     /**
9902      * Case insensitive string
9903      * @param {Mixed} s The value being converted
9904      * @return {String} The comparison value
9905      */
9906     asUCString : function(s) {
9907         return String(s).toUpperCase();
9908     },
9909     
9910     /**
9911      * Date sorting
9912      * @param {Mixed} s The value being converted
9913      * @return {Number} The comparison value
9914      */
9915     asDate : function(s) {
9916         if(!s){
9917             return 0;
9918         }
9919         if(s instanceof Date){
9920             return s.getTime();
9921         }
9922         return Date.parse(String(s));
9923     },
9924     
9925     /**
9926      * Float sorting
9927      * @param {Mixed} s The value being converted
9928      * @return {Float} The comparison value
9929      */
9930     asFloat : function(s) {
9931         var val = parseFloat(String(s).replace(/,/g, ""));
9932         if(isNaN(val)) {
9933             val = 0;
9934         }
9935         return val;
9936     },
9937     
9938     /**
9939      * Integer sorting
9940      * @param {Mixed} s The value being converted
9941      * @return {Number} The comparison value
9942      */
9943     asInt : function(s) {
9944         var val = parseInt(String(s).replace(/,/g, ""));
9945         if(isNaN(val)) {
9946             val = 0;
9947         }
9948         return val;
9949     }
9950 };/*
9951  * Based on:
9952  * Ext JS Library 1.1.1
9953  * Copyright(c) 2006-2007, Ext JS, LLC.
9954  *
9955  * Originally Released Under LGPL - original licence link has changed is not relivant.
9956  *
9957  * Fork - LGPL
9958  * <script type="text/javascript">
9959  */
9960
9961 /**
9962 * @class Roo.data.Record
9963  * Instances of this class encapsulate both record <em>definition</em> information, and record
9964  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9965  * to access Records cached in an {@link Roo.data.Store} object.<br>
9966  * <p>
9967  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9968  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9969  * objects.<br>
9970  * <p>
9971  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9972  * @constructor
9973  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9974  * {@link #create}. The parameters are the same.
9975  * @param {Array} data An associative Array of data values keyed by the field name.
9976  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9977  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9978  * not specified an integer id is generated.
9979  */
9980 Roo.data.Record = function(data, id){
9981     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9982     this.data = data;
9983 };
9984
9985 /**
9986  * Generate a constructor for a specific record layout.
9987  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9988  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9989  * Each field definition object may contain the following properties: <ul>
9990  * <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,
9991  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9992  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9993  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9994  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9995  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9996  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9997  * this may be omitted.</p></li>
9998  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9999  * <ul><li>auto (Default, implies no conversion)</li>
10000  * <li>string</li>
10001  * <li>int</li>
10002  * <li>float</li>
10003  * <li>boolean</li>
10004  * <li>date</li></ul></p></li>
10005  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10006  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10007  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10008  * by the Reader into an object that will be stored in the Record. It is passed the
10009  * following parameters:<ul>
10010  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10011  * </ul></p></li>
10012  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10013  * </ul>
10014  * <br>usage:<br><pre><code>
10015 var TopicRecord = Roo.data.Record.create(
10016     {name: 'title', mapping: 'topic_title'},
10017     {name: 'author', mapping: 'username'},
10018     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10019     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10020     {name: 'lastPoster', mapping: 'user2'},
10021     {name: 'excerpt', mapping: 'post_text'}
10022 );
10023
10024 var myNewRecord = new TopicRecord({
10025     title: 'Do my job please',
10026     author: 'noobie',
10027     totalPosts: 1,
10028     lastPost: new Date(),
10029     lastPoster: 'Animal',
10030     excerpt: 'No way dude!'
10031 });
10032 myStore.add(myNewRecord);
10033 </code></pre>
10034  * @method create
10035  * @static
10036  */
10037 Roo.data.Record.create = function(o){
10038     var f = function(){
10039         f.superclass.constructor.apply(this, arguments);
10040     };
10041     Roo.extend(f, Roo.data.Record);
10042     var p = f.prototype;
10043     p.fields = new Roo.util.MixedCollection(false, function(field){
10044         return field.name;
10045     });
10046     for(var i = 0, len = o.length; i < len; i++){
10047         p.fields.add(new Roo.data.Field(o[i]));
10048     }
10049     f.getField = function(name){
10050         return p.fields.get(name);  
10051     };
10052     return f;
10053 };
10054
10055 Roo.data.Record.AUTO_ID = 1000;
10056 Roo.data.Record.EDIT = 'edit';
10057 Roo.data.Record.REJECT = 'reject';
10058 Roo.data.Record.COMMIT = 'commit';
10059
10060 Roo.data.Record.prototype = {
10061     /**
10062      * Readonly flag - true if this record has been modified.
10063      * @type Boolean
10064      */
10065     dirty : false,
10066     editing : false,
10067     error: null,
10068     modified: null,
10069
10070     // private
10071     join : function(store){
10072         this.store = store;
10073     },
10074
10075     /**
10076      * Set the named field to the specified value.
10077      * @param {String} name The name of the field to set.
10078      * @param {Object} value The value to set the field to.
10079      */
10080     set : function(name, value){
10081         if(this.data[name] == value){
10082             return;
10083         }
10084         this.dirty = true;
10085         if(!this.modified){
10086             this.modified = {};
10087         }
10088         if(typeof this.modified[name] == 'undefined'){
10089             this.modified[name] = this.data[name];
10090         }
10091         this.data[name] = value;
10092         if(!this.editing && this.store){
10093             this.store.afterEdit(this);
10094         }       
10095     },
10096
10097     /**
10098      * Get the value of the named field.
10099      * @param {String} name The name of the field to get the value of.
10100      * @return {Object} The value of the field.
10101      */
10102     get : function(name){
10103         return this.data[name]; 
10104     },
10105
10106     // private
10107     beginEdit : function(){
10108         this.editing = true;
10109         this.modified = {}; 
10110     },
10111
10112     // private
10113     cancelEdit : function(){
10114         this.editing = false;
10115         delete this.modified;
10116     },
10117
10118     // private
10119     endEdit : function(){
10120         this.editing = false;
10121         if(this.dirty && this.store){
10122             this.store.afterEdit(this);
10123         }
10124     },
10125
10126     /**
10127      * Usually called by the {@link Roo.data.Store} which owns the Record.
10128      * Rejects all changes made to the Record since either creation, or the last commit operation.
10129      * Modified fields are reverted to their original values.
10130      * <p>
10131      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10132      * of reject operations.
10133      */
10134     reject : function(){
10135         var m = this.modified;
10136         for(var n in m){
10137             if(typeof m[n] != "function"){
10138                 this.data[n] = m[n];
10139             }
10140         }
10141         this.dirty = false;
10142         delete this.modified;
10143         this.editing = false;
10144         if(this.store){
10145             this.store.afterReject(this);
10146         }
10147     },
10148
10149     /**
10150      * Usually called by the {@link Roo.data.Store} which owns the Record.
10151      * Commits all changes made to the Record since either creation, or the last commit operation.
10152      * <p>
10153      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10154      * of commit operations.
10155      */
10156     commit : function(){
10157         this.dirty = false;
10158         delete this.modified;
10159         this.editing = false;
10160         if(this.store){
10161             this.store.afterCommit(this);
10162         }
10163     },
10164
10165     // private
10166     hasError : function(){
10167         return this.error != null;
10168     },
10169
10170     // private
10171     clearError : function(){
10172         this.error = null;
10173     },
10174
10175     /**
10176      * Creates a copy of this record.
10177      * @param {String} id (optional) A new record id if you don't want to use this record's id
10178      * @return {Record}
10179      */
10180     copy : function(newId) {
10181         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10182     }
10183 };/*
10184  * Based on:
10185  * Ext JS Library 1.1.1
10186  * Copyright(c) 2006-2007, Ext JS, LLC.
10187  *
10188  * Originally Released Under LGPL - original licence link has changed is not relivant.
10189  *
10190  * Fork - LGPL
10191  * <script type="text/javascript">
10192  */
10193
10194
10195
10196 /**
10197  * @class Roo.data.Store
10198  * @extends Roo.util.Observable
10199  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10200  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10201  * <p>
10202  * 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
10203  * has no knowledge of the format of the data returned by the Proxy.<br>
10204  * <p>
10205  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10206  * instances from the data object. These records are cached and made available through accessor functions.
10207  * @constructor
10208  * Creates a new Store.
10209  * @param {Object} config A config object containing the objects needed for the Store to access data,
10210  * and read the data into Records.
10211  */
10212 Roo.data.Store = function(config){
10213     this.data = new Roo.util.MixedCollection(false);
10214     this.data.getKey = function(o){
10215         return o.id;
10216     };
10217     this.baseParams = {};
10218     // private
10219     this.paramNames = {
10220         "start" : "start",
10221         "limit" : "limit",
10222         "sort" : "sort",
10223         "dir" : "dir",
10224         "multisort" : "_multisort"
10225     };
10226
10227     if(config && config.data){
10228         this.inlineData = config.data;
10229         delete config.data;
10230     }
10231
10232     Roo.apply(this, config);
10233     
10234     if(this.reader){ // reader passed
10235         this.reader = Roo.factory(this.reader, Roo.data);
10236         this.reader.xmodule = this.xmodule || false;
10237         if(!this.recordType){
10238             this.recordType = this.reader.recordType;
10239         }
10240         if(this.reader.onMetaChange){
10241             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10242         }
10243     }
10244
10245     if(this.recordType){
10246         this.fields = this.recordType.prototype.fields;
10247     }
10248     this.modified = [];
10249
10250     this.addEvents({
10251         /**
10252          * @event datachanged
10253          * Fires when the data cache has changed, and a widget which is using this Store
10254          * as a Record cache should refresh its view.
10255          * @param {Store} this
10256          */
10257         datachanged : true,
10258         /**
10259          * @event metachange
10260          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10261          * @param {Store} this
10262          * @param {Object} meta The JSON metadata
10263          */
10264         metachange : true,
10265         /**
10266          * @event add
10267          * Fires when Records have been added to the Store
10268          * @param {Store} this
10269          * @param {Roo.data.Record[]} records The array of Records added
10270          * @param {Number} index The index at which the record(s) were added
10271          */
10272         add : true,
10273         /**
10274          * @event remove
10275          * Fires when a Record has been removed from the Store
10276          * @param {Store} this
10277          * @param {Roo.data.Record} record The Record that was removed
10278          * @param {Number} index The index at which the record was removed
10279          */
10280         remove : true,
10281         /**
10282          * @event update
10283          * Fires when a Record has been updated
10284          * @param {Store} this
10285          * @param {Roo.data.Record} record The Record that was updated
10286          * @param {String} operation The update operation being performed.  Value may be one of:
10287          * <pre><code>
10288  Roo.data.Record.EDIT
10289  Roo.data.Record.REJECT
10290  Roo.data.Record.COMMIT
10291          * </code></pre>
10292          */
10293         update : true,
10294         /**
10295          * @event clear
10296          * Fires when the data cache has been cleared.
10297          * @param {Store} this
10298          */
10299         clear : true,
10300         /**
10301          * @event beforeload
10302          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10303          * the load action will be canceled.
10304          * @param {Store} this
10305          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10306          */
10307         beforeload : true,
10308         /**
10309          * @event beforeloadadd
10310          * Fires after a new set of Records has been loaded.
10311          * @param {Store} this
10312          * @param {Roo.data.Record[]} records The Records that were loaded
10313          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10314          */
10315         beforeloadadd : true,
10316         /**
10317          * @event load
10318          * Fires after a new set of Records has been loaded, before they are added to the store.
10319          * @param {Store} this
10320          * @param {Roo.data.Record[]} records The Records that were loaded
10321          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10322          * @params {Object} return from reader
10323          */
10324         load : true,
10325         /**
10326          * @event loadexception
10327          * Fires if an exception occurs in the Proxy during loading.
10328          * Called with the signature of the Proxy's "loadexception" event.
10329          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10330          * 
10331          * @param {Proxy} 
10332          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10333          * @param {Object} load options 
10334          * @param {Object} jsonData from your request (normally this contains the Exception)
10335          */
10336         loadexception : true
10337     });
10338     
10339     if(this.proxy){
10340         this.proxy = Roo.factory(this.proxy, Roo.data);
10341         this.proxy.xmodule = this.xmodule || false;
10342         this.relayEvents(this.proxy,  ["loadexception"]);
10343     }
10344     this.sortToggle = {};
10345     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10346
10347     Roo.data.Store.superclass.constructor.call(this);
10348
10349     if(this.inlineData){
10350         this.loadData(this.inlineData);
10351         delete this.inlineData;
10352     }
10353 };
10354
10355 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10356      /**
10357     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10358     * without a remote query - used by combo/forms at present.
10359     */
10360     
10361     /**
10362     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10363     */
10364     /**
10365     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10366     */
10367     /**
10368     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10369     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10370     */
10371     /**
10372     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10373     * on any HTTP request
10374     */
10375     /**
10376     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10377     */
10378     /**
10379     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10380     */
10381     multiSort: false,
10382     /**
10383     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10384     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10385     */
10386     remoteSort : false,
10387
10388     /**
10389     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10390      * loaded or when a record is removed. (defaults to false).
10391     */
10392     pruneModifiedRecords : false,
10393
10394     // private
10395     lastOptions : null,
10396
10397     /**
10398      * Add Records to the Store and fires the add event.
10399      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10400      */
10401     add : function(records){
10402         records = [].concat(records);
10403         for(var i = 0, len = records.length; i < len; i++){
10404             records[i].join(this);
10405         }
10406         var index = this.data.length;
10407         this.data.addAll(records);
10408         this.fireEvent("add", this, records, index);
10409     },
10410
10411     /**
10412      * Remove a Record from the Store and fires the remove event.
10413      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10414      */
10415     remove : function(record){
10416         var index = this.data.indexOf(record);
10417         this.data.removeAt(index);
10418         if(this.pruneModifiedRecords){
10419             this.modified.remove(record);
10420         }
10421         this.fireEvent("remove", this, record, index);
10422     },
10423
10424     /**
10425      * Remove all Records from the Store and fires the clear event.
10426      */
10427     removeAll : function(){
10428         this.data.clear();
10429         if(this.pruneModifiedRecords){
10430             this.modified = [];
10431         }
10432         this.fireEvent("clear", this);
10433     },
10434
10435     /**
10436      * Inserts Records to the Store at the given index and fires the add event.
10437      * @param {Number} index The start index at which to insert the passed Records.
10438      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10439      */
10440     insert : function(index, records){
10441         records = [].concat(records);
10442         for(var i = 0, len = records.length; i < len; i++){
10443             this.data.insert(index, records[i]);
10444             records[i].join(this);
10445         }
10446         this.fireEvent("add", this, records, index);
10447     },
10448
10449     /**
10450      * Get the index within the cache of the passed Record.
10451      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10452      * @return {Number} The index of the passed Record. Returns -1 if not found.
10453      */
10454     indexOf : function(record){
10455         return this.data.indexOf(record);
10456     },
10457
10458     /**
10459      * Get the index within the cache of the Record with the passed id.
10460      * @param {String} id The id of the Record to find.
10461      * @return {Number} The index of the Record. Returns -1 if not found.
10462      */
10463     indexOfId : function(id){
10464         return this.data.indexOfKey(id);
10465     },
10466
10467     /**
10468      * Get the Record with the specified id.
10469      * @param {String} id The id of the Record to find.
10470      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10471      */
10472     getById : function(id){
10473         return this.data.key(id);
10474     },
10475
10476     /**
10477      * Get the Record at the specified index.
10478      * @param {Number} index The index of the Record to find.
10479      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10480      */
10481     getAt : function(index){
10482         return this.data.itemAt(index);
10483     },
10484
10485     /**
10486      * Returns a range of Records between specified indices.
10487      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10488      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10489      * @return {Roo.data.Record[]} An array of Records
10490      */
10491     getRange : function(start, end){
10492         return this.data.getRange(start, end);
10493     },
10494
10495     // private
10496     storeOptions : function(o){
10497         o = Roo.apply({}, o);
10498         delete o.callback;
10499         delete o.scope;
10500         this.lastOptions = o;
10501     },
10502
10503     /**
10504      * Loads the Record cache from the configured Proxy using the configured Reader.
10505      * <p>
10506      * If using remote paging, then the first load call must specify the <em>start</em>
10507      * and <em>limit</em> properties in the options.params property to establish the initial
10508      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10509      * <p>
10510      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10511      * and this call will return before the new data has been loaded. Perform any post-processing
10512      * in a callback function, or in a "load" event handler.</strong>
10513      * <p>
10514      * @param {Object} options An object containing properties which control loading options:<ul>
10515      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10516      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10517      * passed the following arguments:<ul>
10518      * <li>r : Roo.data.Record[]</li>
10519      * <li>options: Options object from the load call</li>
10520      * <li>success: Boolean success indicator</li></ul></li>
10521      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10522      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10523      * </ul>
10524      */
10525     load : function(options){
10526         options = options || {};
10527         if(this.fireEvent("beforeload", this, options) !== false){
10528             this.storeOptions(options);
10529             var p = Roo.apply(options.params || {}, this.baseParams);
10530             // if meta was not loaded from remote source.. try requesting it.
10531             if (!this.reader.metaFromRemote) {
10532                 p._requestMeta = 1;
10533             }
10534             if(this.sortInfo && this.remoteSort){
10535                 var pn = this.paramNames;
10536                 p[pn["sort"]] = this.sortInfo.field;
10537                 p[pn["dir"]] = this.sortInfo.direction;
10538             }
10539             if (this.multiSort) {
10540                 var pn = this.paramNames;
10541                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10542             }
10543             
10544             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10545         }
10546     },
10547
10548     /**
10549      * Reloads the Record cache from the configured Proxy using the configured Reader and
10550      * the options from the last load operation performed.
10551      * @param {Object} options (optional) An object containing properties which may override the options
10552      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10553      * the most recently used options are reused).
10554      */
10555     reload : function(options){
10556         this.load(Roo.applyIf(options||{}, this.lastOptions));
10557     },
10558
10559     // private
10560     // Called as a callback by the Reader during a load operation.
10561     loadRecords : function(o, options, success){
10562         if(!o || success === false){
10563             if(success !== false){
10564                 this.fireEvent("load", this, [], options, o);
10565             }
10566             if(options.callback){
10567                 options.callback.call(options.scope || this, [], options, false);
10568             }
10569             return;
10570         }
10571         // if data returned failure - throw an exception.
10572         if (o.success === false) {
10573             // show a message if no listener is registered.
10574             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10575                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10576             }
10577             // loadmask wil be hooked into this..
10578             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10579             return;
10580         }
10581         var r = o.records, t = o.totalRecords || r.length;
10582         
10583         this.fireEvent("beforeloadadd", this, r, options, o);
10584         
10585         if(!options || options.add !== true){
10586             if(this.pruneModifiedRecords){
10587                 this.modified = [];
10588             }
10589             for(var i = 0, len = r.length; i < len; i++){
10590                 r[i].join(this);
10591             }
10592             if(this.snapshot){
10593                 this.data = this.snapshot;
10594                 delete this.snapshot;
10595             }
10596             this.data.clear();
10597             this.data.addAll(r);
10598             this.totalLength = t;
10599             this.applySort();
10600             this.fireEvent("datachanged", this);
10601         }else{
10602             this.totalLength = Math.max(t, this.data.length+r.length);
10603             this.add(r);
10604         }
10605         this.fireEvent("load", this, r, options, o);
10606         if(options.callback){
10607             options.callback.call(options.scope || this, r, options, true);
10608         }
10609     },
10610
10611
10612     /**
10613      * Loads data from a passed data block. A Reader which understands the format of the data
10614      * must have been configured in the constructor.
10615      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10616      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10617      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10618      */
10619     loadData : function(o, append){
10620         var r = this.reader.readRecords(o);
10621         this.loadRecords(r, {add: append}, true);
10622     },
10623
10624     /**
10625      * Gets the number of cached records.
10626      * <p>
10627      * <em>If using paging, this may not be the total size of the dataset. If the data object
10628      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10629      * the data set size</em>
10630      */
10631     getCount : function(){
10632         return this.data.length || 0;
10633     },
10634
10635     /**
10636      * Gets the total number of records in the dataset as returned by the server.
10637      * <p>
10638      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10639      * the dataset size</em>
10640      */
10641     getTotalCount : function(){
10642         return this.totalLength || 0;
10643     },
10644
10645     /**
10646      * Returns the sort state of the Store as an object with two properties:
10647      * <pre><code>
10648  field {String} The name of the field by which the Records are sorted
10649  direction {String} The sort order, "ASC" or "DESC"
10650      * </code></pre>
10651      */
10652     getSortState : function(){
10653         return this.sortInfo;
10654     },
10655
10656     // private
10657     applySort : function(){
10658         if(this.sortInfo && !this.remoteSort){
10659             var s = this.sortInfo, f = s.field;
10660             var st = this.fields.get(f).sortType;
10661             var fn = function(r1, r2){
10662                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10663                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10664             };
10665             this.data.sort(s.direction, fn);
10666             if(this.snapshot && this.snapshot != this.data){
10667                 this.snapshot.sort(s.direction, fn);
10668             }
10669         }
10670     },
10671
10672     /**
10673      * Sets the default sort column and order to be used by the next load operation.
10674      * @param {String} fieldName The name of the field to sort by.
10675      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10676      */
10677     setDefaultSort : function(field, dir){
10678         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10679     },
10680
10681     /**
10682      * Sort the Records.
10683      * If remote sorting is used, the sort is performed on the server, and the cache is
10684      * reloaded. If local sorting is used, the cache is sorted internally.
10685      * @param {String} fieldName The name of the field to sort by.
10686      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10687      */
10688     sort : function(fieldName, dir){
10689         var f = this.fields.get(fieldName);
10690         if(!dir){
10691             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10692             
10693             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10694                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10695             }else{
10696                 dir = f.sortDir;
10697             }
10698         }
10699         this.sortToggle[f.name] = dir;
10700         this.sortInfo = {field: f.name, direction: dir};
10701         if(!this.remoteSort){
10702             this.applySort();
10703             this.fireEvent("datachanged", this);
10704         }else{
10705             this.load(this.lastOptions);
10706         }
10707     },
10708
10709     /**
10710      * Calls the specified function for each of the Records in the cache.
10711      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10712      * Returning <em>false</em> aborts and exits the iteration.
10713      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10714      */
10715     each : function(fn, scope){
10716         this.data.each(fn, scope);
10717     },
10718
10719     /**
10720      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10721      * (e.g., during paging).
10722      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10723      */
10724     getModifiedRecords : function(){
10725         return this.modified;
10726     },
10727
10728     // private
10729     createFilterFn : function(property, value, anyMatch){
10730         if(!value.exec){ // not a regex
10731             value = String(value);
10732             if(value.length == 0){
10733                 return false;
10734             }
10735             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10736         }
10737         return function(r){
10738             return value.test(r.data[property]);
10739         };
10740     },
10741
10742     /**
10743      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10744      * @param {String} property A field on your records
10745      * @param {Number} start The record index to start at (defaults to 0)
10746      * @param {Number} end The last record index to include (defaults to length - 1)
10747      * @return {Number} The sum
10748      */
10749     sum : function(property, start, end){
10750         var rs = this.data.items, v = 0;
10751         start = start || 0;
10752         end = (end || end === 0) ? end : rs.length-1;
10753
10754         for(var i = start; i <= end; i++){
10755             v += (rs[i].data[property] || 0);
10756         }
10757         return v;
10758     },
10759
10760     /**
10761      * Filter the records by a specified property.
10762      * @param {String} field A field on your records
10763      * @param {String/RegExp} value Either a string that the field
10764      * should start with or a RegExp to test against the field
10765      * @param {Boolean} anyMatch True to match any part not just the beginning
10766      */
10767     filter : function(property, value, anyMatch){
10768         var fn = this.createFilterFn(property, value, anyMatch);
10769         return fn ? this.filterBy(fn) : this.clearFilter();
10770     },
10771
10772     /**
10773      * Filter by a function. The specified function will be called with each
10774      * record in this data source. If the function returns true the record is included,
10775      * otherwise it is filtered.
10776      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10777      * @param {Object} scope (optional) The scope of the function (defaults to this)
10778      */
10779     filterBy : function(fn, scope){
10780         this.snapshot = this.snapshot || this.data;
10781         this.data = this.queryBy(fn, scope||this);
10782         this.fireEvent("datachanged", this);
10783     },
10784
10785     /**
10786      * Query the records by a specified property.
10787      * @param {String} field A field on your records
10788      * @param {String/RegExp} value Either a string that the field
10789      * should start with or a RegExp to test against the field
10790      * @param {Boolean} anyMatch True to match any part not just the beginning
10791      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10792      */
10793     query : function(property, value, anyMatch){
10794         var fn = this.createFilterFn(property, value, anyMatch);
10795         return fn ? this.queryBy(fn) : this.data.clone();
10796     },
10797
10798     /**
10799      * Query by a function. The specified function will be called with each
10800      * record in this data source. If the function returns true the record is included
10801      * in the results.
10802      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10803      * @param {Object} scope (optional) The scope of the function (defaults to this)
10804       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10805      **/
10806     queryBy : function(fn, scope){
10807         var data = this.snapshot || this.data;
10808         return data.filterBy(fn, scope||this);
10809     },
10810
10811     /**
10812      * Collects unique values for a particular dataIndex from this store.
10813      * @param {String} dataIndex The property to collect
10814      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10815      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10816      * @return {Array} An array of the unique values
10817      **/
10818     collect : function(dataIndex, allowNull, bypassFilter){
10819         var d = (bypassFilter === true && this.snapshot) ?
10820                 this.snapshot.items : this.data.items;
10821         var v, sv, r = [], l = {};
10822         for(var i = 0, len = d.length; i < len; i++){
10823             v = d[i].data[dataIndex];
10824             sv = String(v);
10825             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10826                 l[sv] = true;
10827                 r[r.length] = v;
10828             }
10829         }
10830         return r;
10831     },
10832
10833     /**
10834      * Revert to a view of the Record cache with no filtering applied.
10835      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10836      */
10837     clearFilter : function(suppressEvent){
10838         if(this.snapshot && this.snapshot != this.data){
10839             this.data = this.snapshot;
10840             delete this.snapshot;
10841             if(suppressEvent !== true){
10842                 this.fireEvent("datachanged", this);
10843             }
10844         }
10845     },
10846
10847     // private
10848     afterEdit : function(record){
10849         if(this.modified.indexOf(record) == -1){
10850             this.modified.push(record);
10851         }
10852         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10853     },
10854     
10855     // private
10856     afterReject : function(record){
10857         this.modified.remove(record);
10858         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10859     },
10860
10861     // private
10862     afterCommit : function(record){
10863         this.modified.remove(record);
10864         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10865     },
10866
10867     /**
10868      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10869      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10870      */
10871     commitChanges : function(){
10872         var m = this.modified.slice(0);
10873         this.modified = [];
10874         for(var i = 0, len = m.length; i < len; i++){
10875             m[i].commit();
10876         }
10877     },
10878
10879     /**
10880      * Cancel outstanding changes on all changed records.
10881      */
10882     rejectChanges : function(){
10883         var m = this.modified.slice(0);
10884         this.modified = [];
10885         for(var i = 0, len = m.length; i < len; i++){
10886             m[i].reject();
10887         }
10888     },
10889
10890     onMetaChange : function(meta, rtype, o){
10891         this.recordType = rtype;
10892         this.fields = rtype.prototype.fields;
10893         delete this.snapshot;
10894         this.sortInfo = meta.sortInfo || this.sortInfo;
10895         this.modified = [];
10896         this.fireEvent('metachange', this, this.reader.meta);
10897     },
10898     
10899     moveIndex : function(data, type)
10900     {
10901         var index = this.indexOf(data);
10902         
10903         var newIndex = index + type;
10904         
10905         this.remove(data);
10906         
10907         this.insert(newIndex, data);
10908         
10909     }
10910 });/*
10911  * Based on:
10912  * Ext JS Library 1.1.1
10913  * Copyright(c) 2006-2007, Ext JS, LLC.
10914  *
10915  * Originally Released Under LGPL - original licence link has changed is not relivant.
10916  *
10917  * Fork - LGPL
10918  * <script type="text/javascript">
10919  */
10920
10921 /**
10922  * @class Roo.data.SimpleStore
10923  * @extends Roo.data.Store
10924  * Small helper class to make creating Stores from Array data easier.
10925  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10926  * @cfg {Array} fields An array of field definition objects, or field name strings.
10927  * @cfg {Array} data The multi-dimensional array of data
10928  * @constructor
10929  * @param {Object} config
10930  */
10931 Roo.data.SimpleStore = function(config){
10932     Roo.data.SimpleStore.superclass.constructor.call(this, {
10933         isLocal : true,
10934         reader: new Roo.data.ArrayReader({
10935                 id: config.id
10936             },
10937             Roo.data.Record.create(config.fields)
10938         ),
10939         proxy : new Roo.data.MemoryProxy(config.data)
10940     });
10941     this.load();
10942 };
10943 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10944  * Based on:
10945  * Ext JS Library 1.1.1
10946  * Copyright(c) 2006-2007, Ext JS, LLC.
10947  *
10948  * Originally Released Under LGPL - original licence link has changed is not relivant.
10949  *
10950  * Fork - LGPL
10951  * <script type="text/javascript">
10952  */
10953
10954 /**
10955 /**
10956  * @extends Roo.data.Store
10957  * @class Roo.data.JsonStore
10958  * Small helper class to make creating Stores for JSON data easier. <br/>
10959 <pre><code>
10960 var store = new Roo.data.JsonStore({
10961     url: 'get-images.php',
10962     root: 'images',
10963     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10964 });
10965 </code></pre>
10966  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10967  * JsonReader and HttpProxy (unless inline data is provided).</b>
10968  * @cfg {Array} fields An array of field definition objects, or field name strings.
10969  * @constructor
10970  * @param {Object} config
10971  */
10972 Roo.data.JsonStore = function(c){
10973     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10974         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10975         reader: new Roo.data.JsonReader(c, c.fields)
10976     }));
10977 };
10978 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10979  * Based on:
10980  * Ext JS Library 1.1.1
10981  * Copyright(c) 2006-2007, Ext JS, LLC.
10982  *
10983  * Originally Released Under LGPL - original licence link has changed is not relivant.
10984  *
10985  * Fork - LGPL
10986  * <script type="text/javascript">
10987  */
10988
10989  
10990 Roo.data.Field = function(config){
10991     if(typeof config == "string"){
10992         config = {name: config};
10993     }
10994     Roo.apply(this, config);
10995     
10996     if(!this.type){
10997         this.type = "auto";
10998     }
10999     
11000     var st = Roo.data.SortTypes;
11001     // named sortTypes are supported, here we look them up
11002     if(typeof this.sortType == "string"){
11003         this.sortType = st[this.sortType];
11004     }
11005     
11006     // set default sortType for strings and dates
11007     if(!this.sortType){
11008         switch(this.type){
11009             case "string":
11010                 this.sortType = st.asUCString;
11011                 break;
11012             case "date":
11013                 this.sortType = st.asDate;
11014                 break;
11015             default:
11016                 this.sortType = st.none;
11017         }
11018     }
11019
11020     // define once
11021     var stripRe = /[\$,%]/g;
11022
11023     // prebuilt conversion function for this field, instead of
11024     // switching every time we're reading a value
11025     if(!this.convert){
11026         var cv, dateFormat = this.dateFormat;
11027         switch(this.type){
11028             case "":
11029             case "auto":
11030             case undefined:
11031                 cv = function(v){ return v; };
11032                 break;
11033             case "string":
11034                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11035                 break;
11036             case "int":
11037                 cv = function(v){
11038                     return v !== undefined && v !== null && v !== '' ?
11039                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11040                     };
11041                 break;
11042             case "float":
11043                 cv = function(v){
11044                     return v !== undefined && v !== null && v !== '' ?
11045                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11046                     };
11047                 break;
11048             case "bool":
11049             case "boolean":
11050                 cv = function(v){ return v === true || v === "true" || v == 1; };
11051                 break;
11052             case "date":
11053                 cv = function(v){
11054                     if(!v){
11055                         return '';
11056                     }
11057                     if(v instanceof Date){
11058                         return v;
11059                     }
11060                     if(dateFormat){
11061                         if(dateFormat == "timestamp"){
11062                             return new Date(v*1000);
11063                         }
11064                         return Date.parseDate(v, dateFormat);
11065                     }
11066                     var parsed = Date.parse(v);
11067                     return parsed ? new Date(parsed) : null;
11068                 };
11069              break;
11070             
11071         }
11072         this.convert = cv;
11073     }
11074 };
11075
11076 Roo.data.Field.prototype = {
11077     dateFormat: null,
11078     defaultValue: "",
11079     mapping: null,
11080     sortType : null,
11081     sortDir : "ASC"
11082 };/*
11083  * Based on:
11084  * Ext JS Library 1.1.1
11085  * Copyright(c) 2006-2007, Ext JS, LLC.
11086  *
11087  * Originally Released Under LGPL - original licence link has changed is not relivant.
11088  *
11089  * Fork - LGPL
11090  * <script type="text/javascript">
11091  */
11092  
11093 // Base class for reading structured data from a data source.  This class is intended to be
11094 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11095
11096 /**
11097  * @class Roo.data.DataReader
11098  * Base class for reading structured data from a data source.  This class is intended to be
11099  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11100  */
11101
11102 Roo.data.DataReader = function(meta, recordType){
11103     
11104     this.meta = meta;
11105     
11106     this.recordType = recordType instanceof Array ? 
11107         Roo.data.Record.create(recordType) : recordType;
11108 };
11109
11110 Roo.data.DataReader.prototype = {
11111      /**
11112      * Create an empty record
11113      * @param {Object} data (optional) - overlay some values
11114      * @return {Roo.data.Record} record created.
11115      */
11116     newRow :  function(d) {
11117         var da =  {};
11118         this.recordType.prototype.fields.each(function(c) {
11119             switch( c.type) {
11120                 case 'int' : da[c.name] = 0; break;
11121                 case 'date' : da[c.name] = new Date(); break;
11122                 case 'float' : da[c.name] = 0.0; break;
11123                 case 'boolean' : da[c.name] = false; break;
11124                 default : da[c.name] = ""; break;
11125             }
11126             
11127         });
11128         return new this.recordType(Roo.apply(da, d));
11129     }
11130     
11131 };/*
11132  * Based on:
11133  * Ext JS Library 1.1.1
11134  * Copyright(c) 2006-2007, Ext JS, LLC.
11135  *
11136  * Originally Released Under LGPL - original licence link has changed is not relivant.
11137  *
11138  * Fork - LGPL
11139  * <script type="text/javascript">
11140  */
11141
11142 /**
11143  * @class Roo.data.DataProxy
11144  * @extends Roo.data.Observable
11145  * This class is an abstract base class for implementations which provide retrieval of
11146  * unformatted data objects.<br>
11147  * <p>
11148  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11149  * (of the appropriate type which knows how to parse the data object) to provide a block of
11150  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11151  * <p>
11152  * Custom implementations must implement the load method as described in
11153  * {@link Roo.data.HttpProxy#load}.
11154  */
11155 Roo.data.DataProxy = function(){
11156     this.addEvents({
11157         /**
11158          * @event beforeload
11159          * Fires before a network request is made to retrieve a data object.
11160          * @param {Object} This DataProxy object.
11161          * @param {Object} params The params parameter to the load function.
11162          */
11163         beforeload : true,
11164         /**
11165          * @event load
11166          * Fires before the load method's callback is called.
11167          * @param {Object} This DataProxy object.
11168          * @param {Object} o The data object.
11169          * @param {Object} arg The callback argument object passed to the load function.
11170          */
11171         load : true,
11172         /**
11173          * @event loadexception
11174          * Fires if an Exception occurs during data retrieval.
11175          * @param {Object} This DataProxy object.
11176          * @param {Object} o The data object.
11177          * @param {Object} arg The callback argument object passed to the load function.
11178          * @param {Object} e The Exception.
11179          */
11180         loadexception : true
11181     });
11182     Roo.data.DataProxy.superclass.constructor.call(this);
11183 };
11184
11185 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11186
11187     /**
11188      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11189      */
11190 /*
11191  * Based on:
11192  * Ext JS Library 1.1.1
11193  * Copyright(c) 2006-2007, Ext JS, LLC.
11194  *
11195  * Originally Released Under LGPL - original licence link has changed is not relivant.
11196  *
11197  * Fork - LGPL
11198  * <script type="text/javascript">
11199  */
11200 /**
11201  * @class Roo.data.MemoryProxy
11202  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11203  * to the Reader when its load method is called.
11204  * @constructor
11205  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11206  */
11207 Roo.data.MemoryProxy = function(data){
11208     if (data.data) {
11209         data = data.data;
11210     }
11211     Roo.data.MemoryProxy.superclass.constructor.call(this);
11212     this.data = data;
11213 };
11214
11215 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11216     
11217     /**
11218      * Load data from the requested source (in this case an in-memory
11219      * data object passed to the constructor), read the data object into
11220      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11221      * process that block using the passed callback.
11222      * @param {Object} params This parameter is not used by the MemoryProxy class.
11223      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11224      * object into a block of Roo.data.Records.
11225      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11226      * The function must be passed <ul>
11227      * <li>The Record block object</li>
11228      * <li>The "arg" argument from the load function</li>
11229      * <li>A boolean success indicator</li>
11230      * </ul>
11231      * @param {Object} scope The scope in which to call the callback
11232      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11233      */
11234     load : function(params, reader, callback, scope, arg){
11235         params = params || {};
11236         var result;
11237         try {
11238             result = reader.readRecords(this.data);
11239         }catch(e){
11240             this.fireEvent("loadexception", this, arg, null, e);
11241             callback.call(scope, null, arg, false);
11242             return;
11243         }
11244         callback.call(scope, result, arg, true);
11245     },
11246     
11247     // private
11248     update : function(params, records){
11249         
11250     }
11251 });/*
11252  * Based on:
11253  * Ext JS Library 1.1.1
11254  * Copyright(c) 2006-2007, Ext JS, LLC.
11255  *
11256  * Originally Released Under LGPL - original licence link has changed is not relivant.
11257  *
11258  * Fork - LGPL
11259  * <script type="text/javascript">
11260  */
11261 /**
11262  * @class Roo.data.HttpProxy
11263  * @extends Roo.data.DataProxy
11264  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11265  * configured to reference a certain URL.<br><br>
11266  * <p>
11267  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11268  * from which the running page was served.<br><br>
11269  * <p>
11270  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11271  * <p>
11272  * Be aware that to enable the browser to parse an XML document, the server must set
11273  * the Content-Type header in the HTTP response to "text/xml".
11274  * @constructor
11275  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11276  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11277  * will be used to make the request.
11278  */
11279 Roo.data.HttpProxy = function(conn){
11280     Roo.data.HttpProxy.superclass.constructor.call(this);
11281     // is conn a conn config or a real conn?
11282     this.conn = conn;
11283     this.useAjax = !conn || !conn.events;
11284   
11285 };
11286
11287 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11288     // thse are take from connection...
11289     
11290     /**
11291      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11292      */
11293     /**
11294      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11295      * extra parameters to each request made by this object. (defaults to undefined)
11296      */
11297     /**
11298      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11299      *  to each request made by this object. (defaults to undefined)
11300      */
11301     /**
11302      * @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)
11303      */
11304     /**
11305      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11306      */
11307      /**
11308      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11309      * @type Boolean
11310      */
11311   
11312
11313     /**
11314      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11315      * @type Boolean
11316      */
11317     /**
11318      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11319      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11320      * a finer-grained basis than the DataProxy events.
11321      */
11322     getConnection : function(){
11323         return this.useAjax ? Roo.Ajax : this.conn;
11324     },
11325
11326     /**
11327      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11328      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11329      * process that block using the passed callback.
11330      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11331      * for the request to the remote server.
11332      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11333      * object into a block of Roo.data.Records.
11334      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11335      * The function must be passed <ul>
11336      * <li>The Record block object</li>
11337      * <li>The "arg" argument from the load function</li>
11338      * <li>A boolean success indicator</li>
11339      * </ul>
11340      * @param {Object} scope The scope in which to call the callback
11341      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11342      */
11343     load : function(params, reader, callback, scope, arg){
11344         if(this.fireEvent("beforeload", this, params) !== false){
11345             var  o = {
11346                 params : params || {},
11347                 request: {
11348                     callback : callback,
11349                     scope : scope,
11350                     arg : arg
11351                 },
11352                 reader: reader,
11353                 callback : this.loadResponse,
11354                 scope: this
11355             };
11356             if(this.useAjax){
11357                 Roo.applyIf(o, this.conn);
11358                 if(this.activeRequest){
11359                     Roo.Ajax.abort(this.activeRequest);
11360                 }
11361                 this.activeRequest = Roo.Ajax.request(o);
11362             }else{
11363                 this.conn.request(o);
11364             }
11365         }else{
11366             callback.call(scope||this, null, arg, false);
11367         }
11368     },
11369
11370     // private
11371     loadResponse : function(o, success, response){
11372         delete this.activeRequest;
11373         if(!success){
11374             this.fireEvent("loadexception", this, o, response);
11375             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11376             return;
11377         }
11378         var result;
11379         try {
11380             result = o.reader.read(response);
11381         }catch(e){
11382             this.fireEvent("loadexception", this, o, response, e);
11383             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11384             return;
11385         }
11386         
11387         this.fireEvent("load", this, o, o.request.arg);
11388         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11389     },
11390
11391     // private
11392     update : function(dataSet){
11393
11394     },
11395
11396     // private
11397     updateResponse : function(dataSet){
11398
11399     }
11400 });/*
11401  * Based on:
11402  * Ext JS Library 1.1.1
11403  * Copyright(c) 2006-2007, Ext JS, LLC.
11404  *
11405  * Originally Released Under LGPL - original licence link has changed is not relivant.
11406  *
11407  * Fork - LGPL
11408  * <script type="text/javascript">
11409  */
11410
11411 /**
11412  * @class Roo.data.ScriptTagProxy
11413  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11414  * other than the originating domain of the running page.<br><br>
11415  * <p>
11416  * <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
11417  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11418  * <p>
11419  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11420  * source code that is used as the source inside a &lt;script> tag.<br><br>
11421  * <p>
11422  * In order for the browser to process the returned data, the server must wrap the data object
11423  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11424  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11425  * depending on whether the callback name was passed:
11426  * <p>
11427  * <pre><code>
11428 boolean scriptTag = false;
11429 String cb = request.getParameter("callback");
11430 if (cb != null) {
11431     scriptTag = true;
11432     response.setContentType("text/javascript");
11433 } else {
11434     response.setContentType("application/x-json");
11435 }
11436 Writer out = response.getWriter();
11437 if (scriptTag) {
11438     out.write(cb + "(");
11439 }
11440 out.print(dataBlock.toJsonString());
11441 if (scriptTag) {
11442     out.write(");");
11443 }
11444 </pre></code>
11445  *
11446  * @constructor
11447  * @param {Object} config A configuration object.
11448  */
11449 Roo.data.ScriptTagProxy = function(config){
11450     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11451     Roo.apply(this, config);
11452     this.head = document.getElementsByTagName("head")[0];
11453 };
11454
11455 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11456
11457 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11458     /**
11459      * @cfg {String} url The URL from which to request the data object.
11460      */
11461     /**
11462      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11463      */
11464     timeout : 30000,
11465     /**
11466      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11467      * the server the name of the callback function set up by the load call to process the returned data object.
11468      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11469      * javascript output which calls this named function passing the data object as its only parameter.
11470      */
11471     callbackParam : "callback",
11472     /**
11473      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11474      * name to the request.
11475      */
11476     nocache : true,
11477
11478     /**
11479      * Load data from the configured URL, read the data object into
11480      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11481      * process that block using the passed callback.
11482      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11483      * for the request to the remote server.
11484      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11485      * object into a block of Roo.data.Records.
11486      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11487      * The function must be passed <ul>
11488      * <li>The Record block object</li>
11489      * <li>The "arg" argument from the load function</li>
11490      * <li>A boolean success indicator</li>
11491      * </ul>
11492      * @param {Object} scope The scope in which to call the callback
11493      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11494      */
11495     load : function(params, reader, callback, scope, arg){
11496         if(this.fireEvent("beforeload", this, params) !== false){
11497
11498             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11499
11500             var url = this.url;
11501             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11502             if(this.nocache){
11503                 url += "&_dc=" + (new Date().getTime());
11504             }
11505             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11506             var trans = {
11507                 id : transId,
11508                 cb : "stcCallback"+transId,
11509                 scriptId : "stcScript"+transId,
11510                 params : params,
11511                 arg : arg,
11512                 url : url,
11513                 callback : callback,
11514                 scope : scope,
11515                 reader : reader
11516             };
11517             var conn = this;
11518
11519             window[trans.cb] = function(o){
11520                 conn.handleResponse(o, trans);
11521             };
11522
11523             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11524
11525             if(this.autoAbort !== false){
11526                 this.abort();
11527             }
11528
11529             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11530
11531             var script = document.createElement("script");
11532             script.setAttribute("src", url);
11533             script.setAttribute("type", "text/javascript");
11534             script.setAttribute("id", trans.scriptId);
11535             this.head.appendChild(script);
11536
11537             this.trans = trans;
11538         }else{
11539             callback.call(scope||this, null, arg, false);
11540         }
11541     },
11542
11543     // private
11544     isLoading : function(){
11545         return this.trans ? true : false;
11546     },
11547
11548     /**
11549      * Abort the current server request.
11550      */
11551     abort : function(){
11552         if(this.isLoading()){
11553             this.destroyTrans(this.trans);
11554         }
11555     },
11556
11557     // private
11558     destroyTrans : function(trans, isLoaded){
11559         this.head.removeChild(document.getElementById(trans.scriptId));
11560         clearTimeout(trans.timeoutId);
11561         if(isLoaded){
11562             window[trans.cb] = undefined;
11563             try{
11564                 delete window[trans.cb];
11565             }catch(e){}
11566         }else{
11567             // if hasn't been loaded, wait for load to remove it to prevent script error
11568             window[trans.cb] = function(){
11569                 window[trans.cb] = undefined;
11570                 try{
11571                     delete window[trans.cb];
11572                 }catch(e){}
11573             };
11574         }
11575     },
11576
11577     // private
11578     handleResponse : function(o, trans){
11579         this.trans = false;
11580         this.destroyTrans(trans, true);
11581         var result;
11582         try {
11583             result = trans.reader.readRecords(o);
11584         }catch(e){
11585             this.fireEvent("loadexception", this, o, trans.arg, e);
11586             trans.callback.call(trans.scope||window, null, trans.arg, false);
11587             return;
11588         }
11589         this.fireEvent("load", this, o, trans.arg);
11590         trans.callback.call(trans.scope||window, result, trans.arg, true);
11591     },
11592
11593     // private
11594     handleFailure : function(trans){
11595         this.trans = false;
11596         this.destroyTrans(trans, false);
11597         this.fireEvent("loadexception", this, null, trans.arg);
11598         trans.callback.call(trans.scope||window, null, trans.arg, false);
11599     }
11600 });/*
11601  * Based on:
11602  * Ext JS Library 1.1.1
11603  * Copyright(c) 2006-2007, Ext JS, LLC.
11604  *
11605  * Originally Released Under LGPL - original licence link has changed is not relivant.
11606  *
11607  * Fork - LGPL
11608  * <script type="text/javascript">
11609  */
11610
11611 /**
11612  * @class Roo.data.JsonReader
11613  * @extends Roo.data.DataReader
11614  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11615  * based on mappings in a provided Roo.data.Record constructor.
11616  * 
11617  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11618  * in the reply previously. 
11619  * 
11620  * <p>
11621  * Example code:
11622  * <pre><code>
11623 var RecordDef = Roo.data.Record.create([
11624     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11625     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11626 ]);
11627 var myReader = new Roo.data.JsonReader({
11628     totalProperty: "results",    // The property which contains the total dataset size (optional)
11629     root: "rows",                // The property which contains an Array of row objects
11630     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11631 }, RecordDef);
11632 </code></pre>
11633  * <p>
11634  * This would consume a JSON file like this:
11635  * <pre><code>
11636 { 'results': 2, 'rows': [
11637     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11638     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11639 }
11640 </code></pre>
11641  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11642  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11643  * paged from the remote server.
11644  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11645  * @cfg {String} root name of the property which contains the Array of row objects.
11646  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11647  * @cfg {Array} fields Array of field definition objects
11648  * @constructor
11649  * Create a new JsonReader
11650  * @param {Object} meta Metadata configuration options
11651  * @param {Object} recordType Either an Array of field definition objects,
11652  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11653  */
11654 Roo.data.JsonReader = function(meta, recordType){
11655     
11656     meta = meta || {};
11657     // set some defaults:
11658     Roo.applyIf(meta, {
11659         totalProperty: 'total',
11660         successProperty : 'success',
11661         root : 'data',
11662         id : 'id'
11663     });
11664     
11665     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11666 };
11667 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11668     
11669     /**
11670      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11671      * Used by Store query builder to append _requestMeta to params.
11672      * 
11673      */
11674     metaFromRemote : false,
11675     /**
11676      * This method is only used by a DataProxy which has retrieved data from a remote server.
11677      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11678      * @return {Object} data A data block which is used by an Roo.data.Store object as
11679      * a cache of Roo.data.Records.
11680      */
11681     read : function(response){
11682         var json = response.responseText;
11683        
11684         var o = /* eval:var:o */ eval("("+json+")");
11685         if(!o) {
11686             throw {message: "JsonReader.read: Json object not found"};
11687         }
11688         
11689         if(o.metaData){
11690             
11691             delete this.ef;
11692             this.metaFromRemote = true;
11693             this.meta = o.metaData;
11694             this.recordType = Roo.data.Record.create(o.metaData.fields);
11695             this.onMetaChange(this.meta, this.recordType, o);
11696         }
11697         return this.readRecords(o);
11698     },
11699
11700     // private function a store will implement
11701     onMetaChange : function(meta, recordType, o){
11702
11703     },
11704
11705     /**
11706          * @ignore
11707          */
11708     simpleAccess: function(obj, subsc) {
11709         return obj[subsc];
11710     },
11711
11712         /**
11713          * @ignore
11714          */
11715     getJsonAccessor: function(){
11716         var re = /[\[\.]/;
11717         return function(expr) {
11718             try {
11719                 return(re.test(expr))
11720                     ? new Function("obj", "return obj." + expr)
11721                     : function(obj){
11722                         return obj[expr];
11723                     };
11724             } catch(e){}
11725             return Roo.emptyFn;
11726         };
11727     }(),
11728
11729     /**
11730      * Create a data block containing Roo.data.Records from an XML document.
11731      * @param {Object} o An object which contains an Array of row objects in the property specified
11732      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11733      * which contains the total size of the dataset.
11734      * @return {Object} data A data block which is used by an Roo.data.Store object as
11735      * a cache of Roo.data.Records.
11736      */
11737     readRecords : function(o){
11738         /**
11739          * After any data loads, the raw JSON data is available for further custom processing.
11740          * @type Object
11741          */
11742         this.o = o;
11743         var s = this.meta, Record = this.recordType,
11744             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11745
11746 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11747         if (!this.ef) {
11748             if(s.totalProperty) {
11749                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11750                 }
11751                 if(s.successProperty) {
11752                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11753                 }
11754                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11755                 if (s.id) {
11756                         var g = this.getJsonAccessor(s.id);
11757                         this.getId = function(rec) {
11758                                 var r = g(rec);  
11759                                 return (r === undefined || r === "") ? null : r;
11760                         };
11761                 } else {
11762                         this.getId = function(){return null;};
11763                 }
11764             this.ef = [];
11765             for(var jj = 0; jj < fl; jj++){
11766                 f = fi[jj];
11767                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11768                 this.ef[jj] = this.getJsonAccessor(map);
11769             }
11770         }
11771
11772         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11773         if(s.totalProperty){
11774             var vt = parseInt(this.getTotal(o), 10);
11775             if(!isNaN(vt)){
11776                 totalRecords = vt;
11777             }
11778         }
11779         if(s.successProperty){
11780             var vs = this.getSuccess(o);
11781             if(vs === false || vs === 'false'){
11782                 success = false;
11783             }
11784         }
11785         var records = [];
11786         for(var i = 0; i < c; i++){
11787                 var n = root[i];
11788             var values = {};
11789             var id = this.getId(n);
11790             for(var j = 0; j < fl; j++){
11791                 f = fi[j];
11792             var v = this.ef[j](n);
11793             if (!f.convert) {
11794                 Roo.log('missing convert for ' + f.name);
11795                 Roo.log(f);
11796                 continue;
11797             }
11798             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11799             }
11800             var record = new Record(values, id);
11801             record.json = n;
11802             records[i] = record;
11803         }
11804         return {
11805             raw : o,
11806             success : success,
11807             records : records,
11808             totalRecords : totalRecords
11809         };
11810     }
11811 });/*
11812  * Based on:
11813  * Ext JS Library 1.1.1
11814  * Copyright(c) 2006-2007, Ext JS, LLC.
11815  *
11816  * Originally Released Under LGPL - original licence link has changed is not relivant.
11817  *
11818  * Fork - LGPL
11819  * <script type="text/javascript">
11820  */
11821
11822 /**
11823  * @class Roo.data.ArrayReader
11824  * @extends Roo.data.DataReader
11825  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11826  * Each element of that Array represents a row of data fields. The
11827  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11828  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11829  * <p>
11830  * Example code:.
11831  * <pre><code>
11832 var RecordDef = Roo.data.Record.create([
11833     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11834     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11835 ]);
11836 var myReader = new Roo.data.ArrayReader({
11837     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11838 }, RecordDef);
11839 </code></pre>
11840  * <p>
11841  * This would consume an Array like this:
11842  * <pre><code>
11843 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11844   </code></pre>
11845  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11846  * @constructor
11847  * Create a new JsonReader
11848  * @param {Object} meta Metadata configuration options.
11849  * @param {Object} recordType Either an Array of field definition objects
11850  * as specified to {@link Roo.data.Record#create},
11851  * or an {@link Roo.data.Record} object
11852  * created using {@link Roo.data.Record#create}.
11853  */
11854 Roo.data.ArrayReader = function(meta, recordType){
11855     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11856 };
11857
11858 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11859     /**
11860      * Create a data block containing Roo.data.Records from an XML document.
11861      * @param {Object} o An Array of row objects which represents the dataset.
11862      * @return {Object} data A data block which is used by an Roo.data.Store object as
11863      * a cache of Roo.data.Records.
11864      */
11865     readRecords : function(o){
11866         var sid = this.meta ? this.meta.id : null;
11867         var recordType = this.recordType, fields = recordType.prototype.fields;
11868         var records = [];
11869         var root = o;
11870             for(var i = 0; i < root.length; i++){
11871                     var n = root[i];
11872                 var values = {};
11873                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11874                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11875                 var f = fields.items[j];
11876                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11877                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11878                 v = f.convert(v);
11879                 values[f.name] = v;
11880             }
11881                 var record = new recordType(values, id);
11882                 record.json = n;
11883                 records[records.length] = record;
11884             }
11885             return {
11886                 records : records,
11887                 totalRecords : records.length
11888             };
11889     }
11890 });/*
11891  * - LGPL
11892  * * 
11893  */
11894
11895 /**
11896  * @class Roo.bootstrap.ComboBox
11897  * @extends Roo.bootstrap.TriggerField
11898  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11899  * @cfg {Boolean} append (true|false) default false
11900  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11901  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11902  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11903  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11904  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11905  * @cfg {Boolean} animate default true
11906  * @cfg {Boolean} emptyResultText only for touch device
11907  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11908  * @constructor
11909  * Create a new ComboBox.
11910  * @param {Object} config Configuration options
11911  */
11912 Roo.bootstrap.ComboBox = function(config){
11913     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11914     this.addEvents({
11915         /**
11916          * @event expand
11917          * Fires when the dropdown list is expanded
11918              * @param {Roo.bootstrap.ComboBox} combo This combo box
11919              */
11920         'expand' : true,
11921         /**
11922          * @event collapse
11923          * Fires when the dropdown list is collapsed
11924              * @param {Roo.bootstrap.ComboBox} combo This combo box
11925              */
11926         'collapse' : true,
11927         /**
11928          * @event beforeselect
11929          * Fires before a list item is selected. Return false to cancel the selection.
11930              * @param {Roo.bootstrap.ComboBox} combo This combo box
11931              * @param {Roo.data.Record} record The data record returned from the underlying store
11932              * @param {Number} index The index of the selected item in the dropdown list
11933              */
11934         'beforeselect' : true,
11935         /**
11936          * @event select
11937          * Fires when a list item is selected
11938              * @param {Roo.bootstrap.ComboBox} combo This combo box
11939              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11940              * @param {Number} index The index of the selected item in the dropdown list
11941              */
11942         'select' : true,
11943         /**
11944          * @event beforequery
11945          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11946          * The event object passed has these properties:
11947              * @param {Roo.bootstrap.ComboBox} combo This combo box
11948              * @param {String} query The query
11949              * @param {Boolean} forceAll true to force "all" query
11950              * @param {Boolean} cancel true to cancel the query
11951              * @param {Object} e The query event object
11952              */
11953         'beforequery': true,
11954          /**
11955          * @event add
11956          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11957              * @param {Roo.bootstrap.ComboBox} combo This combo box
11958              */
11959         'add' : true,
11960         /**
11961          * @event edit
11962          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11963              * @param {Roo.bootstrap.ComboBox} combo This combo box
11964              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11965              */
11966         'edit' : true,
11967         /**
11968          * @event remove
11969          * Fires when the remove value from the combobox array
11970              * @param {Roo.bootstrap.ComboBox} combo This combo box
11971              */
11972         'remove' : true,
11973         /**
11974          * @event afterremove
11975          * Fires when the remove value from the combobox array
11976              * @param {Roo.bootstrap.ComboBox} combo This combo box
11977              */
11978         'afterremove' : true,
11979         /**
11980          * @event specialfilter
11981          * Fires when specialfilter
11982             * @param {Roo.bootstrap.ComboBox} combo This combo box
11983             */
11984         'specialfilter' : true,
11985         /**
11986          * @event tick
11987          * Fires when tick the element
11988             * @param {Roo.bootstrap.ComboBox} combo This combo box
11989             */
11990         'tick' : true,
11991         /**
11992          * @event touchviewdisplay
11993          * Fires when touch view require special display (default is using displayField)
11994             * @param {Roo.bootstrap.ComboBox} combo This combo box
11995             * @param {Object} cfg set html .
11996             */
11997         'touchviewdisplay' : true
11998         
11999     });
12000     
12001     this.item = [];
12002     this.tickItems = [];
12003     
12004     this.selectedIndex = -1;
12005     if(this.mode == 'local'){
12006         if(config.queryDelay === undefined){
12007             this.queryDelay = 10;
12008         }
12009         if(config.minChars === undefined){
12010             this.minChars = 0;
12011         }
12012     }
12013 };
12014
12015 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12016      
12017     /**
12018      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12019      * rendering into an Roo.Editor, defaults to false)
12020      */
12021     /**
12022      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12023      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12024      */
12025     /**
12026      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12027      */
12028     /**
12029      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12030      * the dropdown list (defaults to undefined, with no header element)
12031      */
12032
12033      /**
12034      * @cfg {String/Roo.Template} tpl The template to use to render the output
12035      */
12036      
12037      /**
12038      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12039      */
12040     listWidth: undefined,
12041     /**
12042      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12043      * mode = 'remote' or 'text' if mode = 'local')
12044      */
12045     displayField: undefined,
12046     
12047     /**
12048      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12049      * mode = 'remote' or 'value' if mode = 'local'). 
12050      * Note: use of a valueField requires the user make a selection
12051      * in order for a value to be mapped.
12052      */
12053     valueField: undefined,
12054     /**
12055      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12056      */
12057     modalTitle : '',
12058     
12059     /**
12060      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12061      * field's data value (defaults to the underlying DOM element's name)
12062      */
12063     hiddenName: undefined,
12064     /**
12065      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12066      */
12067     listClass: '',
12068     /**
12069      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12070      */
12071     selectedClass: 'active',
12072     
12073     /**
12074      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12075      */
12076     shadow:'sides',
12077     /**
12078      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12079      * anchor positions (defaults to 'tl-bl')
12080      */
12081     listAlign: 'tl-bl?',
12082     /**
12083      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12084      */
12085     maxHeight: 300,
12086     /**
12087      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12088      * query specified by the allQuery config option (defaults to 'query')
12089      */
12090     triggerAction: 'query',
12091     /**
12092      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12093      * (defaults to 4, does not apply if editable = false)
12094      */
12095     minChars : 4,
12096     /**
12097      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12098      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12099      */
12100     typeAhead: false,
12101     /**
12102      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12103      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12104      */
12105     queryDelay: 500,
12106     /**
12107      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12108      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12109      */
12110     pageSize: 0,
12111     /**
12112      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12113      * when editable = true (defaults to false)
12114      */
12115     selectOnFocus:false,
12116     /**
12117      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12118      */
12119     queryParam: 'query',
12120     /**
12121      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12122      * when mode = 'remote' (defaults to 'Loading...')
12123      */
12124     loadingText: 'Loading...',
12125     /**
12126      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12127      */
12128     resizable: false,
12129     /**
12130      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12131      */
12132     handleHeight : 8,
12133     /**
12134      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12135      * traditional select (defaults to true)
12136      */
12137     editable: true,
12138     /**
12139      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12140      */
12141     allQuery: '',
12142     /**
12143      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12144      */
12145     mode: 'remote',
12146     /**
12147      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12148      * listWidth has a higher value)
12149      */
12150     minListWidth : 70,
12151     /**
12152      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12153      * allow the user to set arbitrary text into the field (defaults to false)
12154      */
12155     forceSelection:false,
12156     /**
12157      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12158      * if typeAhead = true (defaults to 250)
12159      */
12160     typeAheadDelay : 250,
12161     /**
12162      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12163      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12164      */
12165     valueNotFoundText : undefined,
12166     /**
12167      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12168      */
12169     blockFocus : false,
12170     
12171     /**
12172      * @cfg {Boolean} disableClear Disable showing of clear button.
12173      */
12174     disableClear : false,
12175     /**
12176      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12177      */
12178     alwaysQuery : false,
12179     
12180     /**
12181      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12182      */
12183     multiple : false,
12184     
12185     /**
12186      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12187      */
12188     invalidClass : "has-warning",
12189     
12190     /**
12191      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12192      */
12193     validClass : "has-success",
12194     
12195     /**
12196      * @cfg {Boolean} specialFilter (true|false) special filter default false
12197      */
12198     specialFilter : false,
12199     
12200     /**
12201      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12202      */
12203     mobileTouchView : true,
12204     
12205     //private
12206     addicon : false,
12207     editicon: false,
12208     
12209     page: 0,
12210     hasQuery: false,
12211     append: false,
12212     loadNext: false,
12213     autoFocus : true,
12214     tickable : false,
12215     btnPosition : 'right',
12216     triggerList : true,
12217     showToggleBtn : true,
12218     animate : true,
12219     emptyResultText: 'Empty',
12220     triggerText : 'Select',
12221     
12222     // element that contains real text value.. (when hidden is used..)
12223     
12224     getAutoCreate : function()
12225     {
12226         var cfg = false;
12227         
12228         /*
12229          * Touch Devices
12230          */
12231         
12232         if(Roo.isTouch && this.mobileTouchView){
12233             cfg = this.getAutoCreateTouchView();
12234             return cfg;;
12235         }
12236         
12237         /*
12238          *  Normal ComboBox
12239          */
12240         if(!this.tickable){
12241             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12242             return cfg;
12243         }
12244         
12245         /*
12246          *  ComboBox with tickable selections
12247          */
12248              
12249         var align = this.labelAlign || this.parentLabelAlign();
12250         
12251         cfg = {
12252             cls : 'form-group roo-combobox-tickable' //input-group
12253         };
12254         
12255         var buttons = {
12256             tag : 'div',
12257             cls : 'tickable-buttons',
12258             cn : [
12259                 {
12260                     tag : 'button',
12261                     type : 'button',
12262                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12263                     html : this.triggerText
12264                 },
12265                 {
12266                     tag : 'button',
12267                     type : 'button',
12268                     name : 'ok',
12269                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12270                     html : 'Done'
12271                 },
12272                 {
12273                     tag : 'button',
12274                     type : 'button',
12275                     name : 'cancel',
12276                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12277                     html : 'Cancel'
12278                 }
12279             ]
12280         };
12281         
12282         if(this.editable){
12283             buttons.cn.unshift({
12284                 tag: 'input',
12285                 cls: 'roo-select2-search-field-input'
12286             });
12287         }
12288         
12289         var _this = this;
12290         
12291         Roo.each(buttons.cn, function(c){
12292             if (_this.size) {
12293                 c.cls += ' btn-' + _this.size;
12294             }
12295
12296             if (_this.disabled) {
12297                 c.disabled = true;
12298             }
12299         });
12300         
12301         var box = {
12302             tag: 'div',
12303             cn: [
12304                 {
12305                     tag: 'input',
12306                     type : 'hidden',
12307                     cls: 'form-hidden-field'
12308                 },
12309                 {
12310                     tag: 'ul',
12311                     cls: 'roo-select2-choices',
12312                     cn:[
12313                         {
12314                             tag: 'li',
12315                             cls: 'roo-select2-search-field',
12316                             cn: [
12317
12318                                 buttons
12319                             ]
12320                         }
12321                     ]
12322                 }
12323             ]
12324         };
12325         
12326         var combobox = {
12327             cls: 'roo-select2-container input-group roo-select2-container-multi',
12328             cn: [
12329                 box
12330 //                {
12331 //                    tag: 'ul',
12332 //                    cls: 'typeahead typeahead-long dropdown-menu',
12333 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12334 //                }
12335             ]
12336         };
12337         
12338         if(this.hasFeedback && !this.allowBlank){
12339             
12340             var feedback = {
12341                 tag: 'span',
12342                 cls: 'glyphicon form-control-feedback'
12343             };
12344
12345             combobox.cn.push(feedback);
12346         }
12347         
12348         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12349             
12350 //                Roo.log("left and has label");
12351             cfg.cn = [
12352                 {
12353                     tag : 'i',
12354                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12355                     tooltip : 'This field is required'
12356                 },
12357                 {
12358                     tag: 'label',
12359                     'for' :  id,
12360                     cls : 'control-label col-sm-' + this.labelWidth,
12361                     html : this.fieldLabel
12362
12363                 },
12364                 {
12365                     cls : "col-sm-" + (12 - this.labelWidth), 
12366                     cn: [
12367                         combobox
12368                     ]
12369                 }
12370
12371             ];
12372
12373             if(this.indicatorpos == 'right'){
12374                 
12375                 cfg.cn = [
12376                     {
12377                         tag: 'label',
12378                         'for' :  id,
12379                         cls : 'control-label col-sm-' + this.labelWidth,
12380                         html : this.fieldLabel
12381
12382                     },
12383                     {
12384                         tag : 'i',
12385                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12386                         tooltip : 'This field is required'
12387                     },
12388                     {
12389                         cls : "col-sm-" + (12 - this.labelWidth), 
12390                         cn: [
12391                             combobox
12392                         ]
12393                     }
12394
12395                 ];
12396             
12397             }
12398                 
12399                 
12400         } else if ( this.fieldLabel.length) {
12401 //                Roo.log(" label");
12402                  cfg.cn = [
12403                     {
12404                         tag : 'i',
12405                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12406                         tooltip : 'This field is required'
12407                     },
12408                     {
12409                         tag: 'label',
12410                         //cls : 'input-group-addon',
12411                         html : this.fieldLabel
12412                         
12413                     },
12414                     
12415                     combobox
12416                     
12417                 ];
12418                 
12419                 if(this.indicatorpos == 'right'){
12420                     
12421                     cfg.cn = [
12422                         {
12423                             tag: 'label',
12424                             //cls : 'input-group-addon',
12425                             html : this.fieldLabel
12426
12427                         },
12428                         
12429                         {
12430                             tag : 'i',
12431                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12432                             tooltip : 'This field is required'
12433                         },
12434                         
12435                         combobox
12436
12437                     ];
12438                 
12439                 }
12440
12441         } else {
12442             
12443 //                Roo.log(" no label && no align");
12444                 cfg = combobox
12445                      
12446                 
12447         }
12448          
12449         var settings=this;
12450         ['xs','sm','md','lg'].map(function(size){
12451             if (settings[size]) {
12452                 cfg.cls += ' col-' + size + '-' + settings[size];
12453             }
12454         });
12455         
12456         return cfg;
12457         
12458     },
12459     
12460     _initEventsCalled : false,
12461     
12462     // private
12463     initEvents: function()
12464     {
12465         
12466         if (this._initEventsCalled) { // as we call render... prevent looping...
12467             return;
12468         }
12469         this._initEventsCalled = true;
12470         
12471         if (!this.store) {
12472             throw "can not find store for combo";
12473         }
12474         
12475         this.store = Roo.factory(this.store, Roo.data);
12476         
12477         // if we are building from html. then this element is so complex, that we can not really
12478         // use the rendered HTML.
12479         // so we have to trash and replace the previous code.
12480         if (Roo.XComponent.build_from_html) {
12481             
12482             // remove this element....
12483             var e = this.el.dom, k=0;
12484             while (e ) { e = e.previousSibling;  ++k;}
12485
12486             this.el.remove();
12487             
12488             this.el=false;
12489             this.rendered = false;
12490             
12491             this.render(this.parent().getChildContainer(true), k);
12492             
12493             
12494             
12495         }
12496         
12497         
12498         /*
12499          * Touch Devices
12500          */
12501         
12502         if(Roo.isTouch && this.mobileTouchView){
12503             this.initTouchView();
12504             return;
12505         }
12506         
12507         if(this.tickable){
12508             this.initTickableEvents();
12509             return;
12510         }
12511         
12512         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12513         
12514         if(this.hiddenName){
12515             
12516             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12517             
12518             this.hiddenField.dom.value =
12519                 this.hiddenValue !== undefined ? this.hiddenValue :
12520                 this.value !== undefined ? this.value : '';
12521
12522             // prevent input submission
12523             this.el.dom.removeAttribute('name');
12524             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12525              
12526              
12527         }
12528         //if(Roo.isGecko){
12529         //    this.el.dom.setAttribute('autocomplete', 'off');
12530         //}
12531         
12532         var cls = 'x-combo-list';
12533         
12534         //this.list = new Roo.Layer({
12535         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12536         //});
12537         
12538         var _this = this;
12539         
12540         (function(){
12541             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12542             _this.list.setWidth(lw);
12543         }).defer(100);
12544         
12545         this.list.on('mouseover', this.onViewOver, this);
12546         this.list.on('mousemove', this.onViewMove, this);
12547         
12548         this.list.on('scroll', this.onViewScroll, this);
12549         
12550         /*
12551         this.list.swallowEvent('mousewheel');
12552         this.assetHeight = 0;
12553
12554         if(this.title){
12555             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12556             this.assetHeight += this.header.getHeight();
12557         }
12558
12559         this.innerList = this.list.createChild({cls:cls+'-inner'});
12560         this.innerList.on('mouseover', this.onViewOver, this);
12561         this.innerList.on('mousemove', this.onViewMove, this);
12562         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12563         
12564         if(this.allowBlank && !this.pageSize && !this.disableClear){
12565             this.footer = this.list.createChild({cls:cls+'-ft'});
12566             this.pageTb = new Roo.Toolbar(this.footer);
12567            
12568         }
12569         if(this.pageSize){
12570             this.footer = this.list.createChild({cls:cls+'-ft'});
12571             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12572                     {pageSize: this.pageSize});
12573             
12574         }
12575         
12576         if (this.pageTb && this.allowBlank && !this.disableClear) {
12577             var _this = this;
12578             this.pageTb.add(new Roo.Toolbar.Fill(), {
12579                 cls: 'x-btn-icon x-btn-clear',
12580                 text: '&#160;',
12581                 handler: function()
12582                 {
12583                     _this.collapse();
12584                     _this.clearValue();
12585                     _this.onSelect(false, -1);
12586                 }
12587             });
12588         }
12589         if (this.footer) {
12590             this.assetHeight += this.footer.getHeight();
12591         }
12592         */
12593             
12594         if(!this.tpl){
12595             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12596         }
12597
12598         this.view = new Roo.View(this.list, this.tpl, {
12599             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12600         });
12601         //this.view.wrapEl.setDisplayed(false);
12602         this.view.on('click', this.onViewClick, this);
12603         
12604         
12605         
12606         this.store.on('beforeload', this.onBeforeLoad, this);
12607         this.store.on('load', this.onLoad, this);
12608         this.store.on('loadexception', this.onLoadException, this);
12609         /*
12610         if(this.resizable){
12611             this.resizer = new Roo.Resizable(this.list,  {
12612                pinned:true, handles:'se'
12613             });
12614             this.resizer.on('resize', function(r, w, h){
12615                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12616                 this.listWidth = w;
12617                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12618                 this.restrictHeight();
12619             }, this);
12620             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12621         }
12622         */
12623         if(!this.editable){
12624             this.editable = true;
12625             this.setEditable(false);
12626         }
12627         
12628         /*
12629         
12630         if (typeof(this.events.add.listeners) != 'undefined') {
12631             
12632             this.addicon = this.wrap.createChild(
12633                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12634        
12635             this.addicon.on('click', function(e) {
12636                 this.fireEvent('add', this);
12637             }, this);
12638         }
12639         if (typeof(this.events.edit.listeners) != 'undefined') {
12640             
12641             this.editicon = this.wrap.createChild(
12642                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12643             if (this.addicon) {
12644                 this.editicon.setStyle('margin-left', '40px');
12645             }
12646             this.editicon.on('click', function(e) {
12647                 
12648                 // we fire even  if inothing is selected..
12649                 this.fireEvent('edit', this, this.lastData );
12650                 
12651             }, this);
12652         }
12653         */
12654         
12655         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12656             "up" : function(e){
12657                 this.inKeyMode = true;
12658                 this.selectPrev();
12659             },
12660
12661             "down" : function(e){
12662                 if(!this.isExpanded()){
12663                     this.onTriggerClick();
12664                 }else{
12665                     this.inKeyMode = true;
12666                     this.selectNext();
12667                 }
12668             },
12669
12670             "enter" : function(e){
12671 //                this.onViewClick();
12672                 //return true;
12673                 this.collapse();
12674                 
12675                 if(this.fireEvent("specialkey", this, e)){
12676                     this.onViewClick(false);
12677                 }
12678                 
12679                 return true;
12680             },
12681
12682             "esc" : function(e){
12683                 this.collapse();
12684             },
12685
12686             "tab" : function(e){
12687                 this.collapse();
12688                 
12689                 if(this.fireEvent("specialkey", this, e)){
12690                     this.onViewClick(false);
12691                 }
12692                 
12693                 return true;
12694             },
12695
12696             scope : this,
12697
12698             doRelay : function(foo, bar, hname){
12699                 if(hname == 'down' || this.scope.isExpanded()){
12700                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12701                 }
12702                 return true;
12703             },
12704
12705             forceKeyDown: true
12706         });
12707         
12708         
12709         this.queryDelay = Math.max(this.queryDelay || 10,
12710                 this.mode == 'local' ? 10 : 250);
12711         
12712         
12713         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12714         
12715         if(this.typeAhead){
12716             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12717         }
12718         if(this.editable !== false){
12719             this.inputEl().on("keyup", this.onKeyUp, this);
12720         }
12721         if(this.forceSelection){
12722             this.inputEl().on('blur', this.doForce, this);
12723         }
12724         
12725         if(this.multiple){
12726             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12727             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12728         }
12729     },
12730     
12731     initTickableEvents: function()
12732     {   
12733         this.createList();
12734         
12735         if(this.hiddenName){
12736             
12737             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12738             
12739             this.hiddenField.dom.value =
12740                 this.hiddenValue !== undefined ? this.hiddenValue :
12741                 this.value !== undefined ? this.value : '';
12742
12743             // prevent input submission
12744             this.el.dom.removeAttribute('name');
12745             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12746              
12747              
12748         }
12749         
12750 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12751         
12752         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12753         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12754         if(this.triggerList){
12755             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12756         }
12757          
12758         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12759         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12760         
12761         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12762         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12763         
12764         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12765         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12766         
12767         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12768         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12769         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12770         
12771         this.okBtn.hide();
12772         this.cancelBtn.hide();
12773         
12774         var _this = this;
12775         
12776         (function(){
12777             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12778             _this.list.setWidth(lw);
12779         }).defer(100);
12780         
12781         this.list.on('mouseover', this.onViewOver, this);
12782         this.list.on('mousemove', this.onViewMove, this);
12783         
12784         this.list.on('scroll', this.onViewScroll, this);
12785         
12786         if(!this.tpl){
12787             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>';
12788         }
12789
12790         this.view = new Roo.View(this.list, this.tpl, {
12791             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12792         });
12793         
12794         //this.view.wrapEl.setDisplayed(false);
12795         this.view.on('click', this.onViewClick, this);
12796         
12797         
12798         
12799         this.store.on('beforeload', this.onBeforeLoad, this);
12800         this.store.on('load', this.onLoad, this);
12801         this.store.on('loadexception', this.onLoadException, this);
12802         
12803         if(this.editable){
12804             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12805                 "up" : function(e){
12806                     this.inKeyMode = true;
12807                     this.selectPrev();
12808                 },
12809
12810                 "down" : function(e){
12811                     this.inKeyMode = true;
12812                     this.selectNext();
12813                 },
12814
12815                 "enter" : function(e){
12816                     if(this.fireEvent("specialkey", this, e)){
12817                         this.onViewClick(false);
12818                     }
12819                     
12820                     return true;
12821                 },
12822
12823                 "esc" : function(e){
12824                     this.onTickableFooterButtonClick(e, false, false);
12825                 },
12826
12827                 "tab" : function(e){
12828                     this.fireEvent("specialkey", this, e);
12829                     
12830                     this.onTickableFooterButtonClick(e, false, false);
12831                     
12832                     return true;
12833                 },
12834
12835                 scope : this,
12836
12837                 doRelay : function(e, fn, key){
12838                     if(this.scope.isExpanded()){
12839                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12840                     }
12841                     return true;
12842                 },
12843
12844                 forceKeyDown: true
12845             });
12846         }
12847         
12848         this.queryDelay = Math.max(this.queryDelay || 10,
12849                 this.mode == 'local' ? 10 : 250);
12850         
12851         
12852         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12853         
12854         if(this.typeAhead){
12855             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12856         }
12857         
12858         if(this.editable !== false){
12859             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12860         }
12861         
12862     },
12863
12864     onDestroy : function(){
12865         if(this.view){
12866             this.view.setStore(null);
12867             this.view.el.removeAllListeners();
12868             this.view.el.remove();
12869             this.view.purgeListeners();
12870         }
12871         if(this.list){
12872             this.list.dom.innerHTML  = '';
12873         }
12874         
12875         if(this.store){
12876             this.store.un('beforeload', this.onBeforeLoad, this);
12877             this.store.un('load', this.onLoad, this);
12878             this.store.un('loadexception', this.onLoadException, this);
12879         }
12880         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12881     },
12882
12883     // private
12884     fireKey : function(e){
12885         if(e.isNavKeyPress() && !this.list.isVisible()){
12886             this.fireEvent("specialkey", this, e);
12887         }
12888     },
12889
12890     // private
12891     onResize: function(w, h){
12892 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12893 //        
12894 //        if(typeof w != 'number'){
12895 //            // we do not handle it!?!?
12896 //            return;
12897 //        }
12898 //        var tw = this.trigger.getWidth();
12899 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12900 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12901 //        var x = w - tw;
12902 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12903 //            
12904 //        //this.trigger.setStyle('left', x+'px');
12905 //        
12906 //        if(this.list && this.listWidth === undefined){
12907 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12908 //            this.list.setWidth(lw);
12909 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12910 //        }
12911         
12912     
12913         
12914     },
12915
12916     /**
12917      * Allow or prevent the user from directly editing the field text.  If false is passed,
12918      * the user will only be able to select from the items defined in the dropdown list.  This method
12919      * is the runtime equivalent of setting the 'editable' config option at config time.
12920      * @param {Boolean} value True to allow the user to directly edit the field text
12921      */
12922     setEditable : function(value){
12923         if(value == this.editable){
12924             return;
12925         }
12926         this.editable = value;
12927         if(!value){
12928             this.inputEl().dom.setAttribute('readOnly', true);
12929             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12930             this.inputEl().addClass('x-combo-noedit');
12931         }else{
12932             this.inputEl().dom.setAttribute('readOnly', false);
12933             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12934             this.inputEl().removeClass('x-combo-noedit');
12935         }
12936     },
12937
12938     // private
12939     
12940     onBeforeLoad : function(combo,opts){
12941         if(!this.hasFocus){
12942             return;
12943         }
12944          if (!opts.add) {
12945             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12946          }
12947         this.restrictHeight();
12948         this.selectedIndex = -1;
12949     },
12950
12951     // private
12952     onLoad : function(){
12953         
12954         this.hasQuery = false;
12955         
12956         if(!this.hasFocus){
12957             return;
12958         }
12959         
12960         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12961             this.loading.hide();
12962         }
12963              
12964         if(this.store.getCount() > 0){
12965             this.expand();
12966             this.restrictHeight();
12967             if(this.lastQuery == this.allQuery){
12968                 if(this.editable && !this.tickable){
12969                     this.inputEl().dom.select();
12970                 }
12971                 
12972                 if(
12973                     !this.selectByValue(this.value, true) &&
12974                     this.autoFocus && 
12975                     (
12976                         !this.store.lastOptions ||
12977                         typeof(this.store.lastOptions.add) == 'undefined' || 
12978                         this.store.lastOptions.add != true
12979                     )
12980                 ){
12981                     this.select(0, true);
12982                 }
12983             }else{
12984                 if(this.autoFocus){
12985                     this.selectNext();
12986                 }
12987                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12988                     this.taTask.delay(this.typeAheadDelay);
12989                 }
12990             }
12991         }else{
12992             this.onEmptyResults();
12993         }
12994         
12995         //this.el.focus();
12996     },
12997     // private
12998     onLoadException : function()
12999     {
13000         this.hasQuery = false;
13001         
13002         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13003             this.loading.hide();
13004         }
13005         
13006         if(this.tickable && this.editable){
13007             return;
13008         }
13009         
13010         this.collapse();
13011         // only causes errors at present
13012         //Roo.log(this.store.reader.jsonData);
13013         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13014             // fixme
13015             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13016         //}
13017         
13018         
13019     },
13020     // private
13021     onTypeAhead : function(){
13022         if(this.store.getCount() > 0){
13023             var r = this.store.getAt(0);
13024             var newValue = r.data[this.displayField];
13025             var len = newValue.length;
13026             var selStart = this.getRawValue().length;
13027             
13028             if(selStart != len){
13029                 this.setRawValue(newValue);
13030                 this.selectText(selStart, newValue.length);
13031             }
13032         }
13033     },
13034
13035     // private
13036     onSelect : function(record, index){
13037         
13038         if(this.fireEvent('beforeselect', this, record, index) !== false){
13039         
13040             this.setFromData(index > -1 ? record.data : false);
13041             
13042             this.collapse();
13043             this.fireEvent('select', this, record, index);
13044         }
13045     },
13046
13047     /**
13048      * Returns the currently selected field value or empty string if no value is set.
13049      * @return {String} value The selected value
13050      */
13051     getValue : function(){
13052         
13053         if(this.multiple){
13054             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13055         }
13056         
13057         if(this.valueField){
13058             return typeof this.value != 'undefined' ? this.value : '';
13059         }else{
13060             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13061         }
13062     },
13063
13064     /**
13065      * Clears any text/value currently set in the field
13066      */
13067     clearValue : function(){
13068         if(this.hiddenField){
13069             this.hiddenField.dom.value = '';
13070         }
13071         this.value = '';
13072         this.setRawValue('');
13073         this.lastSelectionText = '';
13074         this.lastData = false;
13075         
13076         var close = this.closeTriggerEl();
13077         
13078         if(close){
13079             close.hide();
13080         }
13081         
13082         this.validate();
13083         
13084     },
13085
13086     /**
13087      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13088      * will be displayed in the field.  If the value does not match the data value of an existing item,
13089      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13090      * Otherwise the field will be blank (although the value will still be set).
13091      * @param {String} value The value to match
13092      */
13093     setValue : function(v){
13094         if(this.multiple){
13095             this.syncValue();
13096             return;
13097         }
13098         
13099         var text = v;
13100         if(this.valueField){
13101             var r = this.findRecord(this.valueField, v);
13102             if(r){
13103                 text = r.data[this.displayField];
13104             }else if(this.valueNotFoundText !== undefined){
13105                 text = this.valueNotFoundText;
13106             }
13107         }
13108         this.lastSelectionText = text;
13109         if(this.hiddenField){
13110             this.hiddenField.dom.value = v;
13111         }
13112         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13113         this.value = v;
13114         
13115         var close = this.closeTriggerEl();
13116         
13117         if(close){
13118             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13119         }
13120         
13121         this.validate();
13122     },
13123     /**
13124      * @property {Object} the last set data for the element
13125      */
13126     
13127     lastData : false,
13128     /**
13129      * Sets the value of the field based on a object which is related to the record format for the store.
13130      * @param {Object} value the value to set as. or false on reset?
13131      */
13132     setFromData : function(o){
13133         
13134         if(this.multiple){
13135             this.addItem(o);
13136             return;
13137         }
13138             
13139         var dv = ''; // display value
13140         var vv = ''; // value value..
13141         this.lastData = o;
13142         if (this.displayField) {
13143             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13144         } else {
13145             // this is an error condition!!!
13146             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13147         }
13148         
13149         if(this.valueField){
13150             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13151         }
13152         
13153         var close = this.closeTriggerEl();
13154         
13155         if(close){
13156             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13157         }
13158         
13159         if(this.hiddenField){
13160             this.hiddenField.dom.value = vv;
13161             
13162             this.lastSelectionText = dv;
13163             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13164             this.value = vv;
13165             return;
13166         }
13167         // no hidden field.. - we store the value in 'value', but still display
13168         // display field!!!!
13169         this.lastSelectionText = dv;
13170         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13171         this.value = vv;
13172         
13173         
13174         
13175     },
13176     // private
13177     reset : function(){
13178         // overridden so that last data is reset..
13179         
13180         if(this.multiple){
13181             this.clearItem();
13182             return;
13183         }
13184         
13185         this.setValue(this.originalValue);
13186         //this.clearInvalid();
13187         this.lastData = false;
13188         if (this.view) {
13189             this.view.clearSelections();
13190         }
13191         
13192         this.validate();
13193     },
13194     // private
13195     findRecord : function(prop, value){
13196         var record;
13197         if(this.store.getCount() > 0){
13198             this.store.each(function(r){
13199                 if(r.data[prop] == value){
13200                     record = r;
13201                     return false;
13202                 }
13203                 return true;
13204             });
13205         }
13206         return record;
13207     },
13208     
13209     getName: function()
13210     {
13211         // returns hidden if it's set..
13212         if (!this.rendered) {return ''};
13213         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13214         
13215     },
13216     // private
13217     onViewMove : function(e, t){
13218         this.inKeyMode = false;
13219     },
13220
13221     // private
13222     onViewOver : function(e, t){
13223         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13224             return;
13225         }
13226         var item = this.view.findItemFromChild(t);
13227         
13228         if(item){
13229             var index = this.view.indexOf(item);
13230             this.select(index, false);
13231         }
13232     },
13233
13234     // private
13235     onViewClick : function(view, doFocus, el, e)
13236     {
13237         var index = this.view.getSelectedIndexes()[0];
13238         
13239         var r = this.store.getAt(index);
13240         
13241         if(this.tickable){
13242             
13243             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13244                 return;
13245             }
13246             
13247             var rm = false;
13248             var _this = this;
13249             
13250             Roo.each(this.tickItems, function(v,k){
13251                 
13252                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13253                     Roo.log(v);
13254                     _this.tickItems.splice(k, 1);
13255                     
13256                     if(typeof(e) == 'undefined' && view == false){
13257                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13258                     }
13259                     
13260                     rm = true;
13261                     return;
13262                 }
13263             });
13264             
13265             if(rm){
13266                 return;
13267             }
13268             
13269             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13270                 this.tickItems.push(r.data);
13271             }
13272             
13273             if(typeof(e) == 'undefined' && view == false){
13274                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13275             }
13276                     
13277             return;
13278         }
13279         
13280         if(r){
13281             this.onSelect(r, index);
13282         }
13283         if(doFocus !== false && !this.blockFocus){
13284             this.inputEl().focus();
13285         }
13286     },
13287
13288     // private
13289     restrictHeight : function(){
13290         //this.innerList.dom.style.height = '';
13291         //var inner = this.innerList.dom;
13292         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13293         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13294         //this.list.beginUpdate();
13295         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13296         this.list.alignTo(this.inputEl(), this.listAlign);
13297         this.list.alignTo(this.inputEl(), this.listAlign);
13298         //this.list.endUpdate();
13299     },
13300
13301     // private
13302     onEmptyResults : function(){
13303         
13304         if(this.tickable && this.editable){
13305             this.restrictHeight();
13306             return;
13307         }
13308         
13309         this.collapse();
13310     },
13311
13312     /**
13313      * Returns true if the dropdown list is expanded, else false.
13314      */
13315     isExpanded : function(){
13316         return this.list.isVisible();
13317     },
13318
13319     /**
13320      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13321      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13322      * @param {String} value The data value of the item to select
13323      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13324      * selected item if it is not currently in view (defaults to true)
13325      * @return {Boolean} True if the value matched an item in the list, else false
13326      */
13327     selectByValue : function(v, scrollIntoView){
13328         if(v !== undefined && v !== null){
13329             var r = this.findRecord(this.valueField || this.displayField, v);
13330             if(r){
13331                 this.select(this.store.indexOf(r), scrollIntoView);
13332                 return true;
13333             }
13334         }
13335         return false;
13336     },
13337
13338     /**
13339      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13340      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13341      * @param {Number} index The zero-based index of the list item to select
13342      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13343      * selected item if it is not currently in view (defaults to true)
13344      */
13345     select : function(index, scrollIntoView){
13346         this.selectedIndex = index;
13347         this.view.select(index);
13348         if(scrollIntoView !== false){
13349             var el = this.view.getNode(index);
13350             /*
13351              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13352              */
13353             if(el){
13354                 this.list.scrollChildIntoView(el, false);
13355             }
13356         }
13357     },
13358
13359     // private
13360     selectNext : function(){
13361         var ct = this.store.getCount();
13362         if(ct > 0){
13363             if(this.selectedIndex == -1){
13364                 this.select(0);
13365             }else if(this.selectedIndex < ct-1){
13366                 this.select(this.selectedIndex+1);
13367             }
13368         }
13369     },
13370
13371     // private
13372     selectPrev : function(){
13373         var ct = this.store.getCount();
13374         if(ct > 0){
13375             if(this.selectedIndex == -1){
13376                 this.select(0);
13377             }else if(this.selectedIndex != 0){
13378                 this.select(this.selectedIndex-1);
13379             }
13380         }
13381     },
13382
13383     // private
13384     onKeyUp : function(e){
13385         if(this.editable !== false && !e.isSpecialKey()){
13386             this.lastKey = e.getKey();
13387             this.dqTask.delay(this.queryDelay);
13388         }
13389     },
13390
13391     // private
13392     validateBlur : function(){
13393         return !this.list || !this.list.isVisible();   
13394     },
13395
13396     // private
13397     initQuery : function(){
13398         
13399         var v = this.getRawValue();
13400         
13401         if(this.tickable && this.editable){
13402             v = this.tickableInputEl().getValue();
13403         }
13404         
13405         this.doQuery(v);
13406     },
13407
13408     // private
13409     doForce : function(){
13410         if(this.inputEl().dom.value.length > 0){
13411             this.inputEl().dom.value =
13412                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13413              
13414         }
13415     },
13416
13417     /**
13418      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13419      * query allowing the query action to be canceled if needed.
13420      * @param {String} query The SQL query to execute
13421      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13422      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13423      * saved in the current store (defaults to false)
13424      */
13425     doQuery : function(q, forceAll){
13426         
13427         if(q === undefined || q === null){
13428             q = '';
13429         }
13430         var qe = {
13431             query: q,
13432             forceAll: forceAll,
13433             combo: this,
13434             cancel:false
13435         };
13436         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13437             return false;
13438         }
13439         q = qe.query;
13440         
13441         forceAll = qe.forceAll;
13442         if(forceAll === true || (q.length >= this.minChars)){
13443             
13444             this.hasQuery = true;
13445             
13446             if(this.lastQuery != q || this.alwaysQuery){
13447                 this.lastQuery = q;
13448                 if(this.mode == 'local'){
13449                     this.selectedIndex = -1;
13450                     if(forceAll){
13451                         this.store.clearFilter();
13452                     }else{
13453                         
13454                         if(this.specialFilter){
13455                             this.fireEvent('specialfilter', this);
13456                             this.onLoad();
13457                             return;
13458                         }
13459                         
13460                         this.store.filter(this.displayField, q);
13461                     }
13462                     
13463                     this.store.fireEvent("datachanged", this.store);
13464                     
13465                     this.onLoad();
13466                     
13467                     
13468                 }else{
13469                     
13470                     this.store.baseParams[this.queryParam] = q;
13471                     
13472                     var options = {params : this.getParams(q)};
13473                     
13474                     if(this.loadNext){
13475                         options.add = true;
13476                         options.params.start = this.page * this.pageSize;
13477                     }
13478                     
13479                     this.store.load(options);
13480                     
13481                     /*
13482                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13483                      *  we should expand the list on onLoad
13484                      *  so command out it
13485                      */
13486 //                    this.expand();
13487                 }
13488             }else{
13489                 this.selectedIndex = -1;
13490                 this.onLoad();   
13491             }
13492         }
13493         
13494         this.loadNext = false;
13495     },
13496     
13497     // private
13498     getParams : function(q){
13499         var p = {};
13500         //p[this.queryParam] = q;
13501         
13502         if(this.pageSize){
13503             p.start = 0;
13504             p.limit = this.pageSize;
13505         }
13506         return p;
13507     },
13508
13509     /**
13510      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13511      */
13512     collapse : function(){
13513         if(!this.isExpanded()){
13514             return;
13515         }
13516         
13517         this.list.hide();
13518         
13519         if(this.tickable){
13520             this.hasFocus = false;
13521             this.okBtn.hide();
13522             this.cancelBtn.hide();
13523             this.trigger.show();
13524             
13525             if(this.editable){
13526                 this.tickableInputEl().dom.value = '';
13527                 this.tickableInputEl().blur();
13528             }
13529             
13530         }
13531         
13532         Roo.get(document).un('mousedown', this.collapseIf, this);
13533         Roo.get(document).un('mousewheel', this.collapseIf, this);
13534         if (!this.editable) {
13535             Roo.get(document).un('keydown', this.listKeyPress, this);
13536         }
13537         this.fireEvent('collapse', this);
13538         
13539         this.validate();
13540     },
13541
13542     // private
13543     collapseIf : function(e){
13544         var in_combo  = e.within(this.el);
13545         var in_list =  e.within(this.list);
13546         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13547         
13548         if (in_combo || in_list || is_list) {
13549             //e.stopPropagation();
13550             return;
13551         }
13552         
13553         if(this.tickable){
13554             this.onTickableFooterButtonClick(e, false, false);
13555         }
13556
13557         this.collapse();
13558         
13559     },
13560
13561     /**
13562      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13563      */
13564     expand : function(){
13565        
13566         if(this.isExpanded() || !this.hasFocus){
13567             return;
13568         }
13569         
13570         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13571         this.list.setWidth(lw);
13572         
13573         
13574          Roo.log('expand');
13575         
13576         this.list.show();
13577         
13578         this.restrictHeight();
13579         
13580         if(this.tickable){
13581             
13582             this.tickItems = Roo.apply([], this.item);
13583             
13584             this.okBtn.show();
13585             this.cancelBtn.show();
13586             this.trigger.hide();
13587             
13588             if(this.editable){
13589                 this.tickableInputEl().focus();
13590             }
13591             
13592         }
13593         
13594         Roo.get(document).on('mousedown', this.collapseIf, this);
13595         Roo.get(document).on('mousewheel', this.collapseIf, this);
13596         if (!this.editable) {
13597             Roo.get(document).on('keydown', this.listKeyPress, this);
13598         }
13599         
13600         this.fireEvent('expand', this);
13601     },
13602
13603     // private
13604     // Implements the default empty TriggerField.onTriggerClick function
13605     onTriggerClick : function(e)
13606     {
13607         Roo.log('trigger click');
13608         
13609         if(this.disabled || !this.triggerList){
13610             return;
13611         }
13612         
13613         this.page = 0;
13614         this.loadNext = false;
13615         
13616         if(this.isExpanded()){
13617             this.collapse();
13618             if (!this.blockFocus) {
13619                 this.inputEl().focus();
13620             }
13621             
13622         }else {
13623             this.hasFocus = true;
13624             if(this.triggerAction == 'all') {
13625                 this.doQuery(this.allQuery, true);
13626             } else {
13627                 this.doQuery(this.getRawValue());
13628             }
13629             if (!this.blockFocus) {
13630                 this.inputEl().focus();
13631             }
13632         }
13633     },
13634     
13635     onTickableTriggerClick : function(e)
13636     {
13637         if(this.disabled){
13638             return;
13639         }
13640         
13641         this.page = 0;
13642         this.loadNext = false;
13643         this.hasFocus = true;
13644         
13645         if(this.triggerAction == 'all') {
13646             this.doQuery(this.allQuery, true);
13647         } else {
13648             this.doQuery(this.getRawValue());
13649         }
13650     },
13651     
13652     onSearchFieldClick : function(e)
13653     {
13654         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13655             this.onTickableFooterButtonClick(e, false, false);
13656             return;
13657         }
13658         
13659         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13660             return;
13661         }
13662         
13663         this.page = 0;
13664         this.loadNext = false;
13665         this.hasFocus = true;
13666         
13667         if(this.triggerAction == 'all') {
13668             this.doQuery(this.allQuery, true);
13669         } else {
13670             this.doQuery(this.getRawValue());
13671         }
13672     },
13673     
13674     listKeyPress : function(e)
13675     {
13676         //Roo.log('listkeypress');
13677         // scroll to first matching element based on key pres..
13678         if (e.isSpecialKey()) {
13679             return false;
13680         }
13681         var k = String.fromCharCode(e.getKey()).toUpperCase();
13682         //Roo.log(k);
13683         var match  = false;
13684         var csel = this.view.getSelectedNodes();
13685         var cselitem = false;
13686         if (csel.length) {
13687             var ix = this.view.indexOf(csel[0]);
13688             cselitem  = this.store.getAt(ix);
13689             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13690                 cselitem = false;
13691             }
13692             
13693         }
13694         
13695         this.store.each(function(v) { 
13696             if (cselitem) {
13697                 // start at existing selection.
13698                 if (cselitem.id == v.id) {
13699                     cselitem = false;
13700                 }
13701                 return true;
13702             }
13703                 
13704             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13705                 match = this.store.indexOf(v);
13706                 return false;
13707             }
13708             return true;
13709         }, this);
13710         
13711         if (match === false) {
13712             return true; // no more action?
13713         }
13714         // scroll to?
13715         this.view.select(match);
13716         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13717         sn.scrollIntoView(sn.dom.parentNode, false);
13718     },
13719     
13720     onViewScroll : function(e, t){
13721         
13722         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){
13723             return;
13724         }
13725         
13726         this.hasQuery = true;
13727         
13728         this.loading = this.list.select('.loading', true).first();
13729         
13730         if(this.loading === null){
13731             this.list.createChild({
13732                 tag: 'div',
13733                 cls: 'loading roo-select2-more-results roo-select2-active',
13734                 html: 'Loading more results...'
13735             });
13736             
13737             this.loading = this.list.select('.loading', true).first();
13738             
13739             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13740             
13741             this.loading.hide();
13742         }
13743         
13744         this.loading.show();
13745         
13746         var _combo = this;
13747         
13748         this.page++;
13749         this.loadNext = true;
13750         
13751         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13752         
13753         return;
13754     },
13755     
13756     addItem : function(o)
13757     {   
13758         var dv = ''; // display value
13759         
13760         if (this.displayField) {
13761             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13762         } else {
13763             // this is an error condition!!!
13764             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13765         }
13766         
13767         if(!dv.length){
13768             return;
13769         }
13770         
13771         var choice = this.choices.createChild({
13772             tag: 'li',
13773             cls: 'roo-select2-search-choice',
13774             cn: [
13775                 {
13776                     tag: 'div',
13777                     html: dv
13778                 },
13779                 {
13780                     tag: 'a',
13781                     href: '#',
13782                     cls: 'roo-select2-search-choice-close',
13783                     tabindex: '-1'
13784                 }
13785             ]
13786             
13787         }, this.searchField);
13788         
13789         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13790         
13791         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13792         
13793         this.item.push(o);
13794         
13795         this.lastData = o;
13796         
13797         this.syncValue();
13798         
13799         this.inputEl().dom.value = '';
13800         
13801         this.validate();
13802     },
13803     
13804     onRemoveItem : function(e, _self, o)
13805     {
13806         e.preventDefault();
13807         
13808         this.lastItem = Roo.apply([], this.item);
13809         
13810         var index = this.item.indexOf(o.data) * 1;
13811         
13812         if( index < 0){
13813             Roo.log('not this item?!');
13814             return;
13815         }
13816         
13817         this.item.splice(index, 1);
13818         o.item.remove();
13819         
13820         this.syncValue();
13821         
13822         this.fireEvent('remove', this, e);
13823         
13824         this.validate();
13825         
13826     },
13827     
13828     syncValue : function()
13829     {
13830         if(!this.item.length){
13831             this.clearValue();
13832             return;
13833         }
13834             
13835         var value = [];
13836         var _this = this;
13837         Roo.each(this.item, function(i){
13838             if(_this.valueField){
13839                 value.push(i[_this.valueField]);
13840                 return;
13841             }
13842
13843             value.push(i);
13844         });
13845
13846         this.value = value.join(',');
13847
13848         if(this.hiddenField){
13849             this.hiddenField.dom.value = this.value;
13850         }
13851         
13852         this.store.fireEvent("datachanged", this.store);
13853         
13854         this.validate();
13855     },
13856     
13857     clearItem : function()
13858     {
13859         if(!this.multiple){
13860             return;
13861         }
13862         
13863         this.item = [];
13864         
13865         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13866            c.remove();
13867         });
13868         
13869         this.syncValue();
13870         
13871         this.validate();
13872         
13873         if(this.tickable && !Roo.isTouch){
13874             this.view.refresh();
13875         }
13876     },
13877     
13878     inputEl: function ()
13879     {
13880         if(Roo.isTouch && this.mobileTouchView){
13881             return this.el.select('input.form-control',true).first();
13882         }
13883         
13884         if(this.tickable){
13885             return this.searchField;
13886         }
13887         
13888         return this.el.select('input.form-control',true).first();
13889     },
13890     
13891     
13892     onTickableFooterButtonClick : function(e, btn, el)
13893     {
13894         e.preventDefault();
13895         
13896         this.lastItem = Roo.apply([], this.item);
13897         
13898         if(btn && btn.name == 'cancel'){
13899             this.tickItems = Roo.apply([], this.item);
13900             this.collapse();
13901             return;
13902         }
13903         
13904         this.clearItem();
13905         
13906         var _this = this;
13907         
13908         Roo.each(this.tickItems, function(o){
13909             _this.addItem(o);
13910         });
13911         
13912         this.collapse();
13913         
13914     },
13915     
13916     validate : function()
13917     {
13918         var v = this.getRawValue();
13919         
13920         if(this.multiple){
13921             v = this.getValue();
13922         }
13923         
13924         if(this.disabled || this.allowBlank || v.length){
13925             this.markValid();
13926             return true;
13927         }
13928         
13929         this.markInvalid();
13930         return false;
13931     },
13932     
13933     tickableInputEl : function()
13934     {
13935         if(!this.tickable || !this.editable){
13936             return this.inputEl();
13937         }
13938         
13939         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13940     },
13941     
13942     
13943     getAutoCreateTouchView : function()
13944     {
13945         var id = Roo.id();
13946         
13947         var cfg = {
13948             cls: 'form-group' //input-group
13949         };
13950         
13951         var input =  {
13952             tag: 'input',
13953             id : id,
13954             type : this.inputType,
13955             cls : 'form-control x-combo-noedit',
13956             autocomplete: 'new-password',
13957             placeholder : this.placeholder || '',
13958             readonly : true
13959         };
13960         
13961         if (this.name) {
13962             input.name = this.name;
13963         }
13964         
13965         if (this.size) {
13966             input.cls += ' input-' + this.size;
13967         }
13968         
13969         if (this.disabled) {
13970             input.disabled = true;
13971         }
13972         
13973         var inputblock = {
13974             cls : '',
13975             cn : [
13976                 input
13977             ]
13978         };
13979         
13980         if(this.before){
13981             inputblock.cls += ' input-group';
13982             
13983             inputblock.cn.unshift({
13984                 tag :'span',
13985                 cls : 'input-group-addon',
13986                 html : this.before
13987             });
13988         }
13989         
13990         if(this.removable && !this.multiple){
13991             inputblock.cls += ' roo-removable';
13992             
13993             inputblock.cn.push({
13994                 tag: 'button',
13995                 html : 'x',
13996                 cls : 'roo-combo-removable-btn close'
13997             });
13998         }
13999
14000         if(this.hasFeedback && !this.allowBlank){
14001             
14002             inputblock.cls += ' has-feedback';
14003             
14004             inputblock.cn.push({
14005                 tag: 'span',
14006                 cls: 'glyphicon form-control-feedback'
14007             });
14008             
14009         }
14010         
14011         if (this.after) {
14012             
14013             inputblock.cls += (this.before) ? '' : ' input-group';
14014             
14015             inputblock.cn.push({
14016                 tag :'span',
14017                 cls : 'input-group-addon',
14018                 html : this.after
14019             });
14020         }
14021
14022         var box = {
14023             tag: 'div',
14024             cn: [
14025                 {
14026                     tag: 'input',
14027                     type : 'hidden',
14028                     cls: 'form-hidden-field'
14029                 },
14030                 inputblock
14031             ]
14032             
14033         };
14034         
14035         if(this.multiple){
14036             box = {
14037                 tag: 'div',
14038                 cn: [
14039                     {
14040                         tag: 'input',
14041                         type : 'hidden',
14042                         cls: 'form-hidden-field'
14043                     },
14044                     {
14045                         tag: 'ul',
14046                         cls: 'roo-select2-choices',
14047                         cn:[
14048                             {
14049                                 tag: 'li',
14050                                 cls: 'roo-select2-search-field',
14051                                 cn: [
14052
14053                                     inputblock
14054                                 ]
14055                             }
14056                         ]
14057                     }
14058                 ]
14059             }
14060         };
14061         
14062         var combobox = {
14063             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14064             cn: [
14065                 box
14066             ]
14067         };
14068         
14069         if(!this.multiple && this.showToggleBtn){
14070             
14071             var caret = {
14072                         tag: 'span',
14073                         cls: 'caret'
14074             };
14075             
14076             if (this.caret != false) {
14077                 caret = {
14078                      tag: 'i',
14079                      cls: 'fa fa-' + this.caret
14080                 };
14081                 
14082             }
14083             
14084             combobox.cn.push({
14085                 tag :'span',
14086                 cls : 'input-group-addon btn dropdown-toggle',
14087                 cn : [
14088                     caret,
14089                     {
14090                         tag: 'span',
14091                         cls: 'combobox-clear',
14092                         cn  : [
14093                             {
14094                                 tag : 'i',
14095                                 cls: 'icon-remove'
14096                             }
14097                         ]
14098                     }
14099                 ]
14100
14101             })
14102         }
14103         
14104         if(this.multiple){
14105             combobox.cls += ' roo-select2-container-multi';
14106         }
14107         
14108         var align = this.labelAlign || this.parentLabelAlign();
14109         
14110         cfg.cn = combobox;
14111         
14112         if(this.fieldLabel.length && this.labelWidth){
14113             
14114             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14115             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14116             
14117             cfg.cn = [
14118                 {
14119                    tag : 'i',
14120                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14121                    tooltip : 'This field is required'
14122                 },
14123                 {
14124                     tag: 'label',
14125                     cls : 'control-label ' + lw,
14126                     html : this.fieldLabel
14127
14128                 },
14129                 {
14130                     cls : cw, 
14131                     cn: [
14132                         combobox
14133                     ]
14134                 }
14135             ];
14136             
14137             if(this.indicatorpos == 'right'){
14138                 cfg.cn = [
14139                     {
14140                         tag: 'label',
14141                         cls : 'control-label ' + lw,
14142                         html : this.fieldLabel
14143
14144                     },
14145                     {
14146                        tag : 'i',
14147                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14148                        tooltip : 'This field is required'
14149                     },
14150                     {
14151                         cls : cw, 
14152                         cn: [
14153                             combobox
14154                         ]
14155                     }
14156                 ];
14157             }
14158         }
14159         
14160         var settings = this;
14161         
14162         ['xs','sm','md','lg'].map(function(size){
14163             if (settings[size]) {
14164                 cfg.cls += ' col-' + size + '-' + settings[size];
14165             }
14166         });
14167         
14168         return cfg;
14169     },
14170     
14171     initTouchView : function()
14172     {
14173         this.renderTouchView();
14174         
14175         this.touchViewEl.on('scroll', function(){
14176             this.el.dom.scrollTop = 0;
14177         }, this);
14178         
14179         this.originalValue = this.getValue();
14180         
14181         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14182         
14183         this.inputEl().on("click", this.showTouchView, this);
14184         this.triggerEl.on("click", this.showTouchView, this);
14185         
14186         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14187         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14188         
14189         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14190         
14191         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14192         this.store.on('load', this.onTouchViewLoad, this);
14193         this.store.on('loadexception', this.onTouchViewLoadException, this);
14194         
14195         if(this.hiddenName){
14196             
14197             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14198             
14199             this.hiddenField.dom.value =
14200                 this.hiddenValue !== undefined ? this.hiddenValue :
14201                 this.value !== undefined ? this.value : '';
14202         
14203             this.el.dom.removeAttribute('name');
14204             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14205         }
14206         
14207         if(this.multiple){
14208             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14209             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14210         }
14211         
14212         if(this.removable && !this.multiple){
14213             var close = this.closeTriggerEl();
14214             if(close){
14215                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14216                 close.on('click', this.removeBtnClick, this, close);
14217             }
14218         }
14219         /*
14220          * fix the bug in Safari iOS8
14221          */
14222         this.inputEl().on("focus", function(e){
14223             document.activeElement.blur();
14224         }, this);
14225         
14226         return;
14227         
14228         
14229     },
14230     
14231     renderTouchView : function()
14232     {
14233         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14234         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14235         
14236         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14237         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14238         
14239         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14240         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14241         this.touchViewBodyEl.setStyle('overflow', 'auto');
14242         
14243         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14244         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14245         
14246         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14247         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14248         
14249     },
14250     
14251     showTouchView : function()
14252     {
14253         if(this.disabled){
14254             return;
14255         }
14256         
14257         this.touchViewHeaderEl.hide();
14258
14259         if(this.modalTitle.length){
14260             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14261             this.touchViewHeaderEl.show();
14262         }
14263
14264         this.touchViewEl.show();
14265
14266         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14267         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14268                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14269
14270         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14271
14272         if(this.modalTitle.length){
14273             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14274         }
14275         
14276         this.touchViewBodyEl.setHeight(bodyHeight);
14277
14278         if(this.animate){
14279             var _this = this;
14280             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14281         }else{
14282             this.touchViewEl.addClass('in');
14283         }
14284
14285         this.doTouchViewQuery();
14286         
14287     },
14288     
14289     hideTouchView : function()
14290     {
14291         this.touchViewEl.removeClass('in');
14292
14293         if(this.animate){
14294             var _this = this;
14295             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14296         }else{
14297             this.touchViewEl.setStyle('display', 'none');
14298         }
14299         
14300     },
14301     
14302     setTouchViewValue : function()
14303     {
14304         if(this.multiple){
14305             this.clearItem();
14306         
14307             var _this = this;
14308
14309             Roo.each(this.tickItems, function(o){
14310                 this.addItem(o);
14311             }, this);
14312         }
14313         
14314         this.hideTouchView();
14315     },
14316     
14317     doTouchViewQuery : function()
14318     {
14319         var qe = {
14320             query: '',
14321             forceAll: true,
14322             combo: this,
14323             cancel:false
14324         };
14325         
14326         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14327             return false;
14328         }
14329         
14330         if(!this.alwaysQuery || this.mode == 'local'){
14331             this.onTouchViewLoad();
14332             return;
14333         }
14334         
14335         this.store.load();
14336     },
14337     
14338     onTouchViewBeforeLoad : function(combo,opts)
14339     {
14340         return;
14341     },
14342
14343     // private
14344     onTouchViewLoad : function()
14345     {
14346         if(this.store.getCount() < 1){
14347             this.onTouchViewEmptyResults();
14348             return;
14349         }
14350         
14351         this.clearTouchView();
14352         
14353         var rawValue = this.getRawValue();
14354         
14355         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14356         
14357         this.tickItems = [];
14358         
14359         this.store.data.each(function(d, rowIndex){
14360             var row = this.touchViewListGroup.createChild(template);
14361             
14362             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14363                 row.addClass(d.data.cls);
14364             }
14365             
14366             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14367                 var cfg = {
14368                     data : d.data,
14369                     html : d.data[this.displayField]
14370                 };
14371                 
14372                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14373                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14374                 }
14375             }
14376             row.removeClass('selected');
14377             if(!this.multiple && this.valueField &&
14378                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14379             {
14380                 // radio buttons..
14381                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14382                 row.addClass('selected');
14383             }
14384             
14385             if(this.multiple && this.valueField &&
14386                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14387             {
14388                 
14389                 // checkboxes...
14390                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14391                 this.tickItems.push(d.data);
14392             }
14393             
14394             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14395             
14396         }, this);
14397         
14398         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14399         
14400         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14401
14402         if(this.modalTitle.length){
14403             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14404         }
14405
14406         var listHeight = this.touchViewListGroup.getHeight();
14407         
14408         var _this = this;
14409         
14410         if(firstChecked && listHeight > bodyHeight){
14411             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14412         }
14413         
14414     },
14415     
14416     onTouchViewLoadException : function()
14417     {
14418         this.hideTouchView();
14419     },
14420     
14421     onTouchViewEmptyResults : function()
14422     {
14423         this.clearTouchView();
14424         
14425         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14426         
14427         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14428         
14429     },
14430     
14431     clearTouchView : function()
14432     {
14433         this.touchViewListGroup.dom.innerHTML = '';
14434     },
14435     
14436     onTouchViewClick : function(e, el, o)
14437     {
14438         e.preventDefault();
14439         
14440         var row = o.row;
14441         var rowIndex = o.rowIndex;
14442         
14443         var r = this.store.getAt(rowIndex);
14444         
14445         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14446             
14447             if(!this.multiple){
14448                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14449                     c.dom.removeAttribute('checked');
14450                 }, this);
14451
14452                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14453
14454                 this.setFromData(r.data);
14455
14456                 var close = this.closeTriggerEl();
14457
14458                 if(close){
14459                     close.show();
14460                 }
14461
14462                 this.hideTouchView();
14463
14464                 this.fireEvent('select', this, r, rowIndex);
14465
14466                 return;
14467             }
14468
14469             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14470                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14471                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14472                 return;
14473             }
14474
14475             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14476             this.addItem(r.data);
14477             this.tickItems.push(r.data);
14478         }
14479     }
14480     
14481
14482     /** 
14483     * @cfg {Boolean} grow 
14484     * @hide 
14485     */
14486     /** 
14487     * @cfg {Number} growMin 
14488     * @hide 
14489     */
14490     /** 
14491     * @cfg {Number} growMax 
14492     * @hide 
14493     */
14494     /**
14495      * @hide
14496      * @method autoSize
14497      */
14498 });
14499
14500 Roo.apply(Roo.bootstrap.ComboBox,  {
14501     
14502     header : {
14503         tag: 'div',
14504         cls: 'modal-header',
14505         cn: [
14506             {
14507                 tag: 'h4',
14508                 cls: 'modal-title'
14509             }
14510         ]
14511     },
14512     
14513     body : {
14514         tag: 'div',
14515         cls: 'modal-body',
14516         cn: [
14517             {
14518                 tag: 'ul',
14519                 cls: 'list-group'
14520             }
14521         ]
14522     },
14523     
14524     listItemRadio : {
14525         tag: 'li',
14526         cls: 'list-group-item',
14527         cn: [
14528             {
14529                 tag: 'span',
14530                 cls: 'roo-combobox-list-group-item-value'
14531             },
14532             {
14533                 tag: 'div',
14534                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14535                 cn: [
14536                     {
14537                         tag: 'input',
14538                         type: 'radio'
14539                     },
14540                     {
14541                         tag: 'label'
14542                     }
14543                 ]
14544             }
14545         ]
14546     },
14547     
14548     listItemCheckbox : {
14549         tag: 'li',
14550         cls: 'list-group-item',
14551         cn: [
14552             {
14553                 tag: 'span',
14554                 cls: 'roo-combobox-list-group-item-value'
14555             },
14556             {
14557                 tag: 'div',
14558                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14559                 cn: [
14560                     {
14561                         tag: 'input',
14562                         type: 'checkbox'
14563                     },
14564                     {
14565                         tag: 'label'
14566                     }
14567                 ]
14568             }
14569         ]
14570     },
14571     
14572     emptyResult : {
14573         tag: 'div',
14574         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14575     },
14576     
14577     footer : {
14578         tag: 'div',
14579         cls: 'modal-footer',
14580         cn: [
14581             {
14582                 tag: 'div',
14583                 cls: 'row',
14584                 cn: [
14585                     {
14586                         tag: 'div',
14587                         cls: 'col-xs-6 text-left',
14588                         cn: {
14589                             tag: 'button',
14590                             cls: 'btn btn-danger roo-touch-view-cancel',
14591                             html: 'Cancel'
14592                         }
14593                     },
14594                     {
14595                         tag: 'div',
14596                         cls: 'col-xs-6 text-right',
14597                         cn: {
14598                             tag: 'button',
14599                             cls: 'btn btn-success roo-touch-view-ok',
14600                             html: 'OK'
14601                         }
14602                     }
14603                 ]
14604             }
14605         ]
14606         
14607     }
14608 });
14609
14610 Roo.apply(Roo.bootstrap.ComboBox,  {
14611     
14612     touchViewTemplate : {
14613         tag: 'div',
14614         cls: 'modal fade roo-combobox-touch-view',
14615         cn: [
14616             {
14617                 tag: 'div',
14618                 cls: 'modal-dialog',
14619                 style : 'position:fixed', // we have to fix position....
14620                 cn: [
14621                     {
14622                         tag: 'div',
14623                         cls: 'modal-content',
14624                         cn: [
14625                             Roo.bootstrap.ComboBox.header,
14626                             Roo.bootstrap.ComboBox.body,
14627                             Roo.bootstrap.ComboBox.footer
14628                         ]
14629                     }
14630                 ]
14631             }
14632         ]
14633     }
14634 });/*
14635  * Based on:
14636  * Ext JS Library 1.1.1
14637  * Copyright(c) 2006-2007, Ext JS, LLC.
14638  *
14639  * Originally Released Under LGPL - original licence link has changed is not relivant.
14640  *
14641  * Fork - LGPL
14642  * <script type="text/javascript">
14643  */
14644
14645 /**
14646  * @class Roo.View
14647  * @extends Roo.util.Observable
14648  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14649  * This class also supports single and multi selection modes. <br>
14650  * Create a data model bound view:
14651  <pre><code>
14652  var store = new Roo.data.Store(...);
14653
14654  var view = new Roo.View({
14655     el : "my-element",
14656     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14657  
14658     singleSelect: true,
14659     selectedClass: "ydataview-selected",
14660     store: store
14661  });
14662
14663  // listen for node click?
14664  view.on("click", function(vw, index, node, e){
14665  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14666  });
14667
14668  // load XML data
14669  dataModel.load("foobar.xml");
14670  </code></pre>
14671  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14672  * <br><br>
14673  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14674  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14675  * 
14676  * Note: old style constructor is still suported (container, template, config)
14677  * 
14678  * @constructor
14679  * Create a new View
14680  * @param {Object} config The config object
14681  * 
14682  */
14683 Roo.View = function(config, depreciated_tpl, depreciated_config){
14684     
14685     this.parent = false;
14686     
14687     if (typeof(depreciated_tpl) == 'undefined') {
14688         // new way.. - universal constructor.
14689         Roo.apply(this, config);
14690         this.el  = Roo.get(this.el);
14691     } else {
14692         // old format..
14693         this.el  = Roo.get(config);
14694         this.tpl = depreciated_tpl;
14695         Roo.apply(this, depreciated_config);
14696     }
14697     this.wrapEl  = this.el.wrap().wrap();
14698     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14699     
14700     
14701     if(typeof(this.tpl) == "string"){
14702         this.tpl = new Roo.Template(this.tpl);
14703     } else {
14704         // support xtype ctors..
14705         this.tpl = new Roo.factory(this.tpl, Roo);
14706     }
14707     
14708     
14709     this.tpl.compile();
14710     
14711     /** @private */
14712     this.addEvents({
14713         /**
14714          * @event beforeclick
14715          * Fires before a click is processed. Returns false to cancel the default action.
14716          * @param {Roo.View} this
14717          * @param {Number} index The index of the target node
14718          * @param {HTMLElement} node The target node
14719          * @param {Roo.EventObject} e The raw event object
14720          */
14721             "beforeclick" : true,
14722         /**
14723          * @event click
14724          * Fires when a template node is clicked.
14725          * @param {Roo.View} this
14726          * @param {Number} index The index of the target node
14727          * @param {HTMLElement} node The target node
14728          * @param {Roo.EventObject} e The raw event object
14729          */
14730             "click" : true,
14731         /**
14732          * @event dblclick
14733          * Fires when a template node is double clicked.
14734          * @param {Roo.View} this
14735          * @param {Number} index The index of the target node
14736          * @param {HTMLElement} node The target node
14737          * @param {Roo.EventObject} e The raw event object
14738          */
14739             "dblclick" : true,
14740         /**
14741          * @event contextmenu
14742          * Fires when a template node is right clicked.
14743          * @param {Roo.View} this
14744          * @param {Number} index The index of the target node
14745          * @param {HTMLElement} node The target node
14746          * @param {Roo.EventObject} e The raw event object
14747          */
14748             "contextmenu" : true,
14749         /**
14750          * @event selectionchange
14751          * Fires when the selected nodes change.
14752          * @param {Roo.View} this
14753          * @param {Array} selections Array of the selected nodes
14754          */
14755             "selectionchange" : true,
14756     
14757         /**
14758          * @event beforeselect
14759          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14760          * @param {Roo.View} this
14761          * @param {HTMLElement} node The node to be selected
14762          * @param {Array} selections Array of currently selected nodes
14763          */
14764             "beforeselect" : true,
14765         /**
14766          * @event preparedata
14767          * Fires on every row to render, to allow you to change the data.
14768          * @param {Roo.View} this
14769          * @param {Object} data to be rendered (change this)
14770          */
14771           "preparedata" : true
14772           
14773           
14774         });
14775
14776
14777
14778     this.el.on({
14779         "click": this.onClick,
14780         "dblclick": this.onDblClick,
14781         "contextmenu": this.onContextMenu,
14782         scope:this
14783     });
14784
14785     this.selections = [];
14786     this.nodes = [];
14787     this.cmp = new Roo.CompositeElementLite([]);
14788     if(this.store){
14789         this.store = Roo.factory(this.store, Roo.data);
14790         this.setStore(this.store, true);
14791     }
14792     
14793     if ( this.footer && this.footer.xtype) {
14794            
14795          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14796         
14797         this.footer.dataSource = this.store;
14798         this.footer.container = fctr;
14799         this.footer = Roo.factory(this.footer, Roo);
14800         fctr.insertFirst(this.el);
14801         
14802         // this is a bit insane - as the paging toolbar seems to detach the el..
14803 //        dom.parentNode.parentNode.parentNode
14804          // they get detached?
14805     }
14806     
14807     
14808     Roo.View.superclass.constructor.call(this);
14809     
14810     
14811 };
14812
14813 Roo.extend(Roo.View, Roo.util.Observable, {
14814     
14815      /**
14816      * @cfg {Roo.data.Store} store Data store to load data from.
14817      */
14818     store : false,
14819     
14820     /**
14821      * @cfg {String|Roo.Element} el The container element.
14822      */
14823     el : '',
14824     
14825     /**
14826      * @cfg {String|Roo.Template} tpl The template used by this View 
14827      */
14828     tpl : false,
14829     /**
14830      * @cfg {String} dataName the named area of the template to use as the data area
14831      *                          Works with domtemplates roo-name="name"
14832      */
14833     dataName: false,
14834     /**
14835      * @cfg {String} selectedClass The css class to add to selected nodes
14836      */
14837     selectedClass : "x-view-selected",
14838      /**
14839      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14840      */
14841     emptyText : "",
14842     
14843     /**
14844      * @cfg {String} text to display on mask (default Loading)
14845      */
14846     mask : false,
14847     /**
14848      * @cfg {Boolean} multiSelect Allow multiple selection
14849      */
14850     multiSelect : false,
14851     /**
14852      * @cfg {Boolean} singleSelect Allow single selection
14853      */
14854     singleSelect:  false,
14855     
14856     /**
14857      * @cfg {Boolean} toggleSelect - selecting 
14858      */
14859     toggleSelect : false,
14860     
14861     /**
14862      * @cfg {Boolean} tickable - selecting 
14863      */
14864     tickable : false,
14865     
14866     /**
14867      * Returns the element this view is bound to.
14868      * @return {Roo.Element}
14869      */
14870     getEl : function(){
14871         return this.wrapEl;
14872     },
14873     
14874     
14875
14876     /**
14877      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14878      */
14879     refresh : function(){
14880         //Roo.log('refresh');
14881         var t = this.tpl;
14882         
14883         // if we are using something like 'domtemplate', then
14884         // the what gets used is:
14885         // t.applySubtemplate(NAME, data, wrapping data..)
14886         // the outer template then get' applied with
14887         //     the store 'extra data'
14888         // and the body get's added to the
14889         //      roo-name="data" node?
14890         //      <span class='roo-tpl-{name}'></span> ?????
14891         
14892         
14893         
14894         this.clearSelections();
14895         this.el.update("");
14896         var html = [];
14897         var records = this.store.getRange();
14898         if(records.length < 1) {
14899             
14900             // is this valid??  = should it render a template??
14901             
14902             this.el.update(this.emptyText);
14903             return;
14904         }
14905         var el = this.el;
14906         if (this.dataName) {
14907             this.el.update(t.apply(this.store.meta)); //????
14908             el = this.el.child('.roo-tpl-' + this.dataName);
14909         }
14910         
14911         for(var i = 0, len = records.length; i < len; i++){
14912             var data = this.prepareData(records[i].data, i, records[i]);
14913             this.fireEvent("preparedata", this, data, i, records[i]);
14914             
14915             var d = Roo.apply({}, data);
14916             
14917             if(this.tickable){
14918                 Roo.apply(d, {'roo-id' : Roo.id()});
14919                 
14920                 var _this = this;
14921             
14922                 Roo.each(this.parent.item, function(item){
14923                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14924                         return;
14925                     }
14926                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14927                 });
14928             }
14929             
14930             html[html.length] = Roo.util.Format.trim(
14931                 this.dataName ?
14932                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14933                     t.apply(d)
14934             );
14935         }
14936         
14937         
14938         
14939         el.update(html.join(""));
14940         this.nodes = el.dom.childNodes;
14941         this.updateIndexes(0);
14942     },
14943     
14944
14945     /**
14946      * Function to override to reformat the data that is sent to
14947      * the template for each node.
14948      * DEPRICATED - use the preparedata event handler.
14949      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14950      * a JSON object for an UpdateManager bound view).
14951      */
14952     prepareData : function(data, index, record)
14953     {
14954         this.fireEvent("preparedata", this, data, index, record);
14955         return data;
14956     },
14957
14958     onUpdate : function(ds, record){
14959         // Roo.log('on update');   
14960         this.clearSelections();
14961         var index = this.store.indexOf(record);
14962         var n = this.nodes[index];
14963         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14964         n.parentNode.removeChild(n);
14965         this.updateIndexes(index, index);
14966     },
14967
14968     
14969     
14970 // --------- FIXME     
14971     onAdd : function(ds, records, index)
14972     {
14973         //Roo.log(['on Add', ds, records, index] );        
14974         this.clearSelections();
14975         if(this.nodes.length == 0){
14976             this.refresh();
14977             return;
14978         }
14979         var n = this.nodes[index];
14980         for(var i = 0, len = records.length; i < len; i++){
14981             var d = this.prepareData(records[i].data, i, records[i]);
14982             if(n){
14983                 this.tpl.insertBefore(n, d);
14984             }else{
14985                 
14986                 this.tpl.append(this.el, d);
14987             }
14988         }
14989         this.updateIndexes(index);
14990     },
14991
14992     onRemove : function(ds, record, index){
14993        // Roo.log('onRemove');
14994         this.clearSelections();
14995         var el = this.dataName  ?
14996             this.el.child('.roo-tpl-' + this.dataName) :
14997             this.el; 
14998         
14999         el.dom.removeChild(this.nodes[index]);
15000         this.updateIndexes(index);
15001     },
15002
15003     /**
15004      * Refresh an individual node.
15005      * @param {Number} index
15006      */
15007     refreshNode : function(index){
15008         this.onUpdate(this.store, this.store.getAt(index));
15009     },
15010
15011     updateIndexes : function(startIndex, endIndex){
15012         var ns = this.nodes;
15013         startIndex = startIndex || 0;
15014         endIndex = endIndex || ns.length - 1;
15015         for(var i = startIndex; i <= endIndex; i++){
15016             ns[i].nodeIndex = i;
15017         }
15018     },
15019
15020     /**
15021      * Changes the data store this view uses and refresh the view.
15022      * @param {Store} store
15023      */
15024     setStore : function(store, initial){
15025         if(!initial && this.store){
15026             this.store.un("datachanged", this.refresh);
15027             this.store.un("add", this.onAdd);
15028             this.store.un("remove", this.onRemove);
15029             this.store.un("update", this.onUpdate);
15030             this.store.un("clear", this.refresh);
15031             this.store.un("beforeload", this.onBeforeLoad);
15032             this.store.un("load", this.onLoad);
15033             this.store.un("loadexception", this.onLoad);
15034         }
15035         if(store){
15036           
15037             store.on("datachanged", this.refresh, this);
15038             store.on("add", this.onAdd, this);
15039             store.on("remove", this.onRemove, this);
15040             store.on("update", this.onUpdate, this);
15041             store.on("clear", this.refresh, this);
15042             store.on("beforeload", this.onBeforeLoad, this);
15043             store.on("load", this.onLoad, this);
15044             store.on("loadexception", this.onLoad, this);
15045         }
15046         
15047         if(store){
15048             this.refresh();
15049         }
15050     },
15051     /**
15052      * onbeforeLoad - masks the loading area.
15053      *
15054      */
15055     onBeforeLoad : function(store,opts)
15056     {
15057          //Roo.log('onBeforeLoad');   
15058         if (!opts.add) {
15059             this.el.update("");
15060         }
15061         this.el.mask(this.mask ? this.mask : "Loading" ); 
15062     },
15063     onLoad : function ()
15064     {
15065         this.el.unmask();
15066     },
15067     
15068
15069     /**
15070      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15071      * @param {HTMLElement} node
15072      * @return {HTMLElement} The template node
15073      */
15074     findItemFromChild : function(node){
15075         var el = this.dataName  ?
15076             this.el.child('.roo-tpl-' + this.dataName,true) :
15077             this.el.dom; 
15078         
15079         if(!node || node.parentNode == el){
15080                     return node;
15081             }
15082             var p = node.parentNode;
15083             while(p && p != el){
15084             if(p.parentNode == el){
15085                 return p;
15086             }
15087             p = p.parentNode;
15088         }
15089             return null;
15090     },
15091
15092     /** @ignore */
15093     onClick : function(e){
15094         var item = this.findItemFromChild(e.getTarget());
15095         if(item){
15096             var index = this.indexOf(item);
15097             if(this.onItemClick(item, index, e) !== false){
15098                 this.fireEvent("click", this, index, item, e);
15099             }
15100         }else{
15101             this.clearSelections();
15102         }
15103     },
15104
15105     /** @ignore */
15106     onContextMenu : function(e){
15107         var item = this.findItemFromChild(e.getTarget());
15108         if(item){
15109             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15110         }
15111     },
15112
15113     /** @ignore */
15114     onDblClick : function(e){
15115         var item = this.findItemFromChild(e.getTarget());
15116         if(item){
15117             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15118         }
15119     },
15120
15121     onItemClick : function(item, index, e)
15122     {
15123         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15124             return false;
15125         }
15126         if (this.toggleSelect) {
15127             var m = this.isSelected(item) ? 'unselect' : 'select';
15128             //Roo.log(m);
15129             var _t = this;
15130             _t[m](item, true, false);
15131             return true;
15132         }
15133         if(this.multiSelect || this.singleSelect){
15134             if(this.multiSelect && e.shiftKey && this.lastSelection){
15135                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15136             }else{
15137                 this.select(item, this.multiSelect && e.ctrlKey);
15138                 this.lastSelection = item;
15139             }
15140             
15141             if(!this.tickable){
15142                 e.preventDefault();
15143             }
15144             
15145         }
15146         return true;
15147     },
15148
15149     /**
15150      * Get the number of selected nodes.
15151      * @return {Number}
15152      */
15153     getSelectionCount : function(){
15154         return this.selections.length;
15155     },
15156
15157     /**
15158      * Get the currently selected nodes.
15159      * @return {Array} An array of HTMLElements
15160      */
15161     getSelectedNodes : function(){
15162         return this.selections;
15163     },
15164
15165     /**
15166      * Get the indexes of the selected nodes.
15167      * @return {Array}
15168      */
15169     getSelectedIndexes : function(){
15170         var indexes = [], s = this.selections;
15171         for(var i = 0, len = s.length; i < len; i++){
15172             indexes.push(s[i].nodeIndex);
15173         }
15174         return indexes;
15175     },
15176
15177     /**
15178      * Clear all selections
15179      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15180      */
15181     clearSelections : function(suppressEvent){
15182         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15183             this.cmp.elements = this.selections;
15184             this.cmp.removeClass(this.selectedClass);
15185             this.selections = [];
15186             if(!suppressEvent){
15187                 this.fireEvent("selectionchange", this, this.selections);
15188             }
15189         }
15190     },
15191
15192     /**
15193      * Returns true if the passed node is selected
15194      * @param {HTMLElement/Number} node The node or node index
15195      * @return {Boolean}
15196      */
15197     isSelected : function(node){
15198         var s = this.selections;
15199         if(s.length < 1){
15200             return false;
15201         }
15202         node = this.getNode(node);
15203         return s.indexOf(node) !== -1;
15204     },
15205
15206     /**
15207      * Selects nodes.
15208      * @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
15209      * @param {Boolean} keepExisting (optional) true to keep existing selections
15210      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15211      */
15212     select : function(nodeInfo, keepExisting, suppressEvent){
15213         if(nodeInfo instanceof Array){
15214             if(!keepExisting){
15215                 this.clearSelections(true);
15216             }
15217             for(var i = 0, len = nodeInfo.length; i < len; i++){
15218                 this.select(nodeInfo[i], true, true);
15219             }
15220             return;
15221         } 
15222         var node = this.getNode(nodeInfo);
15223         if(!node || this.isSelected(node)){
15224             return; // already selected.
15225         }
15226         if(!keepExisting){
15227             this.clearSelections(true);
15228         }
15229         
15230         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15231             Roo.fly(node).addClass(this.selectedClass);
15232             this.selections.push(node);
15233             if(!suppressEvent){
15234                 this.fireEvent("selectionchange", this, this.selections);
15235             }
15236         }
15237         
15238         
15239     },
15240       /**
15241      * Unselects nodes.
15242      * @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
15243      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15244      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15245      */
15246     unselect : function(nodeInfo, keepExisting, suppressEvent)
15247     {
15248         if(nodeInfo instanceof Array){
15249             Roo.each(this.selections, function(s) {
15250                 this.unselect(s, nodeInfo);
15251             }, this);
15252             return;
15253         }
15254         var node = this.getNode(nodeInfo);
15255         if(!node || !this.isSelected(node)){
15256             //Roo.log("not selected");
15257             return; // not selected.
15258         }
15259         // fireevent???
15260         var ns = [];
15261         Roo.each(this.selections, function(s) {
15262             if (s == node ) {
15263                 Roo.fly(node).removeClass(this.selectedClass);
15264
15265                 return;
15266             }
15267             ns.push(s);
15268         },this);
15269         
15270         this.selections= ns;
15271         this.fireEvent("selectionchange", this, this.selections);
15272     },
15273
15274     /**
15275      * Gets a template node.
15276      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15277      * @return {HTMLElement} The node or null if it wasn't found
15278      */
15279     getNode : function(nodeInfo){
15280         if(typeof nodeInfo == "string"){
15281             return document.getElementById(nodeInfo);
15282         }else if(typeof nodeInfo == "number"){
15283             return this.nodes[nodeInfo];
15284         }
15285         return nodeInfo;
15286     },
15287
15288     /**
15289      * Gets a range template nodes.
15290      * @param {Number} startIndex
15291      * @param {Number} endIndex
15292      * @return {Array} An array of nodes
15293      */
15294     getNodes : function(start, end){
15295         var ns = this.nodes;
15296         start = start || 0;
15297         end = typeof end == "undefined" ? ns.length - 1 : end;
15298         var nodes = [];
15299         if(start <= end){
15300             for(var i = start; i <= end; i++){
15301                 nodes.push(ns[i]);
15302             }
15303         } else{
15304             for(var i = start; i >= end; i--){
15305                 nodes.push(ns[i]);
15306             }
15307         }
15308         return nodes;
15309     },
15310
15311     /**
15312      * Finds the index of the passed node
15313      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15314      * @return {Number} The index of the node or -1
15315      */
15316     indexOf : function(node){
15317         node = this.getNode(node);
15318         if(typeof node.nodeIndex == "number"){
15319             return node.nodeIndex;
15320         }
15321         var ns = this.nodes;
15322         for(var i = 0, len = ns.length; i < len; i++){
15323             if(ns[i] == node){
15324                 return i;
15325             }
15326         }
15327         return -1;
15328     }
15329 });
15330 /*
15331  * - LGPL
15332  *
15333  * based on jquery fullcalendar
15334  * 
15335  */
15336
15337 Roo.bootstrap = Roo.bootstrap || {};
15338 /**
15339  * @class Roo.bootstrap.Calendar
15340  * @extends Roo.bootstrap.Component
15341  * Bootstrap Calendar class
15342  * @cfg {Boolean} loadMask (true|false) default false
15343  * @cfg {Object} header generate the user specific header of the calendar, default false
15344
15345  * @constructor
15346  * Create a new Container
15347  * @param {Object} config The config object
15348  */
15349
15350
15351
15352 Roo.bootstrap.Calendar = function(config){
15353     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15354      this.addEvents({
15355         /**
15356              * @event select
15357              * Fires when a date is selected
15358              * @param {DatePicker} this
15359              * @param {Date} date The selected date
15360              */
15361         'select': true,
15362         /**
15363              * @event monthchange
15364              * Fires when the displayed month changes 
15365              * @param {DatePicker} this
15366              * @param {Date} date The selected month
15367              */
15368         'monthchange': true,
15369         /**
15370              * @event evententer
15371              * Fires when mouse over an event
15372              * @param {Calendar} this
15373              * @param {event} Event
15374              */
15375         'evententer': true,
15376         /**
15377              * @event eventleave
15378              * Fires when the mouse leaves an
15379              * @param {Calendar} this
15380              * @param {event}
15381              */
15382         'eventleave': true,
15383         /**
15384              * @event eventclick
15385              * Fires when the mouse click an
15386              * @param {Calendar} this
15387              * @param {event}
15388              */
15389         'eventclick': true
15390         
15391     });
15392
15393 };
15394
15395 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15396     
15397      /**
15398      * @cfg {Number} startDay
15399      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15400      */
15401     startDay : 0,
15402     
15403     loadMask : false,
15404     
15405     header : false,
15406       
15407     getAutoCreate : function(){
15408         
15409         
15410         var fc_button = function(name, corner, style, content ) {
15411             return Roo.apply({},{
15412                 tag : 'span',
15413                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15414                          (corner.length ?
15415                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15416                             ''
15417                         ),
15418                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15419                 unselectable: 'on'
15420             });
15421         };
15422         
15423         var header = {};
15424         
15425         if(!this.header){
15426             header = {
15427                 tag : 'table',
15428                 cls : 'fc-header',
15429                 style : 'width:100%',
15430                 cn : [
15431                     {
15432                         tag: 'tr',
15433                         cn : [
15434                             {
15435                                 tag : 'td',
15436                                 cls : 'fc-header-left',
15437                                 cn : [
15438                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15439                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15440                                     { tag: 'span', cls: 'fc-header-space' },
15441                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15442
15443
15444                                 ]
15445                             },
15446
15447                             {
15448                                 tag : 'td',
15449                                 cls : 'fc-header-center',
15450                                 cn : [
15451                                     {
15452                                         tag: 'span',
15453                                         cls: 'fc-header-title',
15454                                         cn : {
15455                                             tag: 'H2',
15456                                             html : 'month / year'
15457                                         }
15458                                     }
15459
15460                                 ]
15461                             },
15462                             {
15463                                 tag : 'td',
15464                                 cls : 'fc-header-right',
15465                                 cn : [
15466                               /*      fc_button('month', 'left', '', 'month' ),
15467                                     fc_button('week', '', '', 'week' ),
15468                                     fc_button('day', 'right', '', 'day' )
15469                                 */    
15470
15471                                 ]
15472                             }
15473
15474                         ]
15475                     }
15476                 ]
15477             };
15478         }
15479         
15480         header = this.header;
15481         
15482        
15483         var cal_heads = function() {
15484             var ret = [];
15485             // fixme - handle this.
15486             
15487             for (var i =0; i < Date.dayNames.length; i++) {
15488                 var d = Date.dayNames[i];
15489                 ret.push({
15490                     tag: 'th',
15491                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15492                     html : d.substring(0,3)
15493                 });
15494                 
15495             }
15496             ret[0].cls += ' fc-first';
15497             ret[6].cls += ' fc-last';
15498             return ret;
15499         };
15500         var cal_cell = function(n) {
15501             return  {
15502                 tag: 'td',
15503                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15504                 cn : [
15505                     {
15506                         cn : [
15507                             {
15508                                 cls: 'fc-day-number',
15509                                 html: 'D'
15510                             },
15511                             {
15512                                 cls: 'fc-day-content',
15513                              
15514                                 cn : [
15515                                      {
15516                                         style: 'position: relative;' // height: 17px;
15517                                     }
15518                                 ]
15519                             }
15520                             
15521                             
15522                         ]
15523                     }
15524                 ]
15525                 
15526             }
15527         };
15528         var cal_rows = function() {
15529             
15530             var ret = [];
15531             for (var r = 0; r < 6; r++) {
15532                 var row= {
15533                     tag : 'tr',
15534                     cls : 'fc-week',
15535                     cn : []
15536                 };
15537                 
15538                 for (var i =0; i < Date.dayNames.length; i++) {
15539                     var d = Date.dayNames[i];
15540                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15541
15542                 }
15543                 row.cn[0].cls+=' fc-first';
15544                 row.cn[0].cn[0].style = 'min-height:90px';
15545                 row.cn[6].cls+=' fc-last';
15546                 ret.push(row);
15547                 
15548             }
15549             ret[0].cls += ' fc-first';
15550             ret[4].cls += ' fc-prev-last';
15551             ret[5].cls += ' fc-last';
15552             return ret;
15553             
15554         };
15555         
15556         var cal_table = {
15557             tag: 'table',
15558             cls: 'fc-border-separate',
15559             style : 'width:100%',
15560             cellspacing  : 0,
15561             cn : [
15562                 { 
15563                     tag: 'thead',
15564                     cn : [
15565                         { 
15566                             tag: 'tr',
15567                             cls : 'fc-first fc-last',
15568                             cn : cal_heads()
15569                         }
15570                     ]
15571                 },
15572                 { 
15573                     tag: 'tbody',
15574                     cn : cal_rows()
15575                 }
15576                   
15577             ]
15578         };
15579          
15580          var cfg = {
15581             cls : 'fc fc-ltr',
15582             cn : [
15583                 header,
15584                 {
15585                     cls : 'fc-content',
15586                     style : "position: relative;",
15587                     cn : [
15588                         {
15589                             cls : 'fc-view fc-view-month fc-grid',
15590                             style : 'position: relative',
15591                             unselectable : 'on',
15592                             cn : [
15593                                 {
15594                                     cls : 'fc-event-container',
15595                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15596                                 },
15597                                 cal_table
15598                             ]
15599                         }
15600                     ]
15601     
15602                 }
15603            ] 
15604             
15605         };
15606         
15607          
15608         
15609         return cfg;
15610     },
15611     
15612     
15613     initEvents : function()
15614     {
15615         if(!this.store){
15616             throw "can not find store for calendar";
15617         }
15618         
15619         var mark = {
15620             tag: "div",
15621             cls:"x-dlg-mask",
15622             style: "text-align:center",
15623             cn: [
15624                 {
15625                     tag: "div",
15626                     style: "background-color:white;width:50%;margin:250 auto",
15627                     cn: [
15628                         {
15629                             tag: "img",
15630                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15631                         },
15632                         {
15633                             tag: "span",
15634                             html: "Loading"
15635                         }
15636                         
15637                     ]
15638                 }
15639             ]
15640         };
15641         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15642         
15643         var size = this.el.select('.fc-content', true).first().getSize();
15644         this.maskEl.setSize(size.width, size.height);
15645         this.maskEl.enableDisplayMode("block");
15646         if(!this.loadMask){
15647             this.maskEl.hide();
15648         }
15649         
15650         this.store = Roo.factory(this.store, Roo.data);
15651         this.store.on('load', this.onLoad, this);
15652         this.store.on('beforeload', this.onBeforeLoad, this);
15653         
15654         this.resize();
15655         
15656         this.cells = this.el.select('.fc-day',true);
15657         //Roo.log(this.cells);
15658         this.textNodes = this.el.query('.fc-day-number');
15659         this.cells.addClassOnOver('fc-state-hover');
15660         
15661         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15662         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15663         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15664         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15665         
15666         this.on('monthchange', this.onMonthChange, this);
15667         
15668         this.update(new Date().clearTime());
15669     },
15670     
15671     resize : function() {
15672         var sz  = this.el.getSize();
15673         
15674         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15675         this.el.select('.fc-day-content div',true).setHeight(34);
15676     },
15677     
15678     
15679     // private
15680     showPrevMonth : function(e){
15681         this.update(this.activeDate.add("mo", -1));
15682     },
15683     showToday : function(e){
15684         this.update(new Date().clearTime());
15685     },
15686     // private
15687     showNextMonth : function(e){
15688         this.update(this.activeDate.add("mo", 1));
15689     },
15690
15691     // private
15692     showPrevYear : function(){
15693         this.update(this.activeDate.add("y", -1));
15694     },
15695
15696     // private
15697     showNextYear : function(){
15698         this.update(this.activeDate.add("y", 1));
15699     },
15700
15701     
15702    // private
15703     update : function(date)
15704     {
15705         var vd = this.activeDate;
15706         this.activeDate = date;
15707 //        if(vd && this.el){
15708 //            var t = date.getTime();
15709 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15710 //                Roo.log('using add remove');
15711 //                
15712 //                this.fireEvent('monthchange', this, date);
15713 //                
15714 //                this.cells.removeClass("fc-state-highlight");
15715 //                this.cells.each(function(c){
15716 //                   if(c.dateValue == t){
15717 //                       c.addClass("fc-state-highlight");
15718 //                       setTimeout(function(){
15719 //                            try{c.dom.firstChild.focus();}catch(e){}
15720 //                       }, 50);
15721 //                       return false;
15722 //                   }
15723 //                   return true;
15724 //                });
15725 //                return;
15726 //            }
15727 //        }
15728         
15729         var days = date.getDaysInMonth();
15730         
15731         var firstOfMonth = date.getFirstDateOfMonth();
15732         var startingPos = firstOfMonth.getDay()-this.startDay;
15733         
15734         if(startingPos < this.startDay){
15735             startingPos += 7;
15736         }
15737         
15738         var pm = date.add(Date.MONTH, -1);
15739         var prevStart = pm.getDaysInMonth()-startingPos;
15740 //        
15741         this.cells = this.el.select('.fc-day',true);
15742         this.textNodes = this.el.query('.fc-day-number');
15743         this.cells.addClassOnOver('fc-state-hover');
15744         
15745         var cells = this.cells.elements;
15746         var textEls = this.textNodes;
15747         
15748         Roo.each(cells, function(cell){
15749             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15750         });
15751         
15752         days += startingPos;
15753
15754         // convert everything to numbers so it's fast
15755         var day = 86400000;
15756         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15757         //Roo.log(d);
15758         //Roo.log(pm);
15759         //Roo.log(prevStart);
15760         
15761         var today = new Date().clearTime().getTime();
15762         var sel = date.clearTime().getTime();
15763         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15764         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15765         var ddMatch = this.disabledDatesRE;
15766         var ddText = this.disabledDatesText;
15767         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15768         var ddaysText = this.disabledDaysText;
15769         var format = this.format;
15770         
15771         var setCellClass = function(cal, cell){
15772             cell.row = 0;
15773             cell.events = [];
15774             cell.more = [];
15775             //Roo.log('set Cell Class');
15776             cell.title = "";
15777             var t = d.getTime();
15778             
15779             //Roo.log(d);
15780             
15781             cell.dateValue = t;
15782             if(t == today){
15783                 cell.className += " fc-today";
15784                 cell.className += " fc-state-highlight";
15785                 cell.title = cal.todayText;
15786             }
15787             if(t == sel){
15788                 // disable highlight in other month..
15789                 //cell.className += " fc-state-highlight";
15790                 
15791             }
15792             // disabling
15793             if(t < min) {
15794                 cell.className = " fc-state-disabled";
15795                 cell.title = cal.minText;
15796                 return;
15797             }
15798             if(t > max) {
15799                 cell.className = " fc-state-disabled";
15800                 cell.title = cal.maxText;
15801                 return;
15802             }
15803             if(ddays){
15804                 if(ddays.indexOf(d.getDay()) != -1){
15805                     cell.title = ddaysText;
15806                     cell.className = " fc-state-disabled";
15807                 }
15808             }
15809             if(ddMatch && format){
15810                 var fvalue = d.dateFormat(format);
15811                 if(ddMatch.test(fvalue)){
15812                     cell.title = ddText.replace("%0", fvalue);
15813                     cell.className = " fc-state-disabled";
15814                 }
15815             }
15816             
15817             if (!cell.initialClassName) {
15818                 cell.initialClassName = cell.dom.className;
15819             }
15820             
15821             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15822         };
15823
15824         var i = 0;
15825         
15826         for(; i < startingPos; i++) {
15827             textEls[i].innerHTML = (++prevStart);
15828             d.setDate(d.getDate()+1);
15829             
15830             cells[i].className = "fc-past fc-other-month";
15831             setCellClass(this, cells[i]);
15832         }
15833         
15834         var intDay = 0;
15835         
15836         for(; i < days; i++){
15837             intDay = i - startingPos + 1;
15838             textEls[i].innerHTML = (intDay);
15839             d.setDate(d.getDate()+1);
15840             
15841             cells[i].className = ''; // "x-date-active";
15842             setCellClass(this, cells[i]);
15843         }
15844         var extraDays = 0;
15845         
15846         for(; i < 42; i++) {
15847             textEls[i].innerHTML = (++extraDays);
15848             d.setDate(d.getDate()+1);
15849             
15850             cells[i].className = "fc-future fc-other-month";
15851             setCellClass(this, cells[i]);
15852         }
15853         
15854         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15855         
15856         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15857         
15858         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15859         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15860         
15861         if(totalRows != 6){
15862             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15863             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15864         }
15865         
15866         this.fireEvent('monthchange', this, date);
15867         
15868         
15869         /*
15870         if(!this.internalRender){
15871             var main = this.el.dom.firstChild;
15872             var w = main.offsetWidth;
15873             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15874             Roo.fly(main).setWidth(w);
15875             this.internalRender = true;
15876             // opera does not respect the auto grow header center column
15877             // then, after it gets a width opera refuses to recalculate
15878             // without a second pass
15879             if(Roo.isOpera && !this.secondPass){
15880                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15881                 this.secondPass = true;
15882                 this.update.defer(10, this, [date]);
15883             }
15884         }
15885         */
15886         
15887     },
15888     
15889     findCell : function(dt) {
15890         dt = dt.clearTime().getTime();
15891         var ret = false;
15892         this.cells.each(function(c){
15893             //Roo.log("check " +c.dateValue + '?=' + dt);
15894             if(c.dateValue == dt){
15895                 ret = c;
15896                 return false;
15897             }
15898             return true;
15899         });
15900         
15901         return ret;
15902     },
15903     
15904     findCells : function(ev) {
15905         var s = ev.start.clone().clearTime().getTime();
15906        // Roo.log(s);
15907         var e= ev.end.clone().clearTime().getTime();
15908        // Roo.log(e);
15909         var ret = [];
15910         this.cells.each(function(c){
15911              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15912             
15913             if(c.dateValue > e){
15914                 return ;
15915             }
15916             if(c.dateValue < s){
15917                 return ;
15918             }
15919             ret.push(c);
15920         });
15921         
15922         return ret;    
15923     },
15924     
15925 //    findBestRow: function(cells)
15926 //    {
15927 //        var ret = 0;
15928 //        
15929 //        for (var i =0 ; i < cells.length;i++) {
15930 //            ret  = Math.max(cells[i].rows || 0,ret);
15931 //        }
15932 //        return ret;
15933 //        
15934 //    },
15935     
15936     
15937     addItem : function(ev)
15938     {
15939         // look for vertical location slot in
15940         var cells = this.findCells(ev);
15941         
15942 //        ev.row = this.findBestRow(cells);
15943         
15944         // work out the location.
15945         
15946         var crow = false;
15947         var rows = [];
15948         for(var i =0; i < cells.length; i++) {
15949             
15950             cells[i].row = cells[0].row;
15951             
15952             if(i == 0){
15953                 cells[i].row = cells[i].row + 1;
15954             }
15955             
15956             if (!crow) {
15957                 crow = {
15958                     start : cells[i],
15959                     end :  cells[i]
15960                 };
15961                 continue;
15962             }
15963             if (crow.start.getY() == cells[i].getY()) {
15964                 // on same row.
15965                 crow.end = cells[i];
15966                 continue;
15967             }
15968             // different row.
15969             rows.push(crow);
15970             crow = {
15971                 start: cells[i],
15972                 end : cells[i]
15973             };
15974             
15975         }
15976         
15977         rows.push(crow);
15978         ev.els = [];
15979         ev.rows = rows;
15980         ev.cells = cells;
15981         
15982         cells[0].events.push(ev);
15983         
15984         this.calevents.push(ev);
15985     },
15986     
15987     clearEvents: function() {
15988         
15989         if(!this.calevents){
15990             return;
15991         }
15992         
15993         Roo.each(this.cells.elements, function(c){
15994             c.row = 0;
15995             c.events = [];
15996             c.more = [];
15997         });
15998         
15999         Roo.each(this.calevents, function(e) {
16000             Roo.each(e.els, function(el) {
16001                 el.un('mouseenter' ,this.onEventEnter, this);
16002                 el.un('mouseleave' ,this.onEventLeave, this);
16003                 el.remove();
16004             },this);
16005         },this);
16006         
16007         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16008             e.remove();
16009         });
16010         
16011     },
16012     
16013     renderEvents: function()
16014     {   
16015         var _this = this;
16016         
16017         this.cells.each(function(c) {
16018             
16019             if(c.row < 5){
16020                 return;
16021             }
16022             
16023             var ev = c.events;
16024             
16025             var r = 4;
16026             if(c.row != c.events.length){
16027                 r = 4 - (4 - (c.row - c.events.length));
16028             }
16029             
16030             c.events = ev.slice(0, r);
16031             c.more = ev.slice(r);
16032             
16033             if(c.more.length && c.more.length == 1){
16034                 c.events.push(c.more.pop());
16035             }
16036             
16037             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16038             
16039         });
16040             
16041         this.cells.each(function(c) {
16042             
16043             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16044             
16045             
16046             for (var e = 0; e < c.events.length; e++){
16047                 var ev = c.events[e];
16048                 var rows = ev.rows;
16049                 
16050                 for(var i = 0; i < rows.length; i++) {
16051                 
16052                     // how many rows should it span..
16053
16054                     var  cfg = {
16055                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16056                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16057
16058                         unselectable : "on",
16059                         cn : [
16060                             {
16061                                 cls: 'fc-event-inner',
16062                                 cn : [
16063     //                                {
16064     //                                  tag:'span',
16065     //                                  cls: 'fc-event-time',
16066     //                                  html : cells.length > 1 ? '' : ev.time
16067     //                                },
16068                                     {
16069                                       tag:'span',
16070                                       cls: 'fc-event-title',
16071                                       html : String.format('{0}', ev.title)
16072                                     }
16073
16074
16075                                 ]
16076                             },
16077                             {
16078                                 cls: 'ui-resizable-handle ui-resizable-e',
16079                                 html : '&nbsp;&nbsp;&nbsp'
16080                             }
16081
16082                         ]
16083                     };
16084
16085                     if (i == 0) {
16086                         cfg.cls += ' fc-event-start';
16087                     }
16088                     if ((i+1) == rows.length) {
16089                         cfg.cls += ' fc-event-end';
16090                     }
16091
16092                     var ctr = _this.el.select('.fc-event-container',true).first();
16093                     var cg = ctr.createChild(cfg);
16094
16095                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16096                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16097
16098                     var r = (c.more.length) ? 1 : 0;
16099                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16100                     cg.setWidth(ebox.right - sbox.x -2);
16101
16102                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16103                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16104                     cg.on('click', _this.onEventClick, _this, ev);
16105
16106                     ev.els.push(cg);
16107                     
16108                 }
16109                 
16110             }
16111             
16112             
16113             if(c.more.length){
16114                 var  cfg = {
16115                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16116                     style : 'position: absolute',
16117                     unselectable : "on",
16118                     cn : [
16119                         {
16120                             cls: 'fc-event-inner',
16121                             cn : [
16122                                 {
16123                                   tag:'span',
16124                                   cls: 'fc-event-title',
16125                                   html : 'More'
16126                                 }
16127
16128
16129                             ]
16130                         },
16131                         {
16132                             cls: 'ui-resizable-handle ui-resizable-e',
16133                             html : '&nbsp;&nbsp;&nbsp'
16134                         }
16135
16136                     ]
16137                 };
16138
16139                 var ctr = _this.el.select('.fc-event-container',true).first();
16140                 var cg = ctr.createChild(cfg);
16141
16142                 var sbox = c.select('.fc-day-content',true).first().getBox();
16143                 var ebox = c.select('.fc-day-content',true).first().getBox();
16144                 //Roo.log(cg);
16145                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16146                 cg.setWidth(ebox.right - sbox.x -2);
16147
16148                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16149                 
16150             }
16151             
16152         });
16153         
16154         
16155         
16156     },
16157     
16158     onEventEnter: function (e, el,event,d) {
16159         this.fireEvent('evententer', this, el, event);
16160     },
16161     
16162     onEventLeave: function (e, el,event,d) {
16163         this.fireEvent('eventleave', this, el, event);
16164     },
16165     
16166     onEventClick: function (e, el,event,d) {
16167         this.fireEvent('eventclick', this, el, event);
16168     },
16169     
16170     onMonthChange: function () {
16171         this.store.load();
16172     },
16173     
16174     onMoreEventClick: function(e, el, more)
16175     {
16176         var _this = this;
16177         
16178         this.calpopover.placement = 'right';
16179         this.calpopover.setTitle('More');
16180         
16181         this.calpopover.setContent('');
16182         
16183         var ctr = this.calpopover.el.select('.popover-content', true).first();
16184         
16185         Roo.each(more, function(m){
16186             var cfg = {
16187                 cls : 'fc-event-hori fc-event-draggable',
16188                 html : m.title
16189             };
16190             var cg = ctr.createChild(cfg);
16191             
16192             cg.on('click', _this.onEventClick, _this, m);
16193         });
16194         
16195         this.calpopover.show(el);
16196         
16197         
16198     },
16199     
16200     onLoad: function () 
16201     {   
16202         this.calevents = [];
16203         var cal = this;
16204         
16205         if(this.store.getCount() > 0){
16206             this.store.data.each(function(d){
16207                cal.addItem({
16208                     id : d.data.id,
16209                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16210                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16211                     time : d.data.start_time,
16212                     title : d.data.title,
16213                     description : d.data.description,
16214                     venue : d.data.venue
16215                 });
16216             });
16217         }
16218         
16219         this.renderEvents();
16220         
16221         if(this.calevents.length && this.loadMask){
16222             this.maskEl.hide();
16223         }
16224     },
16225     
16226     onBeforeLoad: function()
16227     {
16228         this.clearEvents();
16229         if(this.loadMask){
16230             this.maskEl.show();
16231         }
16232     }
16233 });
16234
16235  
16236  /*
16237  * - LGPL
16238  *
16239  * element
16240  * 
16241  */
16242
16243 /**
16244  * @class Roo.bootstrap.Popover
16245  * @extends Roo.bootstrap.Component
16246  * Bootstrap Popover class
16247  * @cfg {String} html contents of the popover   (or false to use children..)
16248  * @cfg {String} title of popover (or false to hide)
16249  * @cfg {String} placement how it is placed
16250  * @cfg {String} trigger click || hover (or false to trigger manually)
16251  * @cfg {String} over what (parent or false to trigger manually.)
16252  * @cfg {Number} delay - delay before showing
16253  
16254  * @constructor
16255  * Create a new Popover
16256  * @param {Object} config The config object
16257  */
16258
16259 Roo.bootstrap.Popover = function(config){
16260     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16261     
16262     this.addEvents({
16263         // raw events
16264          /**
16265          * @event show
16266          * After the popover show
16267          * 
16268          * @param {Roo.bootstrap.Popover} this
16269          */
16270         "show" : true,
16271         /**
16272          * @event hide
16273          * After the popover hide
16274          * 
16275          * @param {Roo.bootstrap.Popover} this
16276          */
16277         "hide" : true
16278     });
16279 };
16280
16281 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16282     
16283     title: 'Fill in a title',
16284     html: false,
16285     
16286     placement : 'right',
16287     trigger : 'hover', // hover
16288     
16289     delay : 0,
16290     
16291     over: 'parent',
16292     
16293     can_build_overlaid : false,
16294     
16295     getChildContainer : function()
16296     {
16297         return this.el.select('.popover-content',true).first();
16298     },
16299     
16300     getAutoCreate : function(){
16301          
16302         var cfg = {
16303            cls : 'popover roo-dynamic',
16304            style: 'display:block',
16305            cn : [
16306                 {
16307                     cls : 'arrow'
16308                 },
16309                 {
16310                     cls : 'popover-inner',
16311                     cn : [
16312                         {
16313                             tag: 'h3',
16314                             cls: 'popover-title',
16315                             html : this.title
16316                         },
16317                         {
16318                             cls : 'popover-content',
16319                             html : this.html
16320                         }
16321                     ]
16322                     
16323                 }
16324            ]
16325         };
16326         
16327         return cfg;
16328     },
16329     setTitle: function(str)
16330     {
16331         this.title = str;
16332         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16333     },
16334     setContent: function(str)
16335     {
16336         this.html = str;
16337         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16338     },
16339     // as it get's added to the bottom of the page.
16340     onRender : function(ct, position)
16341     {
16342         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16343         if(!this.el){
16344             var cfg = Roo.apply({},  this.getAutoCreate());
16345             cfg.id = Roo.id();
16346             
16347             if (this.cls) {
16348                 cfg.cls += ' ' + this.cls;
16349             }
16350             if (this.style) {
16351                 cfg.style = this.style;
16352             }
16353             //Roo.log("adding to ");
16354             this.el = Roo.get(document.body).createChild(cfg, position);
16355 //            Roo.log(this.el);
16356         }
16357         this.initEvents();
16358     },
16359     
16360     initEvents : function()
16361     {
16362         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16363         this.el.enableDisplayMode('block');
16364         this.el.hide();
16365         if (this.over === false) {
16366             return; 
16367         }
16368         if (this.triggers === false) {
16369             return;
16370         }
16371         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16372         var triggers = this.trigger ? this.trigger.split(' ') : [];
16373         Roo.each(triggers, function(trigger) {
16374         
16375             if (trigger == 'click') {
16376                 on_el.on('click', this.toggle, this);
16377             } else if (trigger != 'manual') {
16378                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16379                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16380       
16381                 on_el.on(eventIn  ,this.enter, this);
16382                 on_el.on(eventOut, this.leave, this);
16383             }
16384         }, this);
16385         
16386     },
16387     
16388     
16389     // private
16390     timeout : null,
16391     hoverState : null,
16392     
16393     toggle : function () {
16394         this.hoverState == 'in' ? this.leave() : this.enter();
16395     },
16396     
16397     enter : function () {
16398         
16399         clearTimeout(this.timeout);
16400     
16401         this.hoverState = 'in';
16402     
16403         if (!this.delay || !this.delay.show) {
16404             this.show();
16405             return;
16406         }
16407         var _t = this;
16408         this.timeout = setTimeout(function () {
16409             if (_t.hoverState == 'in') {
16410                 _t.show();
16411             }
16412         }, this.delay.show)
16413     },
16414     
16415     leave : function() {
16416         clearTimeout(this.timeout);
16417     
16418         this.hoverState = 'out';
16419     
16420         if (!this.delay || !this.delay.hide) {
16421             this.hide();
16422             return;
16423         }
16424         var _t = this;
16425         this.timeout = setTimeout(function () {
16426             if (_t.hoverState == 'out') {
16427                 _t.hide();
16428             }
16429         }, this.delay.hide)
16430     },
16431     
16432     show : function (on_el)
16433     {
16434         if (!on_el) {
16435             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16436         }
16437         
16438         // set content.
16439         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16440         if (this.html !== false) {
16441             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16442         }
16443         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16444         if (!this.title.length) {
16445             this.el.select('.popover-title',true).hide();
16446         }
16447         
16448         var placement = typeof this.placement == 'function' ?
16449             this.placement.call(this, this.el, on_el) :
16450             this.placement;
16451             
16452         var autoToken = /\s?auto?\s?/i;
16453         var autoPlace = autoToken.test(placement);
16454         if (autoPlace) {
16455             placement = placement.replace(autoToken, '') || 'top';
16456         }
16457         
16458         //this.el.detach()
16459         //this.el.setXY([0,0]);
16460         this.el.show();
16461         this.el.dom.style.display='block';
16462         this.el.addClass(placement);
16463         
16464         //this.el.appendTo(on_el);
16465         
16466         var p = this.getPosition();
16467         var box = this.el.getBox();
16468         
16469         if (autoPlace) {
16470             // fixme..
16471         }
16472         var align = Roo.bootstrap.Popover.alignment[placement];
16473         this.el.alignTo(on_el, align[0],align[1]);
16474         //var arrow = this.el.select('.arrow',true).first();
16475         //arrow.set(align[2], 
16476         
16477         this.el.addClass('in');
16478         
16479         
16480         if (this.el.hasClass('fade')) {
16481             // fade it?
16482         }
16483         
16484         this.hoverState = 'in';
16485         
16486         this.fireEvent('show', this);
16487         
16488     },
16489     hide : function()
16490     {
16491         this.el.setXY([0,0]);
16492         this.el.removeClass('in');
16493         this.el.hide();
16494         this.hoverState = null;
16495         
16496         this.fireEvent('hide', this);
16497     }
16498     
16499 });
16500
16501 Roo.bootstrap.Popover.alignment = {
16502     'left' : ['r-l', [-10,0], 'right'],
16503     'right' : ['l-r', [10,0], 'left'],
16504     'bottom' : ['t-b', [0,10], 'top'],
16505     'top' : [ 'b-t', [0,-10], 'bottom']
16506 };
16507
16508  /*
16509  * - LGPL
16510  *
16511  * Progress
16512  * 
16513  */
16514
16515 /**
16516  * @class Roo.bootstrap.Progress
16517  * @extends Roo.bootstrap.Component
16518  * Bootstrap Progress class
16519  * @cfg {Boolean} striped striped of the progress bar
16520  * @cfg {Boolean} active animated of the progress bar
16521  * 
16522  * 
16523  * @constructor
16524  * Create a new Progress
16525  * @param {Object} config The config object
16526  */
16527
16528 Roo.bootstrap.Progress = function(config){
16529     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16530 };
16531
16532 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16533     
16534     striped : false,
16535     active: false,
16536     
16537     getAutoCreate : function(){
16538         var cfg = {
16539             tag: 'div',
16540             cls: 'progress'
16541         };
16542         
16543         
16544         if(this.striped){
16545             cfg.cls += ' progress-striped';
16546         }
16547       
16548         if(this.active){
16549             cfg.cls += ' active';
16550         }
16551         
16552         
16553         return cfg;
16554     }
16555    
16556 });
16557
16558  
16559
16560  /*
16561  * - LGPL
16562  *
16563  * ProgressBar
16564  * 
16565  */
16566
16567 /**
16568  * @class Roo.bootstrap.ProgressBar
16569  * @extends Roo.bootstrap.Component
16570  * Bootstrap ProgressBar class
16571  * @cfg {Number} aria_valuenow aria-value now
16572  * @cfg {Number} aria_valuemin aria-value min
16573  * @cfg {Number} aria_valuemax aria-value max
16574  * @cfg {String} label label for the progress bar
16575  * @cfg {String} panel (success | info | warning | danger )
16576  * @cfg {String} role role of the progress bar
16577  * @cfg {String} sr_only text
16578  * 
16579  * 
16580  * @constructor
16581  * Create a new ProgressBar
16582  * @param {Object} config The config object
16583  */
16584
16585 Roo.bootstrap.ProgressBar = function(config){
16586     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16587 };
16588
16589 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16590     
16591     aria_valuenow : 0,
16592     aria_valuemin : 0,
16593     aria_valuemax : 100,
16594     label : false,
16595     panel : false,
16596     role : false,
16597     sr_only: false,
16598     
16599     getAutoCreate : function()
16600     {
16601         
16602         var cfg = {
16603             tag: 'div',
16604             cls: 'progress-bar',
16605             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16606         };
16607         
16608         if(this.sr_only){
16609             cfg.cn = {
16610                 tag: 'span',
16611                 cls: 'sr-only',
16612                 html: this.sr_only
16613             }
16614         }
16615         
16616         if(this.role){
16617             cfg.role = this.role;
16618         }
16619         
16620         if(this.aria_valuenow){
16621             cfg['aria-valuenow'] = this.aria_valuenow;
16622         }
16623         
16624         if(this.aria_valuemin){
16625             cfg['aria-valuemin'] = this.aria_valuemin;
16626         }
16627         
16628         if(this.aria_valuemax){
16629             cfg['aria-valuemax'] = this.aria_valuemax;
16630         }
16631         
16632         if(this.label && !this.sr_only){
16633             cfg.html = this.label;
16634         }
16635         
16636         if(this.panel){
16637             cfg.cls += ' progress-bar-' + this.panel;
16638         }
16639         
16640         return cfg;
16641     },
16642     
16643     update : function(aria_valuenow)
16644     {
16645         this.aria_valuenow = aria_valuenow;
16646         
16647         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16648     }
16649    
16650 });
16651
16652  
16653
16654  /*
16655  * - LGPL
16656  *
16657  * column
16658  * 
16659  */
16660
16661 /**
16662  * @class Roo.bootstrap.TabGroup
16663  * @extends Roo.bootstrap.Column
16664  * Bootstrap Column class
16665  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16666  * @cfg {Boolean} carousel true to make the group behave like a carousel
16667  * @cfg {Boolean} bullets show bullets for the panels
16668  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16669  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16670  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16671  * @cfg {Boolean} showarrow (true|false) show arrow default true
16672  * 
16673  * @constructor
16674  * Create a new TabGroup
16675  * @param {Object} config The config object
16676  */
16677
16678 Roo.bootstrap.TabGroup = function(config){
16679     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16680     if (!this.navId) {
16681         this.navId = Roo.id();
16682     }
16683     this.tabs = [];
16684     Roo.bootstrap.TabGroup.register(this);
16685     
16686 };
16687
16688 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16689     
16690     carousel : false,
16691     transition : false,
16692     bullets : 0,
16693     timer : 0,
16694     autoslide : false,
16695     slideFn : false,
16696     slideOnTouch : false,
16697     showarrow : true,
16698     
16699     getAutoCreate : function()
16700     {
16701         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16702         
16703         cfg.cls += ' tab-content';
16704         
16705         if (this.carousel) {
16706             cfg.cls += ' carousel slide';
16707             
16708             cfg.cn = [{
16709                cls : 'carousel-inner',
16710                cn : []
16711             }];
16712         
16713             if(this.bullets  && !Roo.isTouch){
16714                 
16715                 var bullets = {
16716                     cls : 'carousel-bullets',
16717                     cn : []
16718                 };
16719                
16720                 if(this.bullets_cls){
16721                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16722                 }
16723                 
16724                 bullets.cn.push({
16725                     cls : 'clear'
16726                 });
16727                 
16728                 cfg.cn[0].cn.push(bullets);
16729             }
16730             
16731             if(this.showarrow){
16732                 cfg.cn[0].cn.push({
16733                     tag : 'div',
16734                     class : 'carousel-arrow',
16735                     cn : [
16736                         {
16737                             tag : 'div',
16738                             class : 'carousel-prev',
16739                             cn : [
16740                                 {
16741                                     tag : 'i',
16742                                     class : 'fa fa-chevron-left'
16743                                 }
16744                             ]
16745                         },
16746                         {
16747                             tag : 'div',
16748                             class : 'carousel-next',
16749                             cn : [
16750                                 {
16751                                     tag : 'i',
16752                                     class : 'fa fa-chevron-right'
16753                                 }
16754                             ]
16755                         }
16756                     ]
16757                 });
16758             }
16759             
16760         }
16761         
16762         return cfg;
16763     },
16764     
16765     initEvents:  function()
16766     {
16767         if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16768             this.el.on("touchstart", this.onTouchStart, this);
16769         }
16770         
16771         if(this.autoslide){
16772             var _this = this;
16773             
16774             this.slideFn = window.setInterval(function() {
16775                 _this.showPanelNext();
16776             }, this.timer);
16777         }
16778         
16779         if(this.showarrow){
16780             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16781             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16782         }
16783         
16784         
16785     },
16786     
16787     onTouchStart : function(e, el, o)
16788     {
16789         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16790             return;
16791         }
16792         
16793         this.showPanelNext();
16794     },
16795     
16796     getChildContainer : function()
16797     {
16798         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16799     },
16800     
16801     /**
16802     * register a Navigation item
16803     * @param {Roo.bootstrap.NavItem} the navitem to add
16804     */
16805     register : function(item)
16806     {
16807         this.tabs.push( item);
16808         item.navId = this.navId; // not really needed..
16809         this.addBullet();
16810     
16811     },
16812     
16813     getActivePanel : function()
16814     {
16815         var r = false;
16816         Roo.each(this.tabs, function(t) {
16817             if (t.active) {
16818                 r = t;
16819                 return false;
16820             }
16821             return null;
16822         });
16823         return r;
16824         
16825     },
16826     getPanelByName : function(n)
16827     {
16828         var r = false;
16829         Roo.each(this.tabs, function(t) {
16830             if (t.tabId == n) {
16831                 r = t;
16832                 return false;
16833             }
16834             return null;
16835         });
16836         return r;
16837     },
16838     indexOfPanel : function(p)
16839     {
16840         var r = false;
16841         Roo.each(this.tabs, function(t,i) {
16842             if (t.tabId == p.tabId) {
16843                 r = i;
16844                 return false;
16845             }
16846             return null;
16847         });
16848         return r;
16849     },
16850     /**
16851      * show a specific panel
16852      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16853      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16854      */
16855     showPanel : function (pan)
16856     {
16857         if(this.transition || typeof(pan) == 'undefined'){
16858             Roo.log("waiting for the transitionend");
16859             return;
16860         }
16861         
16862         if (typeof(pan) == 'number') {
16863             pan = this.tabs[pan];
16864         }
16865         
16866         if (typeof(pan) == 'string') {
16867             pan = this.getPanelByName(pan);
16868         }
16869         
16870         var cur = this.getActivePanel();
16871         
16872         if(!pan || !cur){
16873             Roo.log('pan or acitve pan is undefined');
16874             return false;
16875         }
16876         
16877         if (pan.tabId == this.getActivePanel().tabId) {
16878             return true;
16879         }
16880         
16881         if (false === cur.fireEvent('beforedeactivate')) {
16882             return false;
16883         }
16884         
16885         if(this.bullets > 0 && !Roo.isTouch){
16886             this.setActiveBullet(this.indexOfPanel(pan));
16887         }
16888         
16889         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16890             
16891             this.transition = true;
16892             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16893             var lr = dir == 'next' ? 'left' : 'right';
16894             pan.el.addClass(dir); // or prev
16895             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16896             cur.el.addClass(lr); // or right
16897             pan.el.addClass(lr);
16898             
16899             var _this = this;
16900             cur.el.on('transitionend', function() {
16901                 Roo.log("trans end?");
16902                 
16903                 pan.el.removeClass([lr,dir]);
16904                 pan.setActive(true);
16905                 
16906                 cur.el.removeClass([lr]);
16907                 cur.setActive(false);
16908                 
16909                 _this.transition = false;
16910                 
16911             }, this, { single:  true } );
16912             
16913             return true;
16914         }
16915         
16916         cur.setActive(false);
16917         pan.setActive(true);
16918         
16919         return true;
16920         
16921     },
16922     showPanelNext : function()
16923     {
16924         var i = this.indexOfPanel(this.getActivePanel());
16925         
16926         if (i >= this.tabs.length - 1 && !this.autoslide) {
16927             return;
16928         }
16929         
16930         if (i >= this.tabs.length - 1 && this.autoslide) {
16931             i = -1;
16932         }
16933         
16934         this.showPanel(this.tabs[i+1]);
16935     },
16936     
16937     showPanelPrev : function()
16938     {
16939         var i = this.indexOfPanel(this.getActivePanel());
16940         
16941         if (i  < 1 && !this.autoslide) {
16942             return;
16943         }
16944         
16945         if (i < 1 && this.autoslide) {
16946             i = this.tabs.length;
16947         }
16948         
16949         this.showPanel(this.tabs[i-1]);
16950     },
16951     
16952     
16953     addBullet: function()
16954     {
16955         if(!this.bullets || Roo.isTouch){
16956             return;
16957         }
16958         var ctr = this.el.select('.carousel-bullets',true).first();
16959         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16960         var bullet = ctr.createChild({
16961             cls : 'bullet bullet-' + i
16962         },ctr.dom.lastChild);
16963         
16964         
16965         var _this = this;
16966         
16967         bullet.on('click', (function(e, el, o, ii, t){
16968
16969             e.preventDefault();
16970
16971             this.showPanel(ii);
16972
16973             if(this.autoslide && this.slideFn){
16974                 clearInterval(this.slideFn);
16975                 this.slideFn = window.setInterval(function() {
16976                     _this.showPanelNext();
16977                 }, this.timer);
16978             }
16979
16980         }).createDelegate(this, [i, bullet], true));
16981                 
16982         
16983     },
16984      
16985     setActiveBullet : function(i)
16986     {
16987         if(Roo.isTouch){
16988             return;
16989         }
16990         
16991         Roo.each(this.el.select('.bullet', true).elements, function(el){
16992             el.removeClass('selected');
16993         });
16994
16995         var bullet = this.el.select('.bullet-' + i, true).first();
16996         
16997         if(!bullet){
16998             return;
16999         }
17000         
17001         bullet.addClass('selected');
17002     }
17003     
17004     
17005   
17006 });
17007
17008  
17009
17010  
17011  
17012 Roo.apply(Roo.bootstrap.TabGroup, {
17013     
17014     groups: {},
17015      /**
17016     * register a Navigation Group
17017     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17018     */
17019     register : function(navgrp)
17020     {
17021         this.groups[navgrp.navId] = navgrp;
17022         
17023     },
17024     /**
17025     * fetch a Navigation Group based on the navigation ID
17026     * if one does not exist , it will get created.
17027     * @param {string} the navgroup to add
17028     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17029     */
17030     get: function(navId) {
17031         if (typeof(this.groups[navId]) == 'undefined') {
17032             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17033         }
17034         return this.groups[navId] ;
17035     }
17036     
17037     
17038     
17039 });
17040
17041  /*
17042  * - LGPL
17043  *
17044  * TabPanel
17045  * 
17046  */
17047
17048 /**
17049  * @class Roo.bootstrap.TabPanel
17050  * @extends Roo.bootstrap.Component
17051  * Bootstrap TabPanel class
17052  * @cfg {Boolean} active panel active
17053  * @cfg {String} html panel content
17054  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17055  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17056  * @cfg {String} href click to link..
17057  * 
17058  * 
17059  * @constructor
17060  * Create a new TabPanel
17061  * @param {Object} config The config object
17062  */
17063
17064 Roo.bootstrap.TabPanel = function(config){
17065     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17066     this.addEvents({
17067         /**
17068              * @event changed
17069              * Fires when the active status changes
17070              * @param {Roo.bootstrap.TabPanel} this
17071              * @param {Boolean} state the new state
17072             
17073          */
17074         'changed': true,
17075         /**
17076              * @event beforedeactivate
17077              * Fires before a tab is de-activated - can be used to do validation on a form.
17078              * @param {Roo.bootstrap.TabPanel} this
17079              * @return {Boolean} false if there is an error
17080             
17081          */
17082         'beforedeactivate': true
17083      });
17084     
17085     this.tabId = this.tabId || Roo.id();
17086   
17087 };
17088
17089 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17090     
17091     active: false,
17092     html: false,
17093     tabId: false,
17094     navId : false,
17095     href : '',
17096     
17097     getAutoCreate : function(){
17098         var cfg = {
17099             tag: 'div',
17100             // item is needed for carousel - not sure if it has any effect otherwise
17101             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17102             html: this.html || ''
17103         };
17104         
17105         if(this.active){
17106             cfg.cls += ' active';
17107         }
17108         
17109         if(this.tabId){
17110             cfg.tabId = this.tabId;
17111         }
17112         
17113         
17114         return cfg;
17115     },
17116     
17117     initEvents:  function()
17118     {
17119         var p = this.parent();
17120         this.navId = this.navId || p.navId;
17121         
17122         if (typeof(this.navId) != 'undefined') {
17123             // not really needed.. but just in case.. parent should be a NavGroup.
17124             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17125             
17126             tg.register(this);
17127             
17128             var i = tg.tabs.length - 1;
17129             
17130             if(this.active && tg.bullets > 0 && i < tg.bullets){
17131                 tg.setActiveBullet(i);
17132             }
17133         }
17134         
17135         if(this.href.length){
17136             this.el.on('click', this.onClick, this);
17137         }
17138         
17139     },
17140     
17141     onRender : function(ct, position)
17142     {
17143        // Roo.log("Call onRender: " + this.xtype);
17144         
17145         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17146         
17147         
17148         
17149         
17150         
17151     },
17152     
17153     setActive: function(state)
17154     {
17155         Roo.log("panel - set active " + this.tabId + "=" + state);
17156         
17157         this.active = state;
17158         if (!state) {
17159             this.el.removeClass('active');
17160             
17161         } else  if (!this.el.hasClass('active')) {
17162             this.el.addClass('active');
17163         }
17164         
17165         this.fireEvent('changed', this, state);
17166     },
17167     
17168     onClick: function(e)
17169     {
17170         e.preventDefault();
17171         
17172         window.location.href = this.href;
17173     }
17174     
17175     
17176 });
17177  
17178
17179  
17180
17181  /*
17182  * - LGPL
17183  *
17184  * DateField
17185  * 
17186  */
17187
17188 /**
17189  * @class Roo.bootstrap.DateField
17190  * @extends Roo.bootstrap.Input
17191  * Bootstrap DateField class
17192  * @cfg {Number} weekStart default 0
17193  * @cfg {String} viewMode default empty, (months|years)
17194  * @cfg {String} minViewMode default empty, (months|years)
17195  * @cfg {Number} startDate default -Infinity
17196  * @cfg {Number} endDate default Infinity
17197  * @cfg {Boolean} todayHighlight default false
17198  * @cfg {Boolean} todayBtn default false
17199  * @cfg {Boolean} calendarWeeks default false
17200  * @cfg {Object} daysOfWeekDisabled default empty
17201  * @cfg {Boolean} singleMode default false (true | false)
17202  * 
17203  * @cfg {Boolean} keyboardNavigation default true
17204  * @cfg {String} language default en
17205  * 
17206  * @constructor
17207  * Create a new DateField
17208  * @param {Object} config The config object
17209  */
17210
17211 Roo.bootstrap.DateField = function(config){
17212     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17213      this.addEvents({
17214             /**
17215              * @event show
17216              * Fires when this field show.
17217              * @param {Roo.bootstrap.DateField} this
17218              * @param {Mixed} date The date value
17219              */
17220             show : true,
17221             /**
17222              * @event show
17223              * Fires when this field hide.
17224              * @param {Roo.bootstrap.DateField} this
17225              * @param {Mixed} date The date value
17226              */
17227             hide : true,
17228             /**
17229              * @event select
17230              * Fires when select a date.
17231              * @param {Roo.bootstrap.DateField} this
17232              * @param {Mixed} date The date value
17233              */
17234             select : true,
17235             /**
17236              * @event beforeselect
17237              * Fires when before select a date.
17238              * @param {Roo.bootstrap.DateField} this
17239              * @param {Mixed} date The date value
17240              */
17241             beforeselect : true
17242         });
17243 };
17244
17245 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17246     
17247     /**
17248      * @cfg {String} format
17249      * The default date format string which can be overriden for localization support.  The format must be
17250      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17251      */
17252     format : "m/d/y",
17253     /**
17254      * @cfg {String} altFormats
17255      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17256      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17257      */
17258     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17259     
17260     weekStart : 0,
17261     
17262     viewMode : '',
17263     
17264     minViewMode : '',
17265     
17266     todayHighlight : false,
17267     
17268     todayBtn: false,
17269     
17270     language: 'en',
17271     
17272     keyboardNavigation: true,
17273     
17274     calendarWeeks: false,
17275     
17276     startDate: -Infinity,
17277     
17278     endDate: Infinity,
17279     
17280     daysOfWeekDisabled: [],
17281     
17282     _events: [],
17283     
17284     singleMode : false,
17285     
17286     UTCDate: function()
17287     {
17288         return new Date(Date.UTC.apply(Date, arguments));
17289     },
17290     
17291     UTCToday: function()
17292     {
17293         var today = new Date();
17294         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17295     },
17296     
17297     getDate: function() {
17298             var d = this.getUTCDate();
17299             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17300     },
17301     
17302     getUTCDate: function() {
17303             return this.date;
17304     },
17305     
17306     setDate: function(d) {
17307             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17308     },
17309     
17310     setUTCDate: function(d) {
17311             this.date = d;
17312             this.setValue(this.formatDate(this.date));
17313     },
17314         
17315     onRender: function(ct, position)
17316     {
17317         
17318         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17319         
17320         this.language = this.language || 'en';
17321         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17322         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17323         
17324         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17325         this.format = this.format || 'm/d/y';
17326         this.isInline = false;
17327         this.isInput = true;
17328         this.component = this.el.select('.add-on', true).first() || false;
17329         this.component = (this.component && this.component.length === 0) ? false : this.component;
17330         this.hasInput = this.component && this.inputEL().length;
17331         
17332         if (typeof(this.minViewMode === 'string')) {
17333             switch (this.minViewMode) {
17334                 case 'months':
17335                     this.minViewMode = 1;
17336                     break;
17337                 case 'years':
17338                     this.minViewMode = 2;
17339                     break;
17340                 default:
17341                     this.minViewMode = 0;
17342                     break;
17343             }
17344         }
17345         
17346         if (typeof(this.viewMode === 'string')) {
17347             switch (this.viewMode) {
17348                 case 'months':
17349                     this.viewMode = 1;
17350                     break;
17351                 case 'years':
17352                     this.viewMode = 2;
17353                     break;
17354                 default:
17355                     this.viewMode = 0;
17356                     break;
17357             }
17358         }
17359                 
17360         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17361         
17362 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17363         
17364         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17365         
17366         this.picker().on('mousedown', this.onMousedown, this);
17367         this.picker().on('click', this.onClick, this);
17368         
17369         this.picker().addClass('datepicker-dropdown');
17370         
17371         this.startViewMode = this.viewMode;
17372         
17373         if(this.singleMode){
17374             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17375                 v.setVisibilityMode(Roo.Element.DISPLAY);
17376                 v.hide();
17377             });
17378             
17379             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17380                 v.setStyle('width', '189px');
17381             });
17382         }
17383         
17384         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17385             if(!this.calendarWeeks){
17386                 v.remove();
17387                 return;
17388             }
17389             
17390             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17391             v.attr('colspan', function(i, val){
17392                 return parseInt(val) + 1;
17393             });
17394         });
17395                         
17396         
17397         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17398         
17399         this.setStartDate(this.startDate);
17400         this.setEndDate(this.endDate);
17401         
17402         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17403         
17404         this.fillDow();
17405         this.fillMonths();
17406         this.update();
17407         this.showMode();
17408         
17409         if(this.isInline) {
17410             this.show();
17411         }
17412     },
17413     
17414     picker : function()
17415     {
17416         return this.pickerEl;
17417 //        return this.el.select('.datepicker', true).first();
17418     },
17419     
17420     fillDow: function()
17421     {
17422         var dowCnt = this.weekStart;
17423         
17424         var dow = {
17425             tag: 'tr',
17426             cn: [
17427                 
17428             ]
17429         };
17430         
17431         if(this.calendarWeeks){
17432             dow.cn.push({
17433                 tag: 'th',
17434                 cls: 'cw',
17435                 html: '&nbsp;'
17436             })
17437         }
17438         
17439         while (dowCnt < this.weekStart + 7) {
17440             dow.cn.push({
17441                 tag: 'th',
17442                 cls: 'dow',
17443                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17444             });
17445         }
17446         
17447         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17448     },
17449     
17450     fillMonths: function()
17451     {    
17452         var i = 0;
17453         var months = this.picker().select('>.datepicker-months td', true).first();
17454         
17455         months.dom.innerHTML = '';
17456         
17457         while (i < 12) {
17458             var month = {
17459                 tag: 'span',
17460                 cls: 'month',
17461                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17462             };
17463             
17464             months.createChild(month);
17465         }
17466         
17467     },
17468     
17469     update: function()
17470     {
17471         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;
17472         
17473         if (this.date < this.startDate) {
17474             this.viewDate = new Date(this.startDate);
17475         } else if (this.date > this.endDate) {
17476             this.viewDate = new Date(this.endDate);
17477         } else {
17478             this.viewDate = new Date(this.date);
17479         }
17480         
17481         this.fill();
17482     },
17483     
17484     fill: function() 
17485     {
17486         var d = new Date(this.viewDate),
17487                 year = d.getUTCFullYear(),
17488                 month = d.getUTCMonth(),
17489                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17490                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17491                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17492                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17493                 currentDate = this.date && this.date.valueOf(),
17494                 today = this.UTCToday();
17495         
17496         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17497         
17498 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17499         
17500 //        this.picker.select('>tfoot th.today').
17501 //                                              .text(dates[this.language].today)
17502 //                                              .toggle(this.todayBtn !== false);
17503     
17504         this.updateNavArrows();
17505         this.fillMonths();
17506                                                 
17507         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17508         
17509         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17510          
17511         prevMonth.setUTCDate(day);
17512         
17513         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17514         
17515         var nextMonth = new Date(prevMonth);
17516         
17517         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17518         
17519         nextMonth = nextMonth.valueOf();
17520         
17521         var fillMonths = false;
17522         
17523         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17524         
17525         while(prevMonth.valueOf() < nextMonth) {
17526             var clsName = '';
17527             
17528             if (prevMonth.getUTCDay() === this.weekStart) {
17529                 if(fillMonths){
17530                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17531                 }
17532                     
17533                 fillMonths = {
17534                     tag: 'tr',
17535                     cn: []
17536                 };
17537                 
17538                 if(this.calendarWeeks){
17539                     // ISO 8601: First week contains first thursday.
17540                     // ISO also states week starts on Monday, but we can be more abstract here.
17541                     var
17542                     // Start of current week: based on weekstart/current date
17543                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17544                     // Thursday of this week
17545                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17546                     // First Thursday of year, year from thursday
17547                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17548                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17549                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17550                     
17551                     fillMonths.cn.push({
17552                         tag: 'td',
17553                         cls: 'cw',
17554                         html: calWeek
17555                     });
17556                 }
17557             }
17558             
17559             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17560                 clsName += ' old';
17561             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17562                 clsName += ' new';
17563             }
17564             if (this.todayHighlight &&
17565                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17566                 prevMonth.getUTCMonth() == today.getMonth() &&
17567                 prevMonth.getUTCDate() == today.getDate()) {
17568                 clsName += ' today';
17569             }
17570             
17571             if (currentDate && prevMonth.valueOf() === currentDate) {
17572                 clsName += ' active';
17573             }
17574             
17575             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17576                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17577                     clsName += ' disabled';
17578             }
17579             
17580             fillMonths.cn.push({
17581                 tag: 'td',
17582                 cls: 'day ' + clsName,
17583                 html: prevMonth.getDate()
17584             });
17585             
17586             prevMonth.setDate(prevMonth.getDate()+1);
17587         }
17588           
17589         var currentYear = this.date && this.date.getUTCFullYear();
17590         var currentMonth = this.date && this.date.getUTCMonth();
17591         
17592         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17593         
17594         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17595             v.removeClass('active');
17596             
17597             if(currentYear === year && k === currentMonth){
17598                 v.addClass('active');
17599             }
17600             
17601             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17602                 v.addClass('disabled');
17603             }
17604             
17605         });
17606         
17607         
17608         year = parseInt(year/10, 10) * 10;
17609         
17610         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17611         
17612         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17613         
17614         year -= 1;
17615         for (var i = -1; i < 11; i++) {
17616             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17617                 tag: 'span',
17618                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17619                 html: year
17620             });
17621             
17622             year += 1;
17623         }
17624     },
17625     
17626     showMode: function(dir) 
17627     {
17628         if (dir) {
17629             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17630         }
17631         
17632         Roo.each(this.picker().select('>div',true).elements, function(v){
17633             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17634             v.hide();
17635         });
17636         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17637     },
17638     
17639     place: function()
17640     {
17641         if(this.isInline) {
17642             return;
17643         }
17644         
17645         this.picker().removeClass(['bottom', 'top']);
17646         
17647         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17648             /*
17649              * place to the top of element!
17650              *
17651              */
17652             
17653             this.picker().addClass('top');
17654             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17655             
17656             return;
17657         }
17658         
17659         this.picker().addClass('bottom');
17660         
17661         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17662     },
17663     
17664     parseDate : function(value)
17665     {
17666         if(!value || value instanceof Date){
17667             return value;
17668         }
17669         var v = Date.parseDate(value, this.format);
17670         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17671             v = Date.parseDate(value, 'Y-m-d');
17672         }
17673         if(!v && this.altFormats){
17674             if(!this.altFormatsArray){
17675                 this.altFormatsArray = this.altFormats.split("|");
17676             }
17677             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17678                 v = Date.parseDate(value, this.altFormatsArray[i]);
17679             }
17680         }
17681         return v;
17682     },
17683     
17684     formatDate : function(date, fmt)
17685     {   
17686         return (!date || !(date instanceof Date)) ?
17687         date : date.dateFormat(fmt || this.format);
17688     },
17689     
17690     onFocus : function()
17691     {
17692         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17693         this.show();
17694     },
17695     
17696     onBlur : function()
17697     {
17698         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17699         
17700         var d = this.inputEl().getValue();
17701         
17702         this.setValue(d);
17703                 
17704         this.hide();
17705     },
17706     
17707     show : function()
17708     {
17709         this.picker().show();
17710         this.update();
17711         this.place();
17712         
17713         this.fireEvent('show', this, this.date);
17714     },
17715     
17716     hide : function()
17717     {
17718         if(this.isInline) {
17719             return;
17720         }
17721         this.picker().hide();
17722         this.viewMode = this.startViewMode;
17723         this.showMode();
17724         
17725         this.fireEvent('hide', this, this.date);
17726         
17727     },
17728     
17729     onMousedown: function(e)
17730     {
17731         e.stopPropagation();
17732         e.preventDefault();
17733     },
17734     
17735     keyup: function(e)
17736     {
17737         Roo.bootstrap.DateField.superclass.keyup.call(this);
17738         this.update();
17739     },
17740
17741     setValue: function(v)
17742     {
17743         if(this.fireEvent('beforeselect', this, v) !== false){
17744             var d = new Date(this.parseDate(v) ).clearTime();
17745         
17746             if(isNaN(d.getTime())){
17747                 this.date = this.viewDate = '';
17748                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17749                 return;
17750             }
17751
17752             v = this.formatDate(d);
17753
17754             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17755
17756             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17757
17758             this.update();
17759
17760             this.fireEvent('select', this, this.date);
17761         }
17762     },
17763     
17764     getValue: function()
17765     {
17766         return this.formatDate(this.date);
17767     },
17768     
17769     fireKey: function(e)
17770     {
17771         if (!this.picker().isVisible()){
17772             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17773                 this.show();
17774             }
17775             return;
17776         }
17777         
17778         var dateChanged = false,
17779         dir, day, month,
17780         newDate, newViewDate;
17781         
17782         switch(e.keyCode){
17783             case 27: // escape
17784                 this.hide();
17785                 e.preventDefault();
17786                 break;
17787             case 37: // left
17788             case 39: // right
17789                 if (!this.keyboardNavigation) {
17790                     break;
17791                 }
17792                 dir = e.keyCode == 37 ? -1 : 1;
17793                 
17794                 if (e.ctrlKey){
17795                     newDate = this.moveYear(this.date, dir);
17796                     newViewDate = this.moveYear(this.viewDate, dir);
17797                 } else if (e.shiftKey){
17798                     newDate = this.moveMonth(this.date, dir);
17799                     newViewDate = this.moveMonth(this.viewDate, dir);
17800                 } else {
17801                     newDate = new Date(this.date);
17802                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17803                     newViewDate = new Date(this.viewDate);
17804                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17805                 }
17806                 if (this.dateWithinRange(newDate)){
17807                     this.date = newDate;
17808                     this.viewDate = newViewDate;
17809                     this.setValue(this.formatDate(this.date));
17810 //                    this.update();
17811                     e.preventDefault();
17812                     dateChanged = true;
17813                 }
17814                 break;
17815             case 38: // up
17816             case 40: // down
17817                 if (!this.keyboardNavigation) {
17818                     break;
17819                 }
17820                 dir = e.keyCode == 38 ? -1 : 1;
17821                 if (e.ctrlKey){
17822                     newDate = this.moveYear(this.date, dir);
17823                     newViewDate = this.moveYear(this.viewDate, dir);
17824                 } else if (e.shiftKey){
17825                     newDate = this.moveMonth(this.date, dir);
17826                     newViewDate = this.moveMonth(this.viewDate, dir);
17827                 } else {
17828                     newDate = new Date(this.date);
17829                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17830                     newViewDate = new Date(this.viewDate);
17831                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17832                 }
17833                 if (this.dateWithinRange(newDate)){
17834                     this.date = newDate;
17835                     this.viewDate = newViewDate;
17836                     this.setValue(this.formatDate(this.date));
17837 //                    this.update();
17838                     e.preventDefault();
17839                     dateChanged = true;
17840                 }
17841                 break;
17842             case 13: // enter
17843                 this.setValue(this.formatDate(this.date));
17844                 this.hide();
17845                 e.preventDefault();
17846                 break;
17847             case 9: // tab
17848                 this.setValue(this.formatDate(this.date));
17849                 this.hide();
17850                 break;
17851             case 16: // shift
17852             case 17: // ctrl
17853             case 18: // alt
17854                 break;
17855             default :
17856                 this.hide();
17857                 
17858         }
17859     },
17860     
17861     
17862     onClick: function(e) 
17863     {
17864         e.stopPropagation();
17865         e.preventDefault();
17866         
17867         var target = e.getTarget();
17868         
17869         if(target.nodeName.toLowerCase() === 'i'){
17870             target = Roo.get(target).dom.parentNode;
17871         }
17872         
17873         var nodeName = target.nodeName;
17874         var className = target.className;
17875         var html = target.innerHTML;
17876         //Roo.log(nodeName);
17877         
17878         switch(nodeName.toLowerCase()) {
17879             case 'th':
17880                 switch(className) {
17881                     case 'switch':
17882                         this.showMode(1);
17883                         break;
17884                     case 'prev':
17885                     case 'next':
17886                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17887                         switch(this.viewMode){
17888                                 case 0:
17889                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17890                                         break;
17891                                 case 1:
17892                                 case 2:
17893                                         this.viewDate = this.moveYear(this.viewDate, dir);
17894                                         break;
17895                         }
17896                         this.fill();
17897                         break;
17898                     case 'today':
17899                         var date = new Date();
17900                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17901 //                        this.fill()
17902                         this.setValue(this.formatDate(this.date));
17903                         
17904                         this.hide();
17905                         break;
17906                 }
17907                 break;
17908             case 'span':
17909                 if (className.indexOf('disabled') < 0) {
17910                     this.viewDate.setUTCDate(1);
17911                     if (className.indexOf('month') > -1) {
17912                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17913                     } else {
17914                         var year = parseInt(html, 10) || 0;
17915                         this.viewDate.setUTCFullYear(year);
17916                         
17917                     }
17918                     
17919                     if(this.singleMode){
17920                         this.setValue(this.formatDate(this.viewDate));
17921                         this.hide();
17922                         return;
17923                     }
17924                     
17925                     this.showMode(-1);
17926                     this.fill();
17927                 }
17928                 break;
17929                 
17930             case 'td':
17931                 //Roo.log(className);
17932                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17933                     var day = parseInt(html, 10) || 1;
17934                     var year = this.viewDate.getUTCFullYear(),
17935                         month = this.viewDate.getUTCMonth();
17936
17937                     if (className.indexOf('old') > -1) {
17938                         if(month === 0 ){
17939                             month = 11;
17940                             year -= 1;
17941                         }else{
17942                             month -= 1;
17943                         }
17944                     } else if (className.indexOf('new') > -1) {
17945                         if (month == 11) {
17946                             month = 0;
17947                             year += 1;
17948                         } else {
17949                             month += 1;
17950                         }
17951                     }
17952                     //Roo.log([year,month,day]);
17953                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17954                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17955 //                    this.fill();
17956                     //Roo.log(this.formatDate(this.date));
17957                     this.setValue(this.formatDate(this.date));
17958                     this.hide();
17959                 }
17960                 break;
17961         }
17962     },
17963     
17964     setStartDate: function(startDate)
17965     {
17966         this.startDate = startDate || -Infinity;
17967         if (this.startDate !== -Infinity) {
17968             this.startDate = this.parseDate(this.startDate);
17969         }
17970         this.update();
17971         this.updateNavArrows();
17972     },
17973
17974     setEndDate: function(endDate)
17975     {
17976         this.endDate = endDate || Infinity;
17977         if (this.endDate !== Infinity) {
17978             this.endDate = this.parseDate(this.endDate);
17979         }
17980         this.update();
17981         this.updateNavArrows();
17982     },
17983     
17984     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17985     {
17986         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17987         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17988             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17989         }
17990         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17991             return parseInt(d, 10);
17992         });
17993         this.update();
17994         this.updateNavArrows();
17995     },
17996     
17997     updateNavArrows: function() 
17998     {
17999         if(this.singleMode){
18000             return;
18001         }
18002         
18003         var d = new Date(this.viewDate),
18004         year = d.getUTCFullYear(),
18005         month = d.getUTCMonth();
18006         
18007         Roo.each(this.picker().select('.prev', true).elements, function(v){
18008             v.show();
18009             switch (this.viewMode) {
18010                 case 0:
18011
18012                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18013                         v.hide();
18014                     }
18015                     break;
18016                 case 1:
18017                 case 2:
18018                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18019                         v.hide();
18020                     }
18021                     break;
18022             }
18023         });
18024         
18025         Roo.each(this.picker().select('.next', true).elements, function(v){
18026             v.show();
18027             switch (this.viewMode) {
18028                 case 0:
18029
18030                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18031                         v.hide();
18032                     }
18033                     break;
18034                 case 1:
18035                 case 2:
18036                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18037                         v.hide();
18038                     }
18039                     break;
18040             }
18041         })
18042     },
18043     
18044     moveMonth: function(date, dir)
18045     {
18046         if (!dir) {
18047             return date;
18048         }
18049         var new_date = new Date(date.valueOf()),
18050         day = new_date.getUTCDate(),
18051         month = new_date.getUTCMonth(),
18052         mag = Math.abs(dir),
18053         new_month, test;
18054         dir = dir > 0 ? 1 : -1;
18055         if (mag == 1){
18056             test = dir == -1
18057             // If going back one month, make sure month is not current month
18058             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18059             ? function(){
18060                 return new_date.getUTCMonth() == month;
18061             }
18062             // If going forward one month, make sure month is as expected
18063             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18064             : function(){
18065                 return new_date.getUTCMonth() != new_month;
18066             };
18067             new_month = month + dir;
18068             new_date.setUTCMonth(new_month);
18069             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18070             if (new_month < 0 || new_month > 11) {
18071                 new_month = (new_month + 12) % 12;
18072             }
18073         } else {
18074             // For magnitudes >1, move one month at a time...
18075             for (var i=0; i<mag; i++) {
18076                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18077                 new_date = this.moveMonth(new_date, dir);
18078             }
18079             // ...then reset the day, keeping it in the new month
18080             new_month = new_date.getUTCMonth();
18081             new_date.setUTCDate(day);
18082             test = function(){
18083                 return new_month != new_date.getUTCMonth();
18084             };
18085         }
18086         // Common date-resetting loop -- if date is beyond end of month, make it
18087         // end of month
18088         while (test()){
18089             new_date.setUTCDate(--day);
18090             new_date.setUTCMonth(new_month);
18091         }
18092         return new_date;
18093     },
18094
18095     moveYear: function(date, dir)
18096     {
18097         return this.moveMonth(date, dir*12);
18098     },
18099
18100     dateWithinRange: function(date)
18101     {
18102         return date >= this.startDate && date <= this.endDate;
18103     },
18104
18105     
18106     remove: function() 
18107     {
18108         this.picker().remove();
18109     }
18110    
18111 });
18112
18113 Roo.apply(Roo.bootstrap.DateField,  {
18114     
18115     head : {
18116         tag: 'thead',
18117         cn: [
18118         {
18119             tag: 'tr',
18120             cn: [
18121             {
18122                 tag: 'th',
18123                 cls: 'prev',
18124                 html: '<i class="fa fa-arrow-left"/>'
18125             },
18126             {
18127                 tag: 'th',
18128                 cls: 'switch',
18129                 colspan: '5'
18130             },
18131             {
18132                 tag: 'th',
18133                 cls: 'next',
18134                 html: '<i class="fa fa-arrow-right"/>'
18135             }
18136
18137             ]
18138         }
18139         ]
18140     },
18141     
18142     content : {
18143         tag: 'tbody',
18144         cn: [
18145         {
18146             tag: 'tr',
18147             cn: [
18148             {
18149                 tag: 'td',
18150                 colspan: '7'
18151             }
18152             ]
18153         }
18154         ]
18155     },
18156     
18157     footer : {
18158         tag: 'tfoot',
18159         cn: [
18160         {
18161             tag: 'tr',
18162             cn: [
18163             {
18164                 tag: 'th',
18165                 colspan: '7',
18166                 cls: 'today'
18167             }
18168                     
18169             ]
18170         }
18171         ]
18172     },
18173     
18174     dates:{
18175         en: {
18176             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18177             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18178             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18179             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18180             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18181             today: "Today"
18182         }
18183     },
18184     
18185     modes: [
18186     {
18187         clsName: 'days',
18188         navFnc: 'Month',
18189         navStep: 1
18190     },
18191     {
18192         clsName: 'months',
18193         navFnc: 'FullYear',
18194         navStep: 1
18195     },
18196     {
18197         clsName: 'years',
18198         navFnc: 'FullYear',
18199         navStep: 10
18200     }]
18201 });
18202
18203 Roo.apply(Roo.bootstrap.DateField,  {
18204   
18205     template : {
18206         tag: 'div',
18207         cls: 'datepicker dropdown-menu roo-dynamic',
18208         cn: [
18209         {
18210             tag: 'div',
18211             cls: 'datepicker-days',
18212             cn: [
18213             {
18214                 tag: 'table',
18215                 cls: 'table-condensed',
18216                 cn:[
18217                 Roo.bootstrap.DateField.head,
18218                 {
18219                     tag: 'tbody'
18220                 },
18221                 Roo.bootstrap.DateField.footer
18222                 ]
18223             }
18224             ]
18225         },
18226         {
18227             tag: 'div',
18228             cls: 'datepicker-months',
18229             cn: [
18230             {
18231                 tag: 'table',
18232                 cls: 'table-condensed',
18233                 cn:[
18234                 Roo.bootstrap.DateField.head,
18235                 Roo.bootstrap.DateField.content,
18236                 Roo.bootstrap.DateField.footer
18237                 ]
18238             }
18239             ]
18240         },
18241         {
18242             tag: 'div',
18243             cls: 'datepicker-years',
18244             cn: [
18245             {
18246                 tag: 'table',
18247                 cls: 'table-condensed',
18248                 cn:[
18249                 Roo.bootstrap.DateField.head,
18250                 Roo.bootstrap.DateField.content,
18251                 Roo.bootstrap.DateField.footer
18252                 ]
18253             }
18254             ]
18255         }
18256         ]
18257     }
18258 });
18259
18260  
18261
18262  /*
18263  * - LGPL
18264  *
18265  * TimeField
18266  * 
18267  */
18268
18269 /**
18270  * @class Roo.bootstrap.TimeField
18271  * @extends Roo.bootstrap.Input
18272  * Bootstrap DateField class
18273  * 
18274  * 
18275  * @constructor
18276  * Create a new TimeField
18277  * @param {Object} config The config object
18278  */
18279
18280 Roo.bootstrap.TimeField = function(config){
18281     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18282     this.addEvents({
18283             /**
18284              * @event show
18285              * Fires when this field show.
18286              * @param {Roo.bootstrap.DateField} thisthis
18287              * @param {Mixed} date The date value
18288              */
18289             show : true,
18290             /**
18291              * @event show
18292              * Fires when this field hide.
18293              * @param {Roo.bootstrap.DateField} this
18294              * @param {Mixed} date The date value
18295              */
18296             hide : true,
18297             /**
18298              * @event select
18299              * Fires when select a date.
18300              * @param {Roo.bootstrap.DateField} this
18301              * @param {Mixed} date The date value
18302              */
18303             select : true
18304         });
18305 };
18306
18307 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18308     
18309     /**
18310      * @cfg {String} format
18311      * The default time format string which can be overriden for localization support.  The format must be
18312      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18313      */
18314     format : "H:i",
18315        
18316     onRender: function(ct, position)
18317     {
18318         
18319         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18320                 
18321         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18322         
18323         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18324         
18325         this.pop = this.picker().select('>.datepicker-time',true).first();
18326         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18327         
18328         this.picker().on('mousedown', this.onMousedown, this);
18329         this.picker().on('click', this.onClick, this);
18330         
18331         this.picker().addClass('datepicker-dropdown');
18332     
18333         this.fillTime();
18334         this.update();
18335             
18336         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18337         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18338         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18339         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18340         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18341         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18342
18343     },
18344     
18345     fireKey: function(e){
18346         if (!this.picker().isVisible()){
18347             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18348                 this.show();
18349             }
18350             return;
18351         }
18352
18353         e.preventDefault();
18354         
18355         switch(e.keyCode){
18356             case 27: // escape
18357                 this.hide();
18358                 break;
18359             case 37: // left
18360             case 39: // right
18361                 this.onTogglePeriod();
18362                 break;
18363             case 38: // up
18364                 this.onIncrementMinutes();
18365                 break;
18366             case 40: // down
18367                 this.onDecrementMinutes();
18368                 break;
18369             case 13: // enter
18370             case 9: // tab
18371                 this.setTime();
18372                 break;
18373         }
18374     },
18375     
18376     onClick: function(e) {
18377         e.stopPropagation();
18378         e.preventDefault();
18379     },
18380     
18381     picker : function()
18382     {
18383         return this.el.select('.datepicker', true).first();
18384     },
18385     
18386     fillTime: function()
18387     {    
18388         var time = this.pop.select('tbody', true).first();
18389         
18390         time.dom.innerHTML = '';
18391         
18392         time.createChild({
18393             tag: 'tr',
18394             cn: [
18395                 {
18396                     tag: 'td',
18397                     cn: [
18398                         {
18399                             tag: 'a',
18400                             href: '#',
18401                             cls: 'btn',
18402                             cn: [
18403                                 {
18404                                     tag: 'span',
18405                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18406                                 }
18407                             ]
18408                         } 
18409                     ]
18410                 },
18411                 {
18412                     tag: 'td',
18413                     cls: 'separator'
18414                 },
18415                 {
18416                     tag: 'td',
18417                     cn: [
18418                         {
18419                             tag: 'a',
18420                             href: '#',
18421                             cls: 'btn',
18422                             cn: [
18423                                 {
18424                                     tag: 'span',
18425                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18426                                 }
18427                             ]
18428                         }
18429                     ]
18430                 },
18431                 {
18432                     tag: 'td',
18433                     cls: 'separator'
18434                 }
18435             ]
18436         });
18437         
18438         time.createChild({
18439             tag: 'tr',
18440             cn: [
18441                 {
18442                     tag: 'td',
18443                     cn: [
18444                         {
18445                             tag: 'span',
18446                             cls: 'timepicker-hour',
18447                             html: '00'
18448                         }  
18449                     ]
18450                 },
18451                 {
18452                     tag: 'td',
18453                     cls: 'separator',
18454                     html: ':'
18455                 },
18456                 {
18457                     tag: 'td',
18458                     cn: [
18459                         {
18460                             tag: 'span',
18461                             cls: 'timepicker-minute',
18462                             html: '00'
18463                         }  
18464                     ]
18465                 },
18466                 {
18467                     tag: 'td',
18468                     cls: 'separator'
18469                 },
18470                 {
18471                     tag: 'td',
18472                     cn: [
18473                         {
18474                             tag: 'button',
18475                             type: 'button',
18476                             cls: 'btn btn-primary period',
18477                             html: 'AM'
18478                             
18479                         }
18480                     ]
18481                 }
18482             ]
18483         });
18484         
18485         time.createChild({
18486             tag: 'tr',
18487             cn: [
18488                 {
18489                     tag: 'td',
18490                     cn: [
18491                         {
18492                             tag: 'a',
18493                             href: '#',
18494                             cls: 'btn',
18495                             cn: [
18496                                 {
18497                                     tag: 'span',
18498                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18499                                 }
18500                             ]
18501                         }
18502                     ]
18503                 },
18504                 {
18505                     tag: 'td',
18506                     cls: 'separator'
18507                 },
18508                 {
18509                     tag: 'td',
18510                     cn: [
18511                         {
18512                             tag: 'a',
18513                             href: '#',
18514                             cls: 'btn',
18515                             cn: [
18516                                 {
18517                                     tag: 'span',
18518                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18519                                 }
18520                             ]
18521                         }
18522                     ]
18523                 },
18524                 {
18525                     tag: 'td',
18526                     cls: 'separator'
18527                 }
18528             ]
18529         });
18530         
18531     },
18532     
18533     update: function()
18534     {
18535         
18536         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18537         
18538         this.fill();
18539     },
18540     
18541     fill: function() 
18542     {
18543         var hours = this.time.getHours();
18544         var minutes = this.time.getMinutes();
18545         var period = 'AM';
18546         
18547         if(hours > 11){
18548             period = 'PM';
18549         }
18550         
18551         if(hours == 0){
18552             hours = 12;
18553         }
18554         
18555         
18556         if(hours > 12){
18557             hours = hours - 12;
18558         }
18559         
18560         if(hours < 10){
18561             hours = '0' + hours;
18562         }
18563         
18564         if(minutes < 10){
18565             minutes = '0' + minutes;
18566         }
18567         
18568         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18569         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18570         this.pop.select('button', true).first().dom.innerHTML = period;
18571         
18572     },
18573     
18574     place: function()
18575     {   
18576         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18577         
18578         var cls = ['bottom'];
18579         
18580         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18581             cls.pop();
18582             cls.push('top');
18583         }
18584         
18585         cls.push('right');
18586         
18587         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18588             cls.pop();
18589             cls.push('left');
18590         }
18591         
18592         this.picker().addClass(cls.join('-'));
18593         
18594         var _this = this;
18595         
18596         Roo.each(cls, function(c){
18597             if(c == 'bottom'){
18598                 _this.picker().setTop(_this.inputEl().getHeight());
18599                 return;
18600             }
18601             if(c == 'top'){
18602                 _this.picker().setTop(0 - _this.picker().getHeight());
18603                 return;
18604             }
18605             
18606             if(c == 'left'){
18607                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18608                 return;
18609             }
18610             if(c == 'right'){
18611                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18612                 return;
18613             }
18614         });
18615         
18616     },
18617   
18618     onFocus : function()
18619     {
18620         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18621         this.show();
18622     },
18623     
18624     onBlur : function()
18625     {
18626         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18627         this.hide();
18628     },
18629     
18630     show : function()
18631     {
18632         this.picker().show();
18633         this.pop.show();
18634         this.update();
18635         this.place();
18636         
18637         this.fireEvent('show', this, this.date);
18638     },
18639     
18640     hide : function()
18641     {
18642         this.picker().hide();
18643         this.pop.hide();
18644         
18645         this.fireEvent('hide', this, this.date);
18646     },
18647     
18648     setTime : function()
18649     {
18650         this.hide();
18651         this.setValue(this.time.format(this.format));
18652         
18653         this.fireEvent('select', this, this.date);
18654         
18655         
18656     },
18657     
18658     onMousedown: function(e){
18659         e.stopPropagation();
18660         e.preventDefault();
18661     },
18662     
18663     onIncrementHours: function()
18664     {
18665         Roo.log('onIncrementHours');
18666         this.time = this.time.add(Date.HOUR, 1);
18667         this.update();
18668         
18669     },
18670     
18671     onDecrementHours: function()
18672     {
18673         Roo.log('onDecrementHours');
18674         this.time = this.time.add(Date.HOUR, -1);
18675         this.update();
18676     },
18677     
18678     onIncrementMinutes: function()
18679     {
18680         Roo.log('onIncrementMinutes');
18681         this.time = this.time.add(Date.MINUTE, 1);
18682         this.update();
18683     },
18684     
18685     onDecrementMinutes: function()
18686     {
18687         Roo.log('onDecrementMinutes');
18688         this.time = this.time.add(Date.MINUTE, -1);
18689         this.update();
18690     },
18691     
18692     onTogglePeriod: function()
18693     {
18694         Roo.log('onTogglePeriod');
18695         this.time = this.time.add(Date.HOUR, 12);
18696         this.update();
18697     }
18698     
18699    
18700 });
18701
18702 Roo.apply(Roo.bootstrap.TimeField,  {
18703     
18704     content : {
18705         tag: 'tbody',
18706         cn: [
18707             {
18708                 tag: 'tr',
18709                 cn: [
18710                 {
18711                     tag: 'td',
18712                     colspan: '7'
18713                 }
18714                 ]
18715             }
18716         ]
18717     },
18718     
18719     footer : {
18720         tag: 'tfoot',
18721         cn: [
18722             {
18723                 tag: 'tr',
18724                 cn: [
18725                 {
18726                     tag: 'th',
18727                     colspan: '7',
18728                     cls: '',
18729                     cn: [
18730                         {
18731                             tag: 'button',
18732                             cls: 'btn btn-info ok',
18733                             html: 'OK'
18734                         }
18735                     ]
18736                 }
18737
18738                 ]
18739             }
18740         ]
18741     }
18742 });
18743
18744 Roo.apply(Roo.bootstrap.TimeField,  {
18745   
18746     template : {
18747         tag: 'div',
18748         cls: 'datepicker dropdown-menu',
18749         cn: [
18750             {
18751                 tag: 'div',
18752                 cls: 'datepicker-time',
18753                 cn: [
18754                 {
18755                     tag: 'table',
18756                     cls: 'table-condensed',
18757                     cn:[
18758                     Roo.bootstrap.TimeField.content,
18759                     Roo.bootstrap.TimeField.footer
18760                     ]
18761                 }
18762                 ]
18763             }
18764         ]
18765     }
18766 });
18767
18768  
18769
18770  /*
18771  * - LGPL
18772  *
18773  * MonthField
18774  * 
18775  */
18776
18777 /**
18778  * @class Roo.bootstrap.MonthField
18779  * @extends Roo.bootstrap.Input
18780  * Bootstrap MonthField class
18781  * 
18782  * @cfg {String} language default en
18783  * 
18784  * @constructor
18785  * Create a new MonthField
18786  * @param {Object} config The config object
18787  */
18788
18789 Roo.bootstrap.MonthField = function(config){
18790     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18791     
18792     this.addEvents({
18793         /**
18794          * @event show
18795          * Fires when this field show.
18796          * @param {Roo.bootstrap.MonthField} this
18797          * @param {Mixed} date The date value
18798          */
18799         show : true,
18800         /**
18801          * @event show
18802          * Fires when this field hide.
18803          * @param {Roo.bootstrap.MonthField} this
18804          * @param {Mixed} date The date value
18805          */
18806         hide : true,
18807         /**
18808          * @event select
18809          * Fires when select a date.
18810          * @param {Roo.bootstrap.MonthField} this
18811          * @param {String} oldvalue The old value
18812          * @param {String} newvalue The new value
18813          */
18814         select : true
18815     });
18816 };
18817
18818 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18819     
18820     onRender: function(ct, position)
18821     {
18822         
18823         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18824         
18825         this.language = this.language || 'en';
18826         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18827         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18828         
18829         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18830         this.isInline = false;
18831         this.isInput = true;
18832         this.component = this.el.select('.add-on', true).first() || false;
18833         this.component = (this.component && this.component.length === 0) ? false : this.component;
18834         this.hasInput = this.component && this.inputEL().length;
18835         
18836         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18837         
18838         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18839         
18840         this.picker().on('mousedown', this.onMousedown, this);
18841         this.picker().on('click', this.onClick, this);
18842         
18843         this.picker().addClass('datepicker-dropdown');
18844         
18845         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18846             v.setStyle('width', '189px');
18847         });
18848         
18849         this.fillMonths();
18850         
18851         this.update();
18852         
18853         if(this.isInline) {
18854             this.show();
18855         }
18856         
18857     },
18858     
18859     setValue: function(v, suppressEvent)
18860     {   
18861         var o = this.getValue();
18862         
18863         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18864         
18865         this.update();
18866
18867         if(suppressEvent !== true){
18868             this.fireEvent('select', this, o, v);
18869         }
18870         
18871     },
18872     
18873     getValue: function()
18874     {
18875         return this.value;
18876     },
18877     
18878     onClick: function(e) 
18879     {
18880         e.stopPropagation();
18881         e.preventDefault();
18882         
18883         var target = e.getTarget();
18884         
18885         if(target.nodeName.toLowerCase() === 'i'){
18886             target = Roo.get(target).dom.parentNode;
18887         }
18888         
18889         var nodeName = target.nodeName;
18890         var className = target.className;
18891         var html = target.innerHTML;
18892         
18893         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18894             return;
18895         }
18896         
18897         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18898         
18899         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18900         
18901         this.hide();
18902                         
18903     },
18904     
18905     picker : function()
18906     {
18907         return this.pickerEl;
18908     },
18909     
18910     fillMonths: function()
18911     {    
18912         var i = 0;
18913         var months = this.picker().select('>.datepicker-months td', true).first();
18914         
18915         months.dom.innerHTML = '';
18916         
18917         while (i < 12) {
18918             var month = {
18919                 tag: 'span',
18920                 cls: 'month',
18921                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18922             };
18923             
18924             months.createChild(month);
18925         }
18926         
18927     },
18928     
18929     update: function()
18930     {
18931         var _this = this;
18932         
18933         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18934             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18935         }
18936         
18937         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18938             e.removeClass('active');
18939             
18940             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18941                 e.addClass('active');
18942             }
18943         })
18944     },
18945     
18946     place: function()
18947     {
18948         if(this.isInline) {
18949             return;
18950         }
18951         
18952         this.picker().removeClass(['bottom', 'top']);
18953         
18954         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18955             /*
18956              * place to the top of element!
18957              *
18958              */
18959             
18960             this.picker().addClass('top');
18961             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18962             
18963             return;
18964         }
18965         
18966         this.picker().addClass('bottom');
18967         
18968         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18969     },
18970     
18971     onFocus : function()
18972     {
18973         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18974         this.show();
18975     },
18976     
18977     onBlur : function()
18978     {
18979         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18980         
18981         var d = this.inputEl().getValue();
18982         
18983         this.setValue(d);
18984                 
18985         this.hide();
18986     },
18987     
18988     show : function()
18989     {
18990         this.picker().show();
18991         this.picker().select('>.datepicker-months', true).first().show();
18992         this.update();
18993         this.place();
18994         
18995         this.fireEvent('show', this, this.date);
18996     },
18997     
18998     hide : function()
18999     {
19000         if(this.isInline) {
19001             return;
19002         }
19003         this.picker().hide();
19004         this.fireEvent('hide', this, this.date);
19005         
19006     },
19007     
19008     onMousedown: function(e)
19009     {
19010         e.stopPropagation();
19011         e.preventDefault();
19012     },
19013     
19014     keyup: function(e)
19015     {
19016         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19017         this.update();
19018     },
19019
19020     fireKey: function(e)
19021     {
19022         if (!this.picker().isVisible()){
19023             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19024                 this.show();
19025             }
19026             return;
19027         }
19028         
19029         var dir;
19030         
19031         switch(e.keyCode){
19032             case 27: // escape
19033                 this.hide();
19034                 e.preventDefault();
19035                 break;
19036             case 37: // left
19037             case 39: // right
19038                 dir = e.keyCode == 37 ? -1 : 1;
19039                 
19040                 this.vIndex = this.vIndex + dir;
19041                 
19042                 if(this.vIndex < 0){
19043                     this.vIndex = 0;
19044                 }
19045                 
19046                 if(this.vIndex > 11){
19047                     this.vIndex = 11;
19048                 }
19049                 
19050                 if(isNaN(this.vIndex)){
19051                     this.vIndex = 0;
19052                 }
19053                 
19054                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19055                 
19056                 break;
19057             case 38: // up
19058             case 40: // down
19059                 
19060                 dir = e.keyCode == 38 ? -1 : 1;
19061                 
19062                 this.vIndex = this.vIndex + dir * 4;
19063                 
19064                 if(this.vIndex < 0){
19065                     this.vIndex = 0;
19066                 }
19067                 
19068                 if(this.vIndex > 11){
19069                     this.vIndex = 11;
19070                 }
19071                 
19072                 if(isNaN(this.vIndex)){
19073                     this.vIndex = 0;
19074                 }
19075                 
19076                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19077                 break;
19078                 
19079             case 13: // enter
19080                 
19081                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19082                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19083                 }
19084                 
19085                 this.hide();
19086                 e.preventDefault();
19087                 break;
19088             case 9: // tab
19089                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19090                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19091                 }
19092                 this.hide();
19093                 break;
19094             case 16: // shift
19095             case 17: // ctrl
19096             case 18: // alt
19097                 break;
19098             default :
19099                 this.hide();
19100                 
19101         }
19102     },
19103     
19104     remove: function() 
19105     {
19106         this.picker().remove();
19107     }
19108    
19109 });
19110
19111 Roo.apply(Roo.bootstrap.MonthField,  {
19112     
19113     content : {
19114         tag: 'tbody',
19115         cn: [
19116         {
19117             tag: 'tr',
19118             cn: [
19119             {
19120                 tag: 'td',
19121                 colspan: '7'
19122             }
19123             ]
19124         }
19125         ]
19126     },
19127     
19128     dates:{
19129         en: {
19130             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19131             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19132         }
19133     }
19134 });
19135
19136 Roo.apply(Roo.bootstrap.MonthField,  {
19137   
19138     template : {
19139         tag: 'div',
19140         cls: 'datepicker dropdown-menu roo-dynamic',
19141         cn: [
19142             {
19143                 tag: 'div',
19144                 cls: 'datepicker-months',
19145                 cn: [
19146                 {
19147                     tag: 'table',
19148                     cls: 'table-condensed',
19149                     cn:[
19150                         Roo.bootstrap.DateField.content
19151                     ]
19152                 }
19153                 ]
19154             }
19155         ]
19156     }
19157 });
19158
19159  
19160
19161  
19162  /*
19163  * - LGPL
19164  *
19165  * CheckBox
19166  * 
19167  */
19168
19169 /**
19170  * @class Roo.bootstrap.CheckBox
19171  * @extends Roo.bootstrap.Input
19172  * Bootstrap CheckBox class
19173  * 
19174  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19175  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19176  * @cfg {String} boxLabel The text that appears beside the checkbox
19177  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19178  * @cfg {Boolean} checked initnal the element
19179  * @cfg {Boolean} inline inline the element (default false)
19180  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19181  * 
19182  * @constructor
19183  * Create a new CheckBox
19184  * @param {Object} config The config object
19185  */
19186
19187 Roo.bootstrap.CheckBox = function(config){
19188     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19189    
19190     this.addEvents({
19191         /**
19192         * @event check
19193         * Fires when the element is checked or unchecked.
19194         * @param {Roo.bootstrap.CheckBox} this This input
19195         * @param {Boolean} checked The new checked value
19196         */
19197        check : true
19198     });
19199     
19200 };
19201
19202 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19203   
19204     inputType: 'checkbox',
19205     inputValue: 1,
19206     valueOff: 0,
19207     boxLabel: false,
19208     checked: false,
19209     weight : false,
19210     inline: false,
19211     
19212     getAutoCreate : function()
19213     {
19214         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19215         
19216         var id = Roo.id();
19217         
19218         var cfg = {};
19219         
19220         cfg.cls = 'form-group ' + this.inputType; //input-group
19221         
19222         if(this.inline){
19223             cfg.cls += ' ' + this.inputType + '-inline';
19224         }
19225         
19226         var input =  {
19227             tag: 'input',
19228             id : id,
19229             type : this.inputType,
19230             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19231             cls : 'roo-' + this.inputType, //'form-box',
19232             placeholder : this.placeholder || ''
19233             
19234         };
19235         
19236         if (this.weight) { // Validity check?
19237             cfg.cls += " " + this.inputType + "-" + this.weight;
19238         }
19239         
19240         if (this.disabled) {
19241             input.disabled=true;
19242         }
19243         
19244         if(this.checked){
19245             input.checked = this.checked;
19246         }
19247         
19248         if (this.name) {
19249             input.name = this.name;
19250         }
19251         
19252         if (this.size) {
19253             input.cls += ' input-' + this.size;
19254         }
19255         
19256         var settings=this;
19257         
19258         ['xs','sm','md','lg'].map(function(size){
19259             if (settings[size]) {
19260                 cfg.cls += ' col-' + size + '-' + settings[size];
19261             }
19262         });
19263         
19264         var inputblock = input;
19265          
19266         if (this.before || this.after) {
19267             
19268             inputblock = {
19269                 cls : 'input-group',
19270                 cn :  [] 
19271             };
19272             
19273             if (this.before) {
19274                 inputblock.cn.push({
19275                     tag :'span',
19276                     cls : 'input-group-addon',
19277                     html : this.before
19278                 });
19279             }
19280             
19281             inputblock.cn.push(input);
19282             
19283             if (this.after) {
19284                 inputblock.cn.push({
19285                     tag :'span',
19286                     cls : 'input-group-addon',
19287                     html : this.after
19288                 });
19289             }
19290             
19291         }
19292         
19293         if (align ==='left' && this.fieldLabel.length) {
19294 //                Roo.log("left and has label");
19295                 cfg.cn = [
19296                     
19297                     {
19298                         tag: 'label',
19299                         'for' :  id,
19300                         cls : 'control-label col-md-' + this.labelWidth,
19301                         html : this.fieldLabel
19302                         
19303                     },
19304                     {
19305                         cls : "col-md-" + (12 - this.labelWidth), 
19306                         cn: [
19307                             inputblock
19308                         ]
19309                     }
19310                     
19311                 ];
19312         } else if ( this.fieldLabel.length) {
19313 //                Roo.log(" label");
19314                 cfg.cn = [
19315                    
19316                     {
19317                         tag: this.boxLabel ? 'span' : 'label',
19318                         'for': id,
19319                         cls: 'control-label box-input-label',
19320                         //cls : 'input-group-addon',
19321                         html : this.fieldLabel
19322                         
19323                     },
19324                     
19325                     inputblock
19326                     
19327                 ];
19328
19329         } else {
19330             
19331 //                Roo.log(" no label && no align");
19332                 cfg.cn = [  inputblock ] ;
19333                 
19334                 
19335         }
19336         
19337         if(this.boxLabel){
19338              var boxLabelCfg = {
19339                 tag: 'label',
19340                 //'for': id, // box label is handled by onclick - so no for...
19341                 cls: 'box-label',
19342                 html: this.boxLabel
19343             };
19344             
19345             if(this.tooltip){
19346                 boxLabelCfg.tooltip = this.tooltip;
19347             }
19348              
19349             cfg.cn.push(boxLabelCfg);
19350         }
19351         
19352         
19353        
19354         return cfg;
19355         
19356     },
19357     
19358     /**
19359      * return the real input element.
19360      */
19361     inputEl: function ()
19362     {
19363         return this.el.select('input.roo-' + this.inputType,true).first();
19364     },
19365     
19366     labelEl: function()
19367     {
19368         return this.el.select('label.control-label',true).first();
19369     },
19370     /* depricated... */
19371     
19372     label: function()
19373     {
19374         return this.labelEl();
19375     },
19376     
19377     boxLabelEl: function()
19378     {
19379         return this.el.select('label.box-label',true).first();
19380     },
19381     
19382     initEvents : function()
19383     {
19384 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19385         
19386         this.inputEl().on('click', this.onClick,  this);
19387         
19388         if (this.boxLabel) { 
19389             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19390         }
19391         
19392         this.startValue = this.getValue();
19393         
19394         if(this.groupId){
19395             Roo.bootstrap.CheckBox.register(this);
19396         }
19397     },
19398     
19399     onClick : function()
19400     {   
19401         this.setChecked(!this.checked);
19402     },
19403     
19404     setChecked : function(state,suppressEvent)
19405     {
19406         this.startValue = this.getValue();
19407         
19408         if(this.inputType == 'radio'){
19409             
19410             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19411                 e.dom.checked = false;
19412             });
19413             
19414             this.inputEl().dom.checked = true;
19415             
19416             this.inputEl().dom.value = this.inputValue;
19417             
19418             if(suppressEvent !== true){
19419                 this.fireEvent('check', this, true);
19420             }
19421             
19422             this.validate();
19423             
19424             return;
19425         }
19426         
19427         this.checked = state;
19428         
19429         this.inputEl().dom.checked = state;
19430         
19431         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19432         
19433         if(suppressEvent !== true){
19434             this.fireEvent('check', this, state);
19435         }
19436         
19437         this.validate();
19438     },
19439     
19440     getValue : function()
19441     {
19442         if(this.inputType == 'radio'){
19443             return this.getGroupValue();
19444         }
19445         
19446         return this.inputEl().getValue();
19447         
19448     },
19449     
19450     getGroupValue : function()
19451     {
19452         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19453             return '';
19454         }
19455         
19456         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19457     },
19458     
19459     setValue : function(v,suppressEvent)
19460     {
19461         if(this.inputType == 'radio'){
19462             this.setGroupValue(v, suppressEvent);
19463             return;
19464         }
19465         
19466         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19467         
19468         this.validate();
19469     },
19470     
19471     setGroupValue : function(v, suppressEvent)
19472     {
19473         this.startValue = this.getValue();
19474         
19475         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19476             e.dom.checked = false;
19477             
19478             if(e.dom.value == v){
19479                 e.dom.checked = true;
19480             }
19481         });
19482         
19483         if(suppressEvent !== true){
19484             this.fireEvent('check', this, true);
19485         }
19486
19487         this.validate();
19488         
19489         return;
19490     },
19491     
19492     validate : function()
19493     {
19494         if(
19495                 this.disabled || 
19496                 (this.inputType == 'radio' && this.validateRadio()) ||
19497                 (this.inputType == 'checkbox' && this.validateCheckbox())
19498         ){
19499             this.markValid();
19500             return true;
19501         }
19502         
19503         this.markInvalid();
19504         return false;
19505     },
19506     
19507     validateRadio : function()
19508     {
19509         var valid = false;
19510         
19511         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19512             if(!e.dom.checked){
19513                 return;
19514             }
19515             
19516             valid = true;
19517             
19518             return false;
19519         });
19520         
19521         return valid;
19522     },
19523     
19524     validateCheckbox : function()
19525     {
19526         if(!this.groupId){
19527             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19528         }
19529         
19530         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19531         
19532         if(!group){
19533             return false;
19534         }
19535         
19536         var r = false;
19537         
19538         for(var i in group){
19539             if(r){
19540                 break;
19541             }
19542             
19543             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19544         }
19545         
19546         return r;
19547     },
19548     
19549     /**
19550      * Mark this field as valid
19551      */
19552     markValid : function()
19553     {
19554         if(this.allowBlank){
19555             return;
19556         }
19557         
19558         var _this = this;
19559         
19560         this.fireEvent('valid', this);
19561         
19562         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19563         
19564         if(this.groupId){
19565             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19566         }
19567         
19568         if(label){
19569             label.markValid();
19570         }
19571         
19572         if(this.inputType == 'radio'){
19573             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19574                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19575                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19576             });
19577             
19578             return;
19579         }
19580         
19581         if(!this.groupId){
19582             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19583             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19584             return;
19585         }
19586         
19587         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19588             
19589         if(!group){
19590             return;
19591         }
19592         
19593         for(var i in group){
19594             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19595             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19596         }
19597     },
19598     
19599      /**
19600      * Mark this field as invalid
19601      * @param {String} msg The validation message
19602      */
19603     markInvalid : function(msg)
19604     {
19605         if(this.allowBlank){
19606             return;
19607         }
19608         
19609         var _this = this;
19610         
19611         this.fireEvent('invalid', this, msg);
19612         
19613         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19614         
19615         if(this.groupId){
19616             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19617         }
19618         
19619         if(label){
19620             label.markInvalid();
19621         }
19622             
19623         if(this.inputType == 'radio'){
19624             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19625                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19626                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19627             });
19628             
19629             return;
19630         }
19631         
19632         if(!this.groupId){
19633             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19634             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19635             return;
19636         }
19637         
19638         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19639         
19640         if(!group){
19641             return;
19642         }
19643         
19644         for(var i in group){
19645             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19646             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19647         }
19648         
19649     }
19650     
19651 });
19652
19653 Roo.apply(Roo.bootstrap.CheckBox, {
19654     
19655     groups: {},
19656     
19657      /**
19658     * register a CheckBox Group
19659     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19660     */
19661     register : function(checkbox)
19662     {
19663         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19664             this.groups[checkbox.groupId] = {};
19665         }
19666         
19667         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19668             return;
19669         }
19670         
19671         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19672         
19673     },
19674     /**
19675     * fetch a CheckBox Group based on the group ID
19676     * @param {string} the group ID
19677     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19678     */
19679     get: function(groupId) {
19680         if (typeof(this.groups[groupId]) == 'undefined') {
19681             return false;
19682         }
19683         
19684         return this.groups[groupId] ;
19685     }
19686     
19687     
19688 });
19689 /*
19690  * - LGPL
19691  *
19692  * Radio
19693  *
19694  *
19695  * not inline
19696  *<div class="radio">
19697   <label>
19698     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19699     Option one is this and that&mdash;be sure to include why it's great
19700   </label>
19701 </div>
19702  *
19703  *
19704  *inline
19705  *<span>
19706  *<label class="radio-inline">fieldLabel</label>
19707  *<label class="radio-inline">
19708   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19709 </label>
19710 <span>
19711  * 
19712  * 
19713  */
19714
19715 /**
19716  * @class Roo.bootstrap.Radio
19717  * @extends Roo.bootstrap.CheckBox
19718  * Bootstrap Radio class
19719
19720  * @constructor
19721  * Create a new Radio
19722  * @param {Object} config The config object
19723  */
19724
19725 Roo.bootstrap.Radio = function(config){
19726     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19727    
19728 };
19729
19730 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19731     
19732     inputType: 'radio',
19733     inputValue: '',
19734     valueOff: '',
19735     
19736     getAutoCreate : function()
19737     {
19738         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19739         align = align || 'left'; // default...
19740         
19741         
19742         
19743         var id = Roo.id();
19744         
19745         var cfg = {
19746                 tag : this.inline ? 'span' : 'div',
19747                 cls : '',
19748                 cn : []
19749         };
19750         
19751         var inline = this.inline ? ' radio-inline' : '';
19752         
19753         var lbl = {
19754                 tag: 'label' ,
19755                 // does not need for, as we wrap the input with it..
19756                 'for' : id,
19757                 cls : 'control-label box-label' + inline,
19758                 cn : []
19759         };
19760         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19761         
19762         var fieldLabel = {
19763             tag: 'label' ,
19764             //cls : 'control-label' + inline,
19765             html : this.fieldLabel,
19766             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19767         };
19768         
19769  
19770         
19771         
19772         var input =  {
19773             tag: 'input',
19774             id : id,
19775             type : this.inputType,
19776             //value : (!this.checked) ? this.valueOff : this.inputValue,
19777             value : this.inputValue,
19778             cls : 'roo-radio',
19779             placeholder : this.placeholder || '' // ?? needed????
19780             
19781         };
19782         if (this.weight) { // Validity check?
19783             input.cls += " radio-" + this.weight;
19784         }
19785         if (this.disabled) {
19786             input.disabled=true;
19787         }
19788         
19789         if(this.checked){
19790             input.checked = this.checked;
19791         }
19792         
19793         if (this.name) {
19794             input.name = this.name;
19795         }
19796         
19797         if (this.size) {
19798             input.cls += ' input-' + this.size;
19799         }
19800         
19801         //?? can span's inline have a width??
19802         
19803         var settings=this;
19804         ['xs','sm','md','lg'].map(function(size){
19805             if (settings[size]) {
19806                 cfg.cls += ' col-' + size + '-' + settings[size];
19807             }
19808         });
19809         
19810         var inputblock = input;
19811         
19812         if (this.before || this.after) {
19813             
19814             inputblock = {
19815                 cls : 'input-group',
19816                 tag : 'span',
19817                 cn :  [] 
19818             };
19819             if (this.before) {
19820                 inputblock.cn.push({
19821                     tag :'span',
19822                     cls : 'input-group-addon',
19823                     html : this.before
19824                 });
19825             }
19826             inputblock.cn.push(input);
19827             if (this.after) {
19828                 inputblock.cn.push({
19829                     tag :'span',
19830                     cls : 'input-group-addon',
19831                     html : this.after
19832                 });
19833             }
19834             
19835         };
19836         
19837         
19838         if (this.fieldLabel && this.fieldLabel.length) {
19839             cfg.cn.push(fieldLabel);
19840         }
19841        
19842         // normal bootstrap puts the input inside the label.
19843         // however with our styled version - it has to go after the input.
19844        
19845         //lbl.cn.push(inputblock);
19846         
19847         var lblwrap =  {
19848             tag: 'span',
19849             cls: 'radio' + inline,
19850             cn: [
19851                 inputblock,
19852                 lbl
19853             ]
19854         };
19855         
19856         cfg.cn.push( lblwrap);
19857         
19858         if(this.boxLabel){
19859             lbl.cn.push({
19860                 tag: 'span',
19861                 html: this.boxLabel
19862             })
19863         }
19864          
19865         
19866         return cfg;
19867         
19868     },
19869     
19870     initEvents : function()
19871     {
19872 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19873         
19874         this.inputEl().on('click', this.onClick,  this);
19875         if (this.boxLabel) {
19876             //Roo.log('find label');
19877             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19878         }
19879         
19880     },
19881     
19882     inputEl: function ()
19883     {
19884         return this.el.select('input.roo-radio',true).first();
19885     },
19886     onClick : function()
19887     {   
19888         Roo.log("click");
19889         this.setChecked(true);
19890     },
19891     
19892     setChecked : function(state,suppressEvent)
19893     {
19894         if(state){
19895             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19896                 v.dom.checked = false;
19897             });
19898         }
19899         Roo.log(this.inputEl().dom);
19900         this.checked = state;
19901         this.inputEl().dom.checked = state;
19902         
19903         if(suppressEvent !== true){
19904             this.fireEvent('check', this, state);
19905         }
19906         
19907         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19908         
19909     },
19910     
19911     getGroupValue : function()
19912     {
19913         var value = '';
19914         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19915             if(v.dom.checked == true){
19916                 value = v.dom.value;
19917             }
19918         });
19919         
19920         return value;
19921     },
19922     
19923     /**
19924      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19925      * @return {Mixed} value The field value
19926      */
19927     getValue : function(){
19928         return this.getGroupValue();
19929     }
19930     
19931 });
19932
19933  
19934 //<script type="text/javascript">
19935
19936 /*
19937  * Based  Ext JS Library 1.1.1
19938  * Copyright(c) 2006-2007, Ext JS, LLC.
19939  * LGPL
19940  *
19941  */
19942  
19943 /**
19944  * @class Roo.HtmlEditorCore
19945  * @extends Roo.Component
19946  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19947  *
19948  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19949  */
19950
19951 Roo.HtmlEditorCore = function(config){
19952     
19953     
19954     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19955     
19956     
19957     this.addEvents({
19958         /**
19959          * @event initialize
19960          * Fires when the editor is fully initialized (including the iframe)
19961          * @param {Roo.HtmlEditorCore} this
19962          */
19963         initialize: true,
19964         /**
19965          * @event activate
19966          * Fires when the editor is first receives the focus. Any insertion must wait
19967          * until after this event.
19968          * @param {Roo.HtmlEditorCore} this
19969          */
19970         activate: true,
19971          /**
19972          * @event beforesync
19973          * Fires before the textarea is updated with content from the editor iframe. Return false
19974          * to cancel the sync.
19975          * @param {Roo.HtmlEditorCore} this
19976          * @param {String} html
19977          */
19978         beforesync: true,
19979          /**
19980          * @event beforepush
19981          * Fires before the iframe editor is updated with content from the textarea. Return false
19982          * to cancel the push.
19983          * @param {Roo.HtmlEditorCore} this
19984          * @param {String} html
19985          */
19986         beforepush: true,
19987          /**
19988          * @event sync
19989          * Fires when the textarea is updated with content from the editor iframe.
19990          * @param {Roo.HtmlEditorCore} this
19991          * @param {String} html
19992          */
19993         sync: true,
19994          /**
19995          * @event push
19996          * Fires when the iframe editor is updated with content from the textarea.
19997          * @param {Roo.HtmlEditorCore} this
19998          * @param {String} html
19999          */
20000         push: true,
20001         
20002         /**
20003          * @event editorevent
20004          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20005          * @param {Roo.HtmlEditorCore} this
20006          */
20007         editorevent: true
20008         
20009     });
20010     
20011     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20012     
20013     // defaults : white / black...
20014     this.applyBlacklists();
20015     
20016     
20017     
20018 };
20019
20020
20021 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20022
20023
20024      /**
20025      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20026      */
20027     
20028     owner : false,
20029     
20030      /**
20031      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20032      *                        Roo.resizable.
20033      */
20034     resizable : false,
20035      /**
20036      * @cfg {Number} height (in pixels)
20037      */   
20038     height: 300,
20039    /**
20040      * @cfg {Number} width (in pixels)
20041      */   
20042     width: 500,
20043     
20044     /**
20045      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20046      * 
20047      */
20048     stylesheets: false,
20049     
20050     // id of frame..
20051     frameId: false,
20052     
20053     // private properties
20054     validationEvent : false,
20055     deferHeight: true,
20056     initialized : false,
20057     activated : false,
20058     sourceEditMode : false,
20059     onFocus : Roo.emptyFn,
20060     iframePad:3,
20061     hideMode:'offsets',
20062     
20063     clearUp: true,
20064     
20065     // blacklist + whitelisted elements..
20066     black: false,
20067     white: false,
20068      
20069     
20070
20071     /**
20072      * Protected method that will not generally be called directly. It
20073      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20074      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20075      */
20076     getDocMarkup : function(){
20077         // body styles..
20078         var st = '';
20079         
20080         // inherit styels from page...?? 
20081         if (this.stylesheets === false) {
20082             
20083             Roo.get(document.head).select('style').each(function(node) {
20084                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20085             });
20086             
20087             Roo.get(document.head).select('link').each(function(node) { 
20088                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20089             });
20090             
20091         } else if (!this.stylesheets.length) {
20092                 // simple..
20093                 st = '<style type="text/css">' +
20094                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20095                    '</style>';
20096         } else { 
20097             
20098         }
20099         
20100         st +=  '<style type="text/css">' +
20101             'IMG { cursor: pointer } ' +
20102         '</style>';
20103
20104         
20105         return '<html><head>' + st  +
20106             //<style type="text/css">' +
20107             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20108             //'</style>' +
20109             ' </head><body class="roo-htmleditor-body"></body></html>';
20110     },
20111
20112     // private
20113     onRender : function(ct, position)
20114     {
20115         var _t = this;
20116         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20117         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20118         
20119         
20120         this.el.dom.style.border = '0 none';
20121         this.el.dom.setAttribute('tabIndex', -1);
20122         this.el.addClass('x-hidden hide');
20123         
20124         
20125         
20126         if(Roo.isIE){ // fix IE 1px bogus margin
20127             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20128         }
20129        
20130         
20131         this.frameId = Roo.id();
20132         
20133          
20134         
20135         var iframe = this.owner.wrap.createChild({
20136             tag: 'iframe',
20137             cls: 'form-control', // bootstrap..
20138             id: this.frameId,
20139             name: this.frameId,
20140             frameBorder : 'no',
20141             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20142         }, this.el
20143         );
20144         
20145         
20146         this.iframe = iframe.dom;
20147
20148          this.assignDocWin();
20149         
20150         this.doc.designMode = 'on';
20151        
20152         this.doc.open();
20153         this.doc.write(this.getDocMarkup());
20154         this.doc.close();
20155
20156         
20157         var task = { // must defer to wait for browser to be ready
20158             run : function(){
20159                 //console.log("run task?" + this.doc.readyState);
20160                 this.assignDocWin();
20161                 if(this.doc.body || this.doc.readyState == 'complete'){
20162                     try {
20163                         this.doc.designMode="on";
20164                     } catch (e) {
20165                         return;
20166                     }
20167                     Roo.TaskMgr.stop(task);
20168                     this.initEditor.defer(10, this);
20169                 }
20170             },
20171             interval : 10,
20172             duration: 10000,
20173             scope: this
20174         };
20175         Roo.TaskMgr.start(task);
20176
20177     },
20178
20179     // private
20180     onResize : function(w, h)
20181     {
20182          Roo.log('resize: ' +w + ',' + h );
20183         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20184         if(!this.iframe){
20185             return;
20186         }
20187         if(typeof w == 'number'){
20188             
20189             this.iframe.style.width = w + 'px';
20190         }
20191         if(typeof h == 'number'){
20192             
20193             this.iframe.style.height = h + 'px';
20194             if(this.doc){
20195                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20196             }
20197         }
20198         
20199     },
20200
20201     /**
20202      * Toggles the editor between standard and source edit mode.
20203      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20204      */
20205     toggleSourceEdit : function(sourceEditMode){
20206         
20207         this.sourceEditMode = sourceEditMode === true;
20208         
20209         if(this.sourceEditMode){
20210  
20211             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20212             
20213         }else{
20214             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20215             //this.iframe.className = '';
20216             this.deferFocus();
20217         }
20218         //this.setSize(this.owner.wrap.getSize());
20219         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20220     },
20221
20222     
20223   
20224
20225     /**
20226      * Protected method that will not generally be called directly. If you need/want
20227      * custom HTML cleanup, this is the method you should override.
20228      * @param {String} html The HTML to be cleaned
20229      * return {String} The cleaned HTML
20230      */
20231     cleanHtml : function(html){
20232         html = String(html);
20233         if(html.length > 5){
20234             if(Roo.isSafari){ // strip safari nonsense
20235                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20236             }
20237         }
20238         if(html == '&nbsp;'){
20239             html = '';
20240         }
20241         return html;
20242     },
20243
20244     /**
20245      * HTML Editor -> Textarea
20246      * Protected method that will not generally be called directly. Syncs the contents
20247      * of the editor iframe with the textarea.
20248      */
20249     syncValue : function(){
20250         if(this.initialized){
20251             var bd = (this.doc.body || this.doc.documentElement);
20252             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20253             var html = bd.innerHTML;
20254             if(Roo.isSafari){
20255                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20256                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20257                 if(m && m[1]){
20258                     html = '<div style="'+m[0]+'">' + html + '</div>';
20259                 }
20260             }
20261             html = this.cleanHtml(html);
20262             // fix up the special chars.. normaly like back quotes in word...
20263             // however we do not want to do this with chinese..
20264             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20265                 var cc = b.charCodeAt();
20266                 if (
20267                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20268                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20269                     (cc >= 0xf900 && cc < 0xfb00 )
20270                 ) {
20271                         return b;
20272                 }
20273                 return "&#"+cc+";" 
20274             });
20275             if(this.owner.fireEvent('beforesync', this, html) !== false){
20276                 this.el.dom.value = html;
20277                 this.owner.fireEvent('sync', this, html);
20278             }
20279         }
20280     },
20281
20282     /**
20283      * Protected method that will not generally be called directly. Pushes the value of the textarea
20284      * into the iframe editor.
20285      */
20286     pushValue : function(){
20287         if(this.initialized){
20288             var v = this.el.dom.value.trim();
20289             
20290 //            if(v.length < 1){
20291 //                v = '&#160;';
20292 //            }
20293             
20294             if(this.owner.fireEvent('beforepush', this, v) !== false){
20295                 var d = (this.doc.body || this.doc.documentElement);
20296                 d.innerHTML = v;
20297                 this.cleanUpPaste();
20298                 this.el.dom.value = d.innerHTML;
20299                 this.owner.fireEvent('push', this, v);
20300             }
20301         }
20302     },
20303
20304     // private
20305     deferFocus : function(){
20306         this.focus.defer(10, this);
20307     },
20308
20309     // doc'ed in Field
20310     focus : function(){
20311         if(this.win && !this.sourceEditMode){
20312             this.win.focus();
20313         }else{
20314             this.el.focus();
20315         }
20316     },
20317     
20318     assignDocWin: function()
20319     {
20320         var iframe = this.iframe;
20321         
20322          if(Roo.isIE){
20323             this.doc = iframe.contentWindow.document;
20324             this.win = iframe.contentWindow;
20325         } else {
20326 //            if (!Roo.get(this.frameId)) {
20327 //                return;
20328 //            }
20329 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20330 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20331             
20332             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20333                 return;
20334             }
20335             
20336             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20337             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20338         }
20339     },
20340     
20341     // private
20342     initEditor : function(){
20343         //console.log("INIT EDITOR");
20344         this.assignDocWin();
20345         
20346         
20347         
20348         this.doc.designMode="on";
20349         this.doc.open();
20350         this.doc.write(this.getDocMarkup());
20351         this.doc.close();
20352         
20353         var dbody = (this.doc.body || this.doc.documentElement);
20354         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20355         // this copies styles from the containing element into thsi one..
20356         // not sure why we need all of this..
20357         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20358         
20359         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20360         //ss['background-attachment'] = 'fixed'; // w3c
20361         dbody.bgProperties = 'fixed'; // ie
20362         //Roo.DomHelper.applyStyles(dbody, ss);
20363         Roo.EventManager.on(this.doc, {
20364             //'mousedown': this.onEditorEvent,
20365             'mouseup': this.onEditorEvent,
20366             'dblclick': this.onEditorEvent,
20367             'click': this.onEditorEvent,
20368             'keyup': this.onEditorEvent,
20369             buffer:100,
20370             scope: this
20371         });
20372         if(Roo.isGecko){
20373             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20374         }
20375         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20376             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20377         }
20378         this.initialized = true;
20379
20380         this.owner.fireEvent('initialize', this);
20381         this.pushValue();
20382     },
20383
20384     // private
20385     onDestroy : function(){
20386         
20387         
20388         
20389         if(this.rendered){
20390             
20391             //for (var i =0; i < this.toolbars.length;i++) {
20392             //    // fixme - ask toolbars for heights?
20393             //    this.toolbars[i].onDestroy();
20394            // }
20395             
20396             //this.wrap.dom.innerHTML = '';
20397             //this.wrap.remove();
20398         }
20399     },
20400
20401     // private
20402     onFirstFocus : function(){
20403         
20404         this.assignDocWin();
20405         
20406         
20407         this.activated = true;
20408          
20409     
20410         if(Roo.isGecko){ // prevent silly gecko errors
20411             this.win.focus();
20412             var s = this.win.getSelection();
20413             if(!s.focusNode || s.focusNode.nodeType != 3){
20414                 var r = s.getRangeAt(0);
20415                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20416                 r.collapse(true);
20417                 this.deferFocus();
20418             }
20419             try{
20420                 this.execCmd('useCSS', true);
20421                 this.execCmd('styleWithCSS', false);
20422             }catch(e){}
20423         }
20424         this.owner.fireEvent('activate', this);
20425     },
20426
20427     // private
20428     adjustFont: function(btn){
20429         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20430         //if(Roo.isSafari){ // safari
20431         //    adjust *= 2;
20432        // }
20433         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20434         if(Roo.isSafari){ // safari
20435             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20436             v =  (v < 10) ? 10 : v;
20437             v =  (v > 48) ? 48 : v;
20438             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20439             
20440         }
20441         
20442         
20443         v = Math.max(1, v+adjust);
20444         
20445         this.execCmd('FontSize', v  );
20446     },
20447
20448     onEditorEvent : function(e)
20449     {
20450         this.owner.fireEvent('editorevent', this, e);
20451       //  this.updateToolbar();
20452         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20453     },
20454
20455     insertTag : function(tg)
20456     {
20457         // could be a bit smarter... -> wrap the current selected tRoo..
20458         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20459             
20460             range = this.createRange(this.getSelection());
20461             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20462             wrappingNode.appendChild(range.extractContents());
20463             range.insertNode(wrappingNode);
20464
20465             return;
20466             
20467             
20468             
20469         }
20470         this.execCmd("formatblock",   tg);
20471         
20472     },
20473     
20474     insertText : function(txt)
20475     {
20476         
20477         
20478         var range = this.createRange();
20479         range.deleteContents();
20480                //alert(Sender.getAttribute('label'));
20481                
20482         range.insertNode(this.doc.createTextNode(txt));
20483     } ,
20484     
20485      
20486
20487     /**
20488      * Executes a Midas editor command on the editor document and performs necessary focus and
20489      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20490      * @param {String} cmd The Midas command
20491      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20492      */
20493     relayCmd : function(cmd, value){
20494         this.win.focus();
20495         this.execCmd(cmd, value);
20496         this.owner.fireEvent('editorevent', this);
20497         //this.updateToolbar();
20498         this.owner.deferFocus();
20499     },
20500
20501     /**
20502      * Executes a Midas editor command directly on the editor document.
20503      * For visual commands, you should use {@link #relayCmd} instead.
20504      * <b>This should only be called after the editor is initialized.</b>
20505      * @param {String} cmd The Midas command
20506      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20507      */
20508     execCmd : function(cmd, value){
20509         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20510         this.syncValue();
20511     },
20512  
20513  
20514    
20515     /**
20516      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20517      * to insert tRoo.
20518      * @param {String} text | dom node.. 
20519      */
20520     insertAtCursor : function(text)
20521     {
20522         
20523         
20524         
20525         if(!this.activated){
20526             return;
20527         }
20528         /*
20529         if(Roo.isIE){
20530             this.win.focus();
20531             var r = this.doc.selection.createRange();
20532             if(r){
20533                 r.collapse(true);
20534                 r.pasteHTML(text);
20535                 this.syncValue();
20536                 this.deferFocus();
20537             
20538             }
20539             return;
20540         }
20541         */
20542         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20543             this.win.focus();
20544             
20545             
20546             // from jquery ui (MIT licenced)
20547             var range, node;
20548             var win = this.win;
20549             
20550             if (win.getSelection && win.getSelection().getRangeAt) {
20551                 range = win.getSelection().getRangeAt(0);
20552                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20553                 range.insertNode(node);
20554             } else if (win.document.selection && win.document.selection.createRange) {
20555                 // no firefox support
20556                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20557                 win.document.selection.createRange().pasteHTML(txt);
20558             } else {
20559                 // no firefox support
20560                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20561                 this.execCmd('InsertHTML', txt);
20562             } 
20563             
20564             this.syncValue();
20565             
20566             this.deferFocus();
20567         }
20568     },
20569  // private
20570     mozKeyPress : function(e){
20571         if(e.ctrlKey){
20572             var c = e.getCharCode(), cmd;
20573           
20574             if(c > 0){
20575                 c = String.fromCharCode(c).toLowerCase();
20576                 switch(c){
20577                     case 'b':
20578                         cmd = 'bold';
20579                         break;
20580                     case 'i':
20581                         cmd = 'italic';
20582                         break;
20583                     
20584                     case 'u':
20585                         cmd = 'underline';
20586                         break;
20587                     
20588                     case 'v':
20589                         this.cleanUpPaste.defer(100, this);
20590                         return;
20591                         
20592                 }
20593                 if(cmd){
20594                     this.win.focus();
20595                     this.execCmd(cmd);
20596                     this.deferFocus();
20597                     e.preventDefault();
20598                 }
20599                 
20600             }
20601         }
20602     },
20603
20604     // private
20605     fixKeys : function(){ // load time branching for fastest keydown performance
20606         if(Roo.isIE){
20607             return function(e){
20608                 var k = e.getKey(), r;
20609                 if(k == e.TAB){
20610                     e.stopEvent();
20611                     r = this.doc.selection.createRange();
20612                     if(r){
20613                         r.collapse(true);
20614                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20615                         this.deferFocus();
20616                     }
20617                     return;
20618                 }
20619                 
20620                 if(k == e.ENTER){
20621                     r = this.doc.selection.createRange();
20622                     if(r){
20623                         var target = r.parentElement();
20624                         if(!target || target.tagName.toLowerCase() != 'li'){
20625                             e.stopEvent();
20626                             r.pasteHTML('<br />');
20627                             r.collapse(false);
20628                             r.select();
20629                         }
20630                     }
20631                 }
20632                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20633                     this.cleanUpPaste.defer(100, this);
20634                     return;
20635                 }
20636                 
20637                 
20638             };
20639         }else if(Roo.isOpera){
20640             return function(e){
20641                 var k = e.getKey();
20642                 if(k == e.TAB){
20643                     e.stopEvent();
20644                     this.win.focus();
20645                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20646                     this.deferFocus();
20647                 }
20648                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20649                     this.cleanUpPaste.defer(100, this);
20650                     return;
20651                 }
20652                 
20653             };
20654         }else if(Roo.isSafari){
20655             return function(e){
20656                 var k = e.getKey();
20657                 
20658                 if(k == e.TAB){
20659                     e.stopEvent();
20660                     this.execCmd('InsertText','\t');
20661                     this.deferFocus();
20662                     return;
20663                 }
20664                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20665                     this.cleanUpPaste.defer(100, this);
20666                     return;
20667                 }
20668                 
20669              };
20670         }
20671     }(),
20672     
20673     getAllAncestors: function()
20674     {
20675         var p = this.getSelectedNode();
20676         var a = [];
20677         if (!p) {
20678             a.push(p); // push blank onto stack..
20679             p = this.getParentElement();
20680         }
20681         
20682         
20683         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20684             a.push(p);
20685             p = p.parentNode;
20686         }
20687         a.push(this.doc.body);
20688         return a;
20689     },
20690     lastSel : false,
20691     lastSelNode : false,
20692     
20693     
20694     getSelection : function() 
20695     {
20696         this.assignDocWin();
20697         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20698     },
20699     
20700     getSelectedNode: function() 
20701     {
20702         // this may only work on Gecko!!!
20703         
20704         // should we cache this!!!!
20705         
20706         
20707         
20708          
20709         var range = this.createRange(this.getSelection()).cloneRange();
20710         
20711         if (Roo.isIE) {
20712             var parent = range.parentElement();
20713             while (true) {
20714                 var testRange = range.duplicate();
20715                 testRange.moveToElementText(parent);
20716                 if (testRange.inRange(range)) {
20717                     break;
20718                 }
20719                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20720                     break;
20721                 }
20722                 parent = parent.parentElement;
20723             }
20724             return parent;
20725         }
20726         
20727         // is ancestor a text element.
20728         var ac =  range.commonAncestorContainer;
20729         if (ac.nodeType == 3) {
20730             ac = ac.parentNode;
20731         }
20732         
20733         var ar = ac.childNodes;
20734          
20735         var nodes = [];
20736         var other_nodes = [];
20737         var has_other_nodes = false;
20738         for (var i=0;i<ar.length;i++) {
20739             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20740                 continue;
20741             }
20742             // fullly contained node.
20743             
20744             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20745                 nodes.push(ar[i]);
20746                 continue;
20747             }
20748             
20749             // probably selected..
20750             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20751                 other_nodes.push(ar[i]);
20752                 continue;
20753             }
20754             // outer..
20755             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20756                 continue;
20757             }
20758             
20759             
20760             has_other_nodes = true;
20761         }
20762         if (!nodes.length && other_nodes.length) {
20763             nodes= other_nodes;
20764         }
20765         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20766             return false;
20767         }
20768         
20769         return nodes[0];
20770     },
20771     createRange: function(sel)
20772     {
20773         // this has strange effects when using with 
20774         // top toolbar - not sure if it's a great idea.
20775         //this.editor.contentWindow.focus();
20776         if (typeof sel != "undefined") {
20777             try {
20778                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20779             } catch(e) {
20780                 return this.doc.createRange();
20781             }
20782         } else {
20783             return this.doc.createRange();
20784         }
20785     },
20786     getParentElement: function()
20787     {
20788         
20789         this.assignDocWin();
20790         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20791         
20792         var range = this.createRange(sel);
20793          
20794         try {
20795             var p = range.commonAncestorContainer;
20796             while (p.nodeType == 3) { // text node
20797                 p = p.parentNode;
20798             }
20799             return p;
20800         } catch (e) {
20801             return null;
20802         }
20803     
20804     },
20805     /***
20806      *
20807      * Range intersection.. the hard stuff...
20808      *  '-1' = before
20809      *  '0' = hits..
20810      *  '1' = after.
20811      *         [ -- selected range --- ]
20812      *   [fail]                        [fail]
20813      *
20814      *    basically..
20815      *      if end is before start or  hits it. fail.
20816      *      if start is after end or hits it fail.
20817      *
20818      *   if either hits (but other is outside. - then it's not 
20819      *   
20820      *    
20821      **/
20822     
20823     
20824     // @see http://www.thismuchiknow.co.uk/?p=64.
20825     rangeIntersectsNode : function(range, node)
20826     {
20827         var nodeRange = node.ownerDocument.createRange();
20828         try {
20829             nodeRange.selectNode(node);
20830         } catch (e) {
20831             nodeRange.selectNodeContents(node);
20832         }
20833     
20834         var rangeStartRange = range.cloneRange();
20835         rangeStartRange.collapse(true);
20836     
20837         var rangeEndRange = range.cloneRange();
20838         rangeEndRange.collapse(false);
20839     
20840         var nodeStartRange = nodeRange.cloneRange();
20841         nodeStartRange.collapse(true);
20842     
20843         var nodeEndRange = nodeRange.cloneRange();
20844         nodeEndRange.collapse(false);
20845     
20846         return rangeStartRange.compareBoundaryPoints(
20847                  Range.START_TO_START, nodeEndRange) == -1 &&
20848                rangeEndRange.compareBoundaryPoints(
20849                  Range.START_TO_START, nodeStartRange) == 1;
20850         
20851          
20852     },
20853     rangeCompareNode : function(range, node)
20854     {
20855         var nodeRange = node.ownerDocument.createRange();
20856         try {
20857             nodeRange.selectNode(node);
20858         } catch (e) {
20859             nodeRange.selectNodeContents(node);
20860         }
20861         
20862         
20863         range.collapse(true);
20864     
20865         nodeRange.collapse(true);
20866      
20867         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20868         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20869          
20870         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20871         
20872         var nodeIsBefore   =  ss == 1;
20873         var nodeIsAfter    = ee == -1;
20874         
20875         if (nodeIsBefore && nodeIsAfter) {
20876             return 0; // outer
20877         }
20878         if (!nodeIsBefore && nodeIsAfter) {
20879             return 1; //right trailed.
20880         }
20881         
20882         if (nodeIsBefore && !nodeIsAfter) {
20883             return 2;  // left trailed.
20884         }
20885         // fully contined.
20886         return 3;
20887     },
20888
20889     // private? - in a new class?
20890     cleanUpPaste :  function()
20891     {
20892         // cleans up the whole document..
20893         Roo.log('cleanuppaste');
20894         
20895         this.cleanUpChildren(this.doc.body);
20896         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20897         if (clean != this.doc.body.innerHTML) {
20898             this.doc.body.innerHTML = clean;
20899         }
20900         
20901     },
20902     
20903     cleanWordChars : function(input) {// change the chars to hex code
20904         var he = Roo.HtmlEditorCore;
20905         
20906         var output = input;
20907         Roo.each(he.swapCodes, function(sw) { 
20908             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20909             
20910             output = output.replace(swapper, sw[1]);
20911         });
20912         
20913         return output;
20914     },
20915     
20916     
20917     cleanUpChildren : function (n)
20918     {
20919         if (!n.childNodes.length) {
20920             return;
20921         }
20922         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20923            this.cleanUpChild(n.childNodes[i]);
20924         }
20925     },
20926     
20927     
20928         
20929     
20930     cleanUpChild : function (node)
20931     {
20932         var ed = this;
20933         //console.log(node);
20934         if (node.nodeName == "#text") {
20935             // clean up silly Windows -- stuff?
20936             return; 
20937         }
20938         if (node.nodeName == "#comment") {
20939             node.parentNode.removeChild(node);
20940             // clean up silly Windows -- stuff?
20941             return; 
20942         }
20943         var lcname = node.tagName.toLowerCase();
20944         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20945         // whitelist of tags..
20946         
20947         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20948             // remove node.
20949             node.parentNode.removeChild(node);
20950             return;
20951             
20952         }
20953         
20954         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20955         
20956         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20957         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20958         
20959         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20960         //    remove_keep_children = true;
20961         //}
20962         
20963         if (remove_keep_children) {
20964             this.cleanUpChildren(node);
20965             // inserts everything just before this node...
20966             while (node.childNodes.length) {
20967                 var cn = node.childNodes[0];
20968                 node.removeChild(cn);
20969                 node.parentNode.insertBefore(cn, node);
20970             }
20971             node.parentNode.removeChild(node);
20972             return;
20973         }
20974         
20975         if (!node.attributes || !node.attributes.length) {
20976             this.cleanUpChildren(node);
20977             return;
20978         }
20979         
20980         function cleanAttr(n,v)
20981         {
20982             
20983             if (v.match(/^\./) || v.match(/^\//)) {
20984                 return;
20985             }
20986             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20987                 return;
20988             }
20989             if (v.match(/^#/)) {
20990                 return;
20991             }
20992 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20993             node.removeAttribute(n);
20994             
20995         }
20996         
20997         var cwhite = this.cwhite;
20998         var cblack = this.cblack;
20999             
21000         function cleanStyle(n,v)
21001         {
21002             if (v.match(/expression/)) { //XSS?? should we even bother..
21003                 node.removeAttribute(n);
21004                 return;
21005             }
21006             
21007             var parts = v.split(/;/);
21008             var clean = [];
21009             
21010             Roo.each(parts, function(p) {
21011                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21012                 if (!p.length) {
21013                     return true;
21014                 }
21015                 var l = p.split(':').shift().replace(/\s+/g,'');
21016                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21017                 
21018                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21019 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21020                     //node.removeAttribute(n);
21021                     return true;
21022                 }
21023                 //Roo.log()
21024                 // only allow 'c whitelisted system attributes'
21025                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21026 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21027                     //node.removeAttribute(n);
21028                     return true;
21029                 }
21030                 
21031                 
21032                  
21033                 
21034                 clean.push(p);
21035                 return true;
21036             });
21037             if (clean.length) { 
21038                 node.setAttribute(n, clean.join(';'));
21039             } else {
21040                 node.removeAttribute(n);
21041             }
21042             
21043         }
21044         
21045         
21046         for (var i = node.attributes.length-1; i > -1 ; i--) {
21047             var a = node.attributes[i];
21048             //console.log(a);
21049             
21050             if (a.name.toLowerCase().substr(0,2)=='on')  {
21051                 node.removeAttribute(a.name);
21052                 continue;
21053             }
21054             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21055                 node.removeAttribute(a.name);
21056                 continue;
21057             }
21058             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21059                 cleanAttr(a.name,a.value); // fixme..
21060                 continue;
21061             }
21062             if (a.name == 'style') {
21063                 cleanStyle(a.name,a.value);
21064                 continue;
21065             }
21066             /// clean up MS crap..
21067             // tecnically this should be a list of valid class'es..
21068             
21069             
21070             if (a.name == 'class') {
21071                 if (a.value.match(/^Mso/)) {
21072                     node.className = '';
21073                 }
21074                 
21075                 if (a.value.match(/body/)) {
21076                     node.className = '';
21077                 }
21078                 continue;
21079             }
21080             
21081             // style cleanup!?
21082             // class cleanup?
21083             
21084         }
21085         
21086         
21087         this.cleanUpChildren(node);
21088         
21089         
21090     },
21091     
21092     /**
21093      * Clean up MS wordisms...
21094      */
21095     cleanWord : function(node)
21096     {
21097         
21098         
21099         if (!node) {
21100             this.cleanWord(this.doc.body);
21101             return;
21102         }
21103         if (node.nodeName == "#text") {
21104             // clean up silly Windows -- stuff?
21105             return; 
21106         }
21107         if (node.nodeName == "#comment") {
21108             node.parentNode.removeChild(node);
21109             // clean up silly Windows -- stuff?
21110             return; 
21111         }
21112         
21113         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21114             node.parentNode.removeChild(node);
21115             return;
21116         }
21117         
21118         // remove - but keep children..
21119         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21120             while (node.childNodes.length) {
21121                 var cn = node.childNodes[0];
21122                 node.removeChild(cn);
21123                 node.parentNode.insertBefore(cn, node);
21124             }
21125             node.parentNode.removeChild(node);
21126             this.iterateChildren(node, this.cleanWord);
21127             return;
21128         }
21129         // clean styles
21130         if (node.className.length) {
21131             
21132             var cn = node.className.split(/\W+/);
21133             var cna = [];
21134             Roo.each(cn, function(cls) {
21135                 if (cls.match(/Mso[a-zA-Z]+/)) {
21136                     return;
21137                 }
21138                 cna.push(cls);
21139             });
21140             node.className = cna.length ? cna.join(' ') : '';
21141             if (!cna.length) {
21142                 node.removeAttribute("class");
21143             }
21144         }
21145         
21146         if (node.hasAttribute("lang")) {
21147             node.removeAttribute("lang");
21148         }
21149         
21150         if (node.hasAttribute("style")) {
21151             
21152             var styles = node.getAttribute("style").split(";");
21153             var nstyle = [];
21154             Roo.each(styles, function(s) {
21155                 if (!s.match(/:/)) {
21156                     return;
21157                 }
21158                 var kv = s.split(":");
21159                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21160                     return;
21161                 }
21162                 // what ever is left... we allow.
21163                 nstyle.push(s);
21164             });
21165             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21166             if (!nstyle.length) {
21167                 node.removeAttribute('style');
21168             }
21169         }
21170         this.iterateChildren(node, this.cleanWord);
21171         
21172         
21173         
21174     },
21175     /**
21176      * iterateChildren of a Node, calling fn each time, using this as the scole..
21177      * @param {DomNode} node node to iterate children of.
21178      * @param {Function} fn method of this class to call on each item.
21179      */
21180     iterateChildren : function(node, fn)
21181     {
21182         if (!node.childNodes.length) {
21183                 return;
21184         }
21185         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21186            fn.call(this, node.childNodes[i])
21187         }
21188     },
21189     
21190     
21191     /**
21192      * cleanTableWidths.
21193      *
21194      * Quite often pasting from word etc.. results in tables with column and widths.
21195      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21196      *
21197      */
21198     cleanTableWidths : function(node)
21199     {
21200          
21201          
21202         if (!node) {
21203             this.cleanTableWidths(this.doc.body);
21204             return;
21205         }
21206         
21207         // ignore list...
21208         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21209             return; 
21210         }
21211         Roo.log(node.tagName);
21212         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21213             this.iterateChildren(node, this.cleanTableWidths);
21214             return;
21215         }
21216         if (node.hasAttribute('width')) {
21217             node.removeAttribute('width');
21218         }
21219         
21220          
21221         if (node.hasAttribute("style")) {
21222             // pretty basic...
21223             
21224             var styles = node.getAttribute("style").split(";");
21225             var nstyle = [];
21226             Roo.each(styles, function(s) {
21227                 if (!s.match(/:/)) {
21228                     return;
21229                 }
21230                 var kv = s.split(":");
21231                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21232                     return;
21233                 }
21234                 // what ever is left... we allow.
21235                 nstyle.push(s);
21236             });
21237             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21238             if (!nstyle.length) {
21239                 node.removeAttribute('style');
21240             }
21241         }
21242         
21243         this.iterateChildren(node, this.cleanTableWidths);
21244         
21245         
21246     },
21247     
21248     
21249     
21250     
21251     domToHTML : function(currentElement, depth, nopadtext) {
21252         
21253         depth = depth || 0;
21254         nopadtext = nopadtext || false;
21255     
21256         if (!currentElement) {
21257             return this.domToHTML(this.doc.body);
21258         }
21259         
21260         //Roo.log(currentElement);
21261         var j;
21262         var allText = false;
21263         var nodeName = currentElement.nodeName;
21264         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21265         
21266         if  (nodeName == '#text') {
21267             
21268             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21269         }
21270         
21271         
21272         var ret = '';
21273         if (nodeName != 'BODY') {
21274              
21275             var i = 0;
21276             // Prints the node tagName, such as <A>, <IMG>, etc
21277             if (tagName) {
21278                 var attr = [];
21279                 for(i = 0; i < currentElement.attributes.length;i++) {
21280                     // quoting?
21281                     var aname = currentElement.attributes.item(i).name;
21282                     if (!currentElement.attributes.item(i).value.length) {
21283                         continue;
21284                     }
21285                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21286                 }
21287                 
21288                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21289             } 
21290             else {
21291                 
21292                 // eack
21293             }
21294         } else {
21295             tagName = false;
21296         }
21297         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21298             return ret;
21299         }
21300         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21301             nopadtext = true;
21302         }
21303         
21304         
21305         // Traverse the tree
21306         i = 0;
21307         var currentElementChild = currentElement.childNodes.item(i);
21308         var allText = true;
21309         var innerHTML  = '';
21310         lastnode = '';
21311         while (currentElementChild) {
21312             // Formatting code (indent the tree so it looks nice on the screen)
21313             var nopad = nopadtext;
21314             if (lastnode == 'SPAN') {
21315                 nopad  = true;
21316             }
21317             // text
21318             if  (currentElementChild.nodeName == '#text') {
21319                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21320                 toadd = nopadtext ? toadd : toadd.trim();
21321                 if (!nopad && toadd.length > 80) {
21322                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21323                 }
21324                 innerHTML  += toadd;
21325                 
21326                 i++;
21327                 currentElementChild = currentElement.childNodes.item(i);
21328                 lastNode = '';
21329                 continue;
21330             }
21331             allText = false;
21332             
21333             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21334                 
21335             // Recursively traverse the tree structure of the child node
21336             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21337             lastnode = currentElementChild.nodeName;
21338             i++;
21339             currentElementChild=currentElement.childNodes.item(i);
21340         }
21341         
21342         ret += innerHTML;
21343         
21344         if (!allText) {
21345                 // The remaining code is mostly for formatting the tree
21346             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21347         }
21348         
21349         
21350         if (tagName) {
21351             ret+= "</"+tagName+">";
21352         }
21353         return ret;
21354         
21355     },
21356         
21357     applyBlacklists : function()
21358     {
21359         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21360         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21361         
21362         this.white = [];
21363         this.black = [];
21364         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21365             if (b.indexOf(tag) > -1) {
21366                 return;
21367             }
21368             this.white.push(tag);
21369             
21370         }, this);
21371         
21372         Roo.each(w, function(tag) {
21373             if (b.indexOf(tag) > -1) {
21374                 return;
21375             }
21376             if (this.white.indexOf(tag) > -1) {
21377                 return;
21378             }
21379             this.white.push(tag);
21380             
21381         }, this);
21382         
21383         
21384         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21385             if (w.indexOf(tag) > -1) {
21386                 return;
21387             }
21388             this.black.push(tag);
21389             
21390         }, this);
21391         
21392         Roo.each(b, function(tag) {
21393             if (w.indexOf(tag) > -1) {
21394                 return;
21395             }
21396             if (this.black.indexOf(tag) > -1) {
21397                 return;
21398             }
21399             this.black.push(tag);
21400             
21401         }, this);
21402         
21403         
21404         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21405         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21406         
21407         this.cwhite = [];
21408         this.cblack = [];
21409         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21410             if (b.indexOf(tag) > -1) {
21411                 return;
21412             }
21413             this.cwhite.push(tag);
21414             
21415         }, this);
21416         
21417         Roo.each(w, function(tag) {
21418             if (b.indexOf(tag) > -1) {
21419                 return;
21420             }
21421             if (this.cwhite.indexOf(tag) > -1) {
21422                 return;
21423             }
21424             this.cwhite.push(tag);
21425             
21426         }, this);
21427         
21428         
21429         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21430             if (w.indexOf(tag) > -1) {
21431                 return;
21432             }
21433             this.cblack.push(tag);
21434             
21435         }, this);
21436         
21437         Roo.each(b, function(tag) {
21438             if (w.indexOf(tag) > -1) {
21439                 return;
21440             }
21441             if (this.cblack.indexOf(tag) > -1) {
21442                 return;
21443             }
21444             this.cblack.push(tag);
21445             
21446         }, this);
21447     },
21448     
21449     setStylesheets : function(stylesheets)
21450     {
21451         if(typeof(stylesheets) == 'string'){
21452             Roo.get(this.iframe.contentDocument.head).createChild({
21453                 tag : 'link',
21454                 rel : 'stylesheet',
21455                 type : 'text/css',
21456                 href : stylesheets
21457             });
21458             
21459             return;
21460         }
21461         var _this = this;
21462      
21463         Roo.each(stylesheets, function(s) {
21464             if(!s.length){
21465                 return;
21466             }
21467             
21468             Roo.get(_this.iframe.contentDocument.head).createChild({
21469                 tag : 'link',
21470                 rel : 'stylesheet',
21471                 type : 'text/css',
21472                 href : s
21473             });
21474         });
21475
21476         
21477     },
21478     
21479     removeStylesheets : function()
21480     {
21481         var _this = this;
21482         
21483         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21484             s.remove();
21485         });
21486     }
21487     
21488     // hide stuff that is not compatible
21489     /**
21490      * @event blur
21491      * @hide
21492      */
21493     /**
21494      * @event change
21495      * @hide
21496      */
21497     /**
21498      * @event focus
21499      * @hide
21500      */
21501     /**
21502      * @event specialkey
21503      * @hide
21504      */
21505     /**
21506      * @cfg {String} fieldClass @hide
21507      */
21508     /**
21509      * @cfg {String} focusClass @hide
21510      */
21511     /**
21512      * @cfg {String} autoCreate @hide
21513      */
21514     /**
21515      * @cfg {String} inputType @hide
21516      */
21517     /**
21518      * @cfg {String} invalidClass @hide
21519      */
21520     /**
21521      * @cfg {String} invalidText @hide
21522      */
21523     /**
21524      * @cfg {String} msgFx @hide
21525      */
21526     /**
21527      * @cfg {String} validateOnBlur @hide
21528      */
21529 });
21530
21531 Roo.HtmlEditorCore.white = [
21532         'area', 'br', 'img', 'input', 'hr', 'wbr',
21533         
21534        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21535        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21536        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21537        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21538        'table',   'ul',         'xmp', 
21539        
21540        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21541       'thead',   'tr', 
21542      
21543       'dir', 'menu', 'ol', 'ul', 'dl',
21544        
21545       'embed',  'object'
21546 ];
21547
21548
21549 Roo.HtmlEditorCore.black = [
21550     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21551         'applet', // 
21552         'base',   'basefont', 'bgsound', 'blink',  'body', 
21553         'frame',  'frameset', 'head',    'html',   'ilayer', 
21554         'iframe', 'layer',  'link',     'meta',    'object',   
21555         'script', 'style' ,'title',  'xml' // clean later..
21556 ];
21557 Roo.HtmlEditorCore.clean = [
21558     'script', 'style', 'title', 'xml'
21559 ];
21560 Roo.HtmlEditorCore.remove = [
21561     'font'
21562 ];
21563 // attributes..
21564
21565 Roo.HtmlEditorCore.ablack = [
21566     'on'
21567 ];
21568     
21569 Roo.HtmlEditorCore.aclean = [ 
21570     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21571 ];
21572
21573 // protocols..
21574 Roo.HtmlEditorCore.pwhite= [
21575         'http',  'https',  'mailto'
21576 ];
21577
21578 // white listed style attributes.
21579 Roo.HtmlEditorCore.cwhite= [
21580       //  'text-align', /// default is to allow most things..
21581       
21582          
21583 //        'font-size'//??
21584 ];
21585
21586 // black listed style attributes.
21587 Roo.HtmlEditorCore.cblack= [
21588       //  'font-size' -- this can be set by the project 
21589 ];
21590
21591
21592 Roo.HtmlEditorCore.swapCodes   =[ 
21593     [    8211, "--" ], 
21594     [    8212, "--" ], 
21595     [    8216,  "'" ],  
21596     [    8217, "'" ],  
21597     [    8220, '"' ],  
21598     [    8221, '"' ],  
21599     [    8226, "*" ],  
21600     [    8230, "..." ]
21601 ]; 
21602
21603     /*
21604  * - LGPL
21605  *
21606  * HtmlEditor
21607  * 
21608  */
21609
21610 /**
21611  * @class Roo.bootstrap.HtmlEditor
21612  * @extends Roo.bootstrap.TextArea
21613  * Bootstrap HtmlEditor class
21614
21615  * @constructor
21616  * Create a new HtmlEditor
21617  * @param {Object} config The config object
21618  */
21619
21620 Roo.bootstrap.HtmlEditor = function(config){
21621     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21622     if (!this.toolbars) {
21623         this.toolbars = [];
21624     }
21625     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21626     this.addEvents({
21627             /**
21628              * @event initialize
21629              * Fires when the editor is fully initialized (including the iframe)
21630              * @param {HtmlEditor} this
21631              */
21632             initialize: true,
21633             /**
21634              * @event activate
21635              * Fires when the editor is first receives the focus. Any insertion must wait
21636              * until after this event.
21637              * @param {HtmlEditor} this
21638              */
21639             activate: true,
21640              /**
21641              * @event beforesync
21642              * Fires before the textarea is updated with content from the editor iframe. Return false
21643              * to cancel the sync.
21644              * @param {HtmlEditor} this
21645              * @param {String} html
21646              */
21647             beforesync: true,
21648              /**
21649              * @event beforepush
21650              * Fires before the iframe editor is updated with content from the textarea. Return false
21651              * to cancel the push.
21652              * @param {HtmlEditor} this
21653              * @param {String} html
21654              */
21655             beforepush: true,
21656              /**
21657              * @event sync
21658              * Fires when the textarea is updated with content from the editor iframe.
21659              * @param {HtmlEditor} this
21660              * @param {String} html
21661              */
21662             sync: true,
21663              /**
21664              * @event push
21665              * Fires when the iframe editor is updated with content from the textarea.
21666              * @param {HtmlEditor} this
21667              * @param {String} html
21668              */
21669             push: true,
21670              /**
21671              * @event editmodechange
21672              * Fires when the editor switches edit modes
21673              * @param {HtmlEditor} this
21674              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21675              */
21676             editmodechange: true,
21677             /**
21678              * @event editorevent
21679              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21680              * @param {HtmlEditor} this
21681              */
21682             editorevent: true,
21683             /**
21684              * @event firstfocus
21685              * Fires when on first focus - needed by toolbars..
21686              * @param {HtmlEditor} this
21687              */
21688             firstfocus: true,
21689             /**
21690              * @event autosave
21691              * Auto save the htmlEditor value as a file into Events
21692              * @param {HtmlEditor} this
21693              */
21694             autosave: true,
21695             /**
21696              * @event savedpreview
21697              * preview the saved version of htmlEditor
21698              * @param {HtmlEditor} this
21699              */
21700             savedpreview: true
21701         });
21702 };
21703
21704
21705 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21706     
21707     
21708       /**
21709      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21710      */
21711     toolbars : false,
21712    
21713      /**
21714      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21715      *                        Roo.resizable.
21716      */
21717     resizable : false,
21718      /**
21719      * @cfg {Number} height (in pixels)
21720      */   
21721     height: 300,
21722    /**
21723      * @cfg {Number} width (in pixels)
21724      */   
21725     width: false,
21726     
21727     /**
21728      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21729      * 
21730      */
21731     stylesheets: false,
21732     
21733     // id of frame..
21734     frameId: false,
21735     
21736     // private properties
21737     validationEvent : false,
21738     deferHeight: true,
21739     initialized : false,
21740     activated : false,
21741     
21742     onFocus : Roo.emptyFn,
21743     iframePad:3,
21744     hideMode:'offsets',
21745     
21746     
21747     tbContainer : false,
21748     
21749     toolbarContainer :function() {
21750         return this.wrap.select('.x-html-editor-tb',true).first();
21751     },
21752
21753     /**
21754      * Protected method that will not generally be called directly. It
21755      * is called when the editor creates its toolbar. Override this method if you need to
21756      * add custom toolbar buttons.
21757      * @param {HtmlEditor} editor
21758      */
21759     createToolbar : function(){
21760         
21761         Roo.log("create toolbars");
21762         
21763         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21764         this.toolbars[0].render(this.toolbarContainer());
21765         
21766         return;
21767         
21768 //        if (!editor.toolbars || !editor.toolbars.length) {
21769 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21770 //        }
21771 //        
21772 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21773 //            editor.toolbars[i] = Roo.factory(
21774 //                    typeof(editor.toolbars[i]) == 'string' ?
21775 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21776 //                Roo.bootstrap.HtmlEditor);
21777 //            editor.toolbars[i].init(editor);
21778 //        }
21779     },
21780
21781      
21782     // private
21783     onRender : function(ct, position)
21784     {
21785        // Roo.log("Call onRender: " + this.xtype);
21786         var _t = this;
21787         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21788       
21789         this.wrap = this.inputEl().wrap({
21790             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21791         });
21792         
21793         this.editorcore.onRender(ct, position);
21794          
21795         if (this.resizable) {
21796             this.resizeEl = new Roo.Resizable(this.wrap, {
21797                 pinned : true,
21798                 wrap: true,
21799                 dynamic : true,
21800                 minHeight : this.height,
21801                 height: this.height,
21802                 handles : this.resizable,
21803                 width: this.width,
21804                 listeners : {
21805                     resize : function(r, w, h) {
21806                         _t.onResize(w,h); // -something
21807                     }
21808                 }
21809             });
21810             
21811         }
21812         this.createToolbar(this);
21813        
21814         
21815         if(!this.width && this.resizable){
21816             this.setSize(this.wrap.getSize());
21817         }
21818         if (this.resizeEl) {
21819             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21820             // should trigger onReize..
21821         }
21822         
21823     },
21824
21825     // private
21826     onResize : function(w, h)
21827     {
21828         Roo.log('resize: ' +w + ',' + h );
21829         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21830         var ew = false;
21831         var eh = false;
21832         
21833         if(this.inputEl() ){
21834             if(typeof w == 'number'){
21835                 var aw = w - this.wrap.getFrameWidth('lr');
21836                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21837                 ew = aw;
21838             }
21839             if(typeof h == 'number'){
21840                  var tbh = -11;  // fixme it needs to tool bar size!
21841                 for (var i =0; i < this.toolbars.length;i++) {
21842                     // fixme - ask toolbars for heights?
21843                     tbh += this.toolbars[i].el.getHeight();
21844                     //if (this.toolbars[i].footer) {
21845                     //    tbh += this.toolbars[i].footer.el.getHeight();
21846                     //}
21847                 }
21848               
21849                 
21850                 
21851                 
21852                 
21853                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21854                 ah -= 5; // knock a few pixes off for look..
21855                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21856                 var eh = ah;
21857             }
21858         }
21859         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21860         this.editorcore.onResize(ew,eh);
21861         
21862     },
21863
21864     /**
21865      * Toggles the editor between standard and source edit mode.
21866      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21867      */
21868     toggleSourceEdit : function(sourceEditMode)
21869     {
21870         this.editorcore.toggleSourceEdit(sourceEditMode);
21871         
21872         if(this.editorcore.sourceEditMode){
21873             Roo.log('editor - showing textarea');
21874             
21875 //            Roo.log('in');
21876 //            Roo.log(this.syncValue());
21877             this.syncValue();
21878             this.inputEl().removeClass(['hide', 'x-hidden']);
21879             this.inputEl().dom.removeAttribute('tabIndex');
21880             this.inputEl().focus();
21881         }else{
21882             Roo.log('editor - hiding textarea');
21883 //            Roo.log('out')
21884 //            Roo.log(this.pushValue()); 
21885             this.pushValue();
21886             
21887             this.inputEl().addClass(['hide', 'x-hidden']);
21888             this.inputEl().dom.setAttribute('tabIndex', -1);
21889             //this.deferFocus();
21890         }
21891          
21892         if(this.resizable){
21893             this.setSize(this.wrap.getSize());
21894         }
21895         
21896         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21897     },
21898  
21899     // private (for BoxComponent)
21900     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21901
21902     // private (for BoxComponent)
21903     getResizeEl : function(){
21904         return this.wrap;
21905     },
21906
21907     // private (for BoxComponent)
21908     getPositionEl : function(){
21909         return this.wrap;
21910     },
21911
21912     // private
21913     initEvents : function(){
21914         this.originalValue = this.getValue();
21915     },
21916
21917 //    /**
21918 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21919 //     * @method
21920 //     */
21921 //    markInvalid : Roo.emptyFn,
21922 //    /**
21923 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21924 //     * @method
21925 //     */
21926 //    clearInvalid : Roo.emptyFn,
21927
21928     setValue : function(v){
21929         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21930         this.editorcore.pushValue();
21931     },
21932
21933      
21934     // private
21935     deferFocus : function(){
21936         this.focus.defer(10, this);
21937     },
21938
21939     // doc'ed in Field
21940     focus : function(){
21941         this.editorcore.focus();
21942         
21943     },
21944       
21945
21946     // private
21947     onDestroy : function(){
21948         
21949         
21950         
21951         if(this.rendered){
21952             
21953             for (var i =0; i < this.toolbars.length;i++) {
21954                 // fixme - ask toolbars for heights?
21955                 this.toolbars[i].onDestroy();
21956             }
21957             
21958             this.wrap.dom.innerHTML = '';
21959             this.wrap.remove();
21960         }
21961     },
21962
21963     // private
21964     onFirstFocus : function(){
21965         //Roo.log("onFirstFocus");
21966         this.editorcore.onFirstFocus();
21967          for (var i =0; i < this.toolbars.length;i++) {
21968             this.toolbars[i].onFirstFocus();
21969         }
21970         
21971     },
21972     
21973     // private
21974     syncValue : function()
21975     {   
21976         this.editorcore.syncValue();
21977     },
21978     
21979     pushValue : function()
21980     {   
21981         this.editorcore.pushValue();
21982     }
21983      
21984     
21985     // hide stuff that is not compatible
21986     /**
21987      * @event blur
21988      * @hide
21989      */
21990     /**
21991      * @event change
21992      * @hide
21993      */
21994     /**
21995      * @event focus
21996      * @hide
21997      */
21998     /**
21999      * @event specialkey
22000      * @hide
22001      */
22002     /**
22003      * @cfg {String} fieldClass @hide
22004      */
22005     /**
22006      * @cfg {String} focusClass @hide
22007      */
22008     /**
22009      * @cfg {String} autoCreate @hide
22010      */
22011     /**
22012      * @cfg {String} inputType @hide
22013      */
22014     /**
22015      * @cfg {String} invalidClass @hide
22016      */
22017     /**
22018      * @cfg {String} invalidText @hide
22019      */
22020     /**
22021      * @cfg {String} msgFx @hide
22022      */
22023     /**
22024      * @cfg {String} validateOnBlur @hide
22025      */
22026 });
22027  
22028     
22029    
22030    
22031    
22032       
22033 Roo.namespace('Roo.bootstrap.htmleditor');
22034 /**
22035  * @class Roo.bootstrap.HtmlEditorToolbar1
22036  * Basic Toolbar
22037  * 
22038  * Usage:
22039  *
22040  new Roo.bootstrap.HtmlEditor({
22041     ....
22042     toolbars : [
22043         new Roo.bootstrap.HtmlEditorToolbar1({
22044             disable : { fonts: 1 , format: 1, ..., ... , ...],
22045             btns : [ .... ]
22046         })
22047     }
22048      
22049  * 
22050  * @cfg {Object} disable List of elements to disable..
22051  * @cfg {Array} btns List of additional buttons.
22052  * 
22053  * 
22054  * NEEDS Extra CSS? 
22055  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22056  */
22057  
22058 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22059 {
22060     
22061     Roo.apply(this, config);
22062     
22063     // default disabled, based on 'good practice'..
22064     this.disable = this.disable || {};
22065     Roo.applyIf(this.disable, {
22066         fontSize : true,
22067         colors : true,
22068         specialElements : true
22069     });
22070     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22071     
22072     this.editor = config.editor;
22073     this.editorcore = config.editor.editorcore;
22074     
22075     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22076     
22077     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22078     // dont call parent... till later.
22079 }
22080 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22081      
22082     bar : true,
22083     
22084     editor : false,
22085     editorcore : false,
22086     
22087     
22088     formats : [
22089         "p" ,  
22090         "h1","h2","h3","h4","h5","h6", 
22091         "pre", "code", 
22092         "abbr", "acronym", "address", "cite", "samp", "var",
22093         'div','span'
22094     ],
22095     
22096     onRender : function(ct, position)
22097     {
22098        // Roo.log("Call onRender: " + this.xtype);
22099         
22100        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22101        Roo.log(this.el);
22102        this.el.dom.style.marginBottom = '0';
22103        var _this = this;
22104        var editorcore = this.editorcore;
22105        var editor= this.editor;
22106        
22107        var children = [];
22108        var btn = function(id,cmd , toggle, handler){
22109        
22110             var  event = toggle ? 'toggle' : 'click';
22111        
22112             var a = {
22113                 size : 'sm',
22114                 xtype: 'Button',
22115                 xns: Roo.bootstrap,
22116                 glyphicon : id,
22117                 cmd : id || cmd,
22118                 enableToggle:toggle !== false,
22119                 //html : 'submit'
22120                 pressed : toggle ? false : null,
22121                 listeners : {}
22122             };
22123             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22124                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22125             };
22126             children.push(a);
22127             return a;
22128        }
22129         
22130         var style = {
22131                 xtype: 'Button',
22132                 size : 'sm',
22133                 xns: Roo.bootstrap,
22134                 glyphicon : 'font',
22135                 //html : 'submit'
22136                 menu : {
22137                     xtype: 'Menu',
22138                     xns: Roo.bootstrap,
22139                     items:  []
22140                 }
22141         };
22142         Roo.each(this.formats, function(f) {
22143             style.menu.items.push({
22144                 xtype :'MenuItem',
22145                 xns: Roo.bootstrap,
22146                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22147                 tagname : f,
22148                 listeners : {
22149                     click : function()
22150                     {
22151                         editorcore.insertTag(this.tagname);
22152                         editor.focus();
22153                     }
22154                 }
22155                 
22156             });
22157         });
22158          children.push(style);   
22159             
22160             
22161         btn('bold',false,true);
22162         btn('italic',false,true);
22163         btn('align-left', 'justifyleft',true);
22164         btn('align-center', 'justifycenter',true);
22165         btn('align-right' , 'justifyright',true);
22166         btn('link', false, false, function(btn) {
22167             //Roo.log("create link?");
22168             var url = prompt(this.createLinkText, this.defaultLinkValue);
22169             if(url && url != 'http:/'+'/'){
22170                 this.editorcore.relayCmd('createlink', url);
22171             }
22172         }),
22173         btn('list','insertunorderedlist',true);
22174         btn('pencil', false,true, function(btn){
22175                 Roo.log(this);
22176                 
22177                 this.toggleSourceEdit(btn.pressed);
22178         });
22179         /*
22180         var cog = {
22181                 xtype: 'Button',
22182                 size : 'sm',
22183                 xns: Roo.bootstrap,
22184                 glyphicon : 'cog',
22185                 //html : 'submit'
22186                 menu : {
22187                     xtype: 'Menu',
22188                     xns: Roo.bootstrap,
22189                     items:  []
22190                 }
22191         };
22192         
22193         cog.menu.items.push({
22194             xtype :'MenuItem',
22195             xns: Roo.bootstrap,
22196             html : Clean styles,
22197             tagname : f,
22198             listeners : {
22199                 click : function()
22200                 {
22201                     editorcore.insertTag(this.tagname);
22202                     editor.focus();
22203                 }
22204             }
22205             
22206         });
22207        */
22208         
22209          
22210        this.xtype = 'NavSimplebar';
22211         
22212         for(var i=0;i< children.length;i++) {
22213             
22214             this.buttons.add(this.addxtypeChild(children[i]));
22215             
22216         }
22217         
22218         editor.on('editorevent', this.updateToolbar, this);
22219     },
22220     onBtnClick : function(id)
22221     {
22222        this.editorcore.relayCmd(id);
22223        this.editorcore.focus();
22224     },
22225     
22226     /**
22227      * Protected method that will not generally be called directly. It triggers
22228      * a toolbar update by reading the markup state of the current selection in the editor.
22229      */
22230     updateToolbar: function(){
22231
22232         if(!this.editorcore.activated){
22233             this.editor.onFirstFocus(); // is this neeed?
22234             return;
22235         }
22236
22237         var btns = this.buttons; 
22238         var doc = this.editorcore.doc;
22239         btns.get('bold').setActive(doc.queryCommandState('bold'));
22240         btns.get('italic').setActive(doc.queryCommandState('italic'));
22241         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22242         
22243         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22244         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22245         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22246         
22247         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22248         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22249          /*
22250         
22251         var ans = this.editorcore.getAllAncestors();
22252         if (this.formatCombo) {
22253             
22254             
22255             var store = this.formatCombo.store;
22256             this.formatCombo.setValue("");
22257             for (var i =0; i < ans.length;i++) {
22258                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22259                     // select it..
22260                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22261                     break;
22262                 }
22263             }
22264         }
22265         
22266         
22267         
22268         // hides menus... - so this cant be on a menu...
22269         Roo.bootstrap.MenuMgr.hideAll();
22270         */
22271         Roo.bootstrap.MenuMgr.hideAll();
22272         //this.editorsyncValue();
22273     },
22274     onFirstFocus: function() {
22275         this.buttons.each(function(item){
22276            item.enable();
22277         });
22278     },
22279     toggleSourceEdit : function(sourceEditMode){
22280         
22281           
22282         if(sourceEditMode){
22283             Roo.log("disabling buttons");
22284            this.buttons.each( function(item){
22285                 if(item.cmd != 'pencil'){
22286                     item.disable();
22287                 }
22288             });
22289           
22290         }else{
22291             Roo.log("enabling buttons");
22292             if(this.editorcore.initialized){
22293                 this.buttons.each( function(item){
22294                     item.enable();
22295                 });
22296             }
22297             
22298         }
22299         Roo.log("calling toggole on editor");
22300         // tell the editor that it's been pressed..
22301         this.editor.toggleSourceEdit(sourceEditMode);
22302        
22303     }
22304 });
22305
22306
22307
22308
22309
22310 /**
22311  * @class Roo.bootstrap.Table.AbstractSelectionModel
22312  * @extends Roo.util.Observable
22313  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22314  * implemented by descendant classes.  This class should not be directly instantiated.
22315  * @constructor
22316  */
22317 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22318     this.locked = false;
22319     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22320 };
22321
22322
22323 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22324     /** @ignore Called by the grid automatically. Do not call directly. */
22325     init : function(grid){
22326         this.grid = grid;
22327         this.initEvents();
22328     },
22329
22330     /**
22331      * Locks the selections.
22332      */
22333     lock : function(){
22334         this.locked = true;
22335     },
22336
22337     /**
22338      * Unlocks the selections.
22339      */
22340     unlock : function(){
22341         this.locked = false;
22342     },
22343
22344     /**
22345      * Returns true if the selections are locked.
22346      * @return {Boolean}
22347      */
22348     isLocked : function(){
22349         return this.locked;
22350     }
22351 });
22352 /**
22353  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22354  * @class Roo.bootstrap.Table.RowSelectionModel
22355  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22356  * It supports multiple selections and keyboard selection/navigation. 
22357  * @constructor
22358  * @param {Object} config
22359  */
22360
22361 Roo.bootstrap.Table.RowSelectionModel = function(config){
22362     Roo.apply(this, config);
22363     this.selections = new Roo.util.MixedCollection(false, function(o){
22364         return o.id;
22365     });
22366
22367     this.last = false;
22368     this.lastActive = false;
22369
22370     this.addEvents({
22371         /**
22372              * @event selectionchange
22373              * Fires when the selection changes
22374              * @param {SelectionModel} this
22375              */
22376             "selectionchange" : true,
22377         /**
22378              * @event afterselectionchange
22379              * Fires after the selection changes (eg. by key press or clicking)
22380              * @param {SelectionModel} this
22381              */
22382             "afterselectionchange" : true,
22383         /**
22384              * @event beforerowselect
22385              * Fires when a row is selected being selected, return false to cancel.
22386              * @param {SelectionModel} this
22387              * @param {Number} rowIndex The selected index
22388              * @param {Boolean} keepExisting False if other selections will be cleared
22389              */
22390             "beforerowselect" : true,
22391         /**
22392              * @event rowselect
22393              * Fires when a row is selected.
22394              * @param {SelectionModel} this
22395              * @param {Number} rowIndex The selected index
22396              * @param {Roo.data.Record} r The record
22397              */
22398             "rowselect" : true,
22399         /**
22400              * @event rowdeselect
22401              * Fires when a row is deselected.
22402              * @param {SelectionModel} this
22403              * @param {Number} rowIndex The selected index
22404              */
22405         "rowdeselect" : true
22406     });
22407     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22408     this.locked = false;
22409  };
22410
22411 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22412     /**
22413      * @cfg {Boolean} singleSelect
22414      * True to allow selection of only one row at a time (defaults to false)
22415      */
22416     singleSelect : false,
22417
22418     // private
22419     initEvents : function()
22420     {
22421
22422         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22423             this.grid.on("mousedown", this.handleMouseDown, this);
22424         }else{ // allow click to work like normal
22425             this.grid.on("rowclick", this.handleDragableRowClick, this);
22426         }
22427
22428         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22429             "up" : function(e){
22430                 if(!e.shiftKey){
22431                     this.selectPrevious(e.shiftKey);
22432                 }else if(this.last !== false && this.lastActive !== false){
22433                     var last = this.last;
22434                     this.selectRange(this.last,  this.lastActive-1);
22435                     this.grid.getView().focusRow(this.lastActive);
22436                     if(last !== false){
22437                         this.last = last;
22438                     }
22439                 }else{
22440                     this.selectFirstRow();
22441                 }
22442                 this.fireEvent("afterselectionchange", this);
22443             },
22444             "down" : function(e){
22445                 if(!e.shiftKey){
22446                     this.selectNext(e.shiftKey);
22447                 }else if(this.last !== false && this.lastActive !== false){
22448                     var last = this.last;
22449                     this.selectRange(this.last,  this.lastActive+1);
22450                     this.grid.getView().focusRow(this.lastActive);
22451                     if(last !== false){
22452                         this.last = last;
22453                     }
22454                 }else{
22455                     this.selectFirstRow();
22456                 }
22457                 this.fireEvent("afterselectionchange", this);
22458             },
22459             scope: this
22460         });
22461         /*
22462         var view = this.grid.view;
22463         view.on("refresh", this.onRefresh, this);
22464         view.on("rowupdated", this.onRowUpdated, this);
22465         view.on("rowremoved", this.onRemove, this);
22466         */
22467     },
22468
22469     // private
22470     onRefresh : function(){
22471         var ds = this.grid.dataSource, i, v = this.grid.view;
22472         var s = this.selections;
22473         s.each(function(r){
22474             if((i = ds.indexOfId(r.id)) != -1){
22475                 v.onRowSelect(i);
22476             }else{
22477                 s.remove(r);
22478             }
22479         });
22480     },
22481
22482     // private
22483     onRemove : function(v, index, r){
22484         this.selections.remove(r);
22485     },
22486
22487     // private
22488     onRowUpdated : function(v, index, r){
22489         if(this.isSelected(r)){
22490             v.onRowSelect(index);
22491         }
22492     },
22493
22494     /**
22495      * Select records.
22496      * @param {Array} records The records to select
22497      * @param {Boolean} keepExisting (optional) True to keep existing selections
22498      */
22499     selectRecords : function(records, keepExisting){
22500         if(!keepExisting){
22501             this.clearSelections();
22502         }
22503         var ds = this.grid.dataSource;
22504         for(var i = 0, len = records.length; i < len; i++){
22505             this.selectRow(ds.indexOf(records[i]), true);
22506         }
22507     },
22508
22509     /**
22510      * Gets the number of selected rows.
22511      * @return {Number}
22512      */
22513     getCount : function(){
22514         return this.selections.length;
22515     },
22516
22517     /**
22518      * Selects the first row in the grid.
22519      */
22520     selectFirstRow : function(){
22521         this.selectRow(0);
22522     },
22523
22524     /**
22525      * Select the last row.
22526      * @param {Boolean} keepExisting (optional) True to keep existing selections
22527      */
22528     selectLastRow : function(keepExisting){
22529         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22530     },
22531
22532     /**
22533      * Selects the row immediately following the last selected row.
22534      * @param {Boolean} keepExisting (optional) True to keep existing selections
22535      */
22536     selectNext : function(keepExisting){
22537         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22538             this.selectRow(this.last+1, keepExisting);
22539             this.grid.getView().focusRow(this.last);
22540         }
22541     },
22542
22543     /**
22544      * Selects the row that precedes the last selected row.
22545      * @param {Boolean} keepExisting (optional) True to keep existing selections
22546      */
22547     selectPrevious : function(keepExisting){
22548         if(this.last){
22549             this.selectRow(this.last-1, keepExisting);
22550             this.grid.getView().focusRow(this.last);
22551         }
22552     },
22553
22554     /**
22555      * Returns the selected records
22556      * @return {Array} Array of selected records
22557      */
22558     getSelections : function(){
22559         return [].concat(this.selections.items);
22560     },
22561
22562     /**
22563      * Returns the first selected record.
22564      * @return {Record}
22565      */
22566     getSelected : function(){
22567         return this.selections.itemAt(0);
22568     },
22569
22570
22571     /**
22572      * Clears all selections.
22573      */
22574     clearSelections : function(fast){
22575         if(this.locked) {
22576             return;
22577         }
22578         if(fast !== true){
22579             var ds = this.grid.dataSource;
22580             var s = this.selections;
22581             s.each(function(r){
22582                 this.deselectRow(ds.indexOfId(r.id));
22583             }, this);
22584             s.clear();
22585         }else{
22586             this.selections.clear();
22587         }
22588         this.last = false;
22589     },
22590
22591
22592     /**
22593      * Selects all rows.
22594      */
22595     selectAll : function(){
22596         if(this.locked) {
22597             return;
22598         }
22599         this.selections.clear();
22600         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22601             this.selectRow(i, true);
22602         }
22603     },
22604
22605     /**
22606      * Returns True if there is a selection.
22607      * @return {Boolean}
22608      */
22609     hasSelection : function(){
22610         return this.selections.length > 0;
22611     },
22612
22613     /**
22614      * Returns True if the specified row is selected.
22615      * @param {Number/Record} record The record or index of the record to check
22616      * @return {Boolean}
22617      */
22618     isSelected : function(index){
22619         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22620         return (r && this.selections.key(r.id) ? true : false);
22621     },
22622
22623     /**
22624      * Returns True if the specified record id is selected.
22625      * @param {String} id The id of record to check
22626      * @return {Boolean}
22627      */
22628     isIdSelected : function(id){
22629         return (this.selections.key(id) ? true : false);
22630     },
22631
22632     // private
22633     handleMouseDown : function(e, t){
22634         var view = this.grid.getView(), rowIndex;
22635         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22636             return;
22637         };
22638         if(e.shiftKey && this.last !== false){
22639             var last = this.last;
22640             this.selectRange(last, rowIndex, e.ctrlKey);
22641             this.last = last; // reset the last
22642             view.focusRow(rowIndex);
22643         }else{
22644             var isSelected = this.isSelected(rowIndex);
22645             if(e.button !== 0 && isSelected){
22646                 view.focusRow(rowIndex);
22647             }else if(e.ctrlKey && isSelected){
22648                 this.deselectRow(rowIndex);
22649             }else if(!isSelected){
22650                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22651                 view.focusRow(rowIndex);
22652             }
22653         }
22654         this.fireEvent("afterselectionchange", this);
22655     },
22656     // private
22657     handleDragableRowClick :  function(grid, rowIndex, e) 
22658     {
22659         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22660             this.selectRow(rowIndex, false);
22661             grid.view.focusRow(rowIndex);
22662              this.fireEvent("afterselectionchange", this);
22663         }
22664     },
22665     
22666     /**
22667      * Selects multiple rows.
22668      * @param {Array} rows Array of the indexes of the row to select
22669      * @param {Boolean} keepExisting (optional) True to keep existing selections
22670      */
22671     selectRows : function(rows, keepExisting){
22672         if(!keepExisting){
22673             this.clearSelections();
22674         }
22675         for(var i = 0, len = rows.length; i < len; i++){
22676             this.selectRow(rows[i], true);
22677         }
22678     },
22679
22680     /**
22681      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22682      * @param {Number} startRow The index of the first row in the range
22683      * @param {Number} endRow The index of the last row in the range
22684      * @param {Boolean} keepExisting (optional) True to retain existing selections
22685      */
22686     selectRange : function(startRow, endRow, keepExisting){
22687         if(this.locked) {
22688             return;
22689         }
22690         if(!keepExisting){
22691             this.clearSelections();
22692         }
22693         if(startRow <= endRow){
22694             for(var i = startRow; i <= endRow; i++){
22695                 this.selectRow(i, true);
22696             }
22697         }else{
22698             for(var i = startRow; i >= endRow; i--){
22699                 this.selectRow(i, true);
22700             }
22701         }
22702     },
22703
22704     /**
22705      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22706      * @param {Number} startRow The index of the first row in the range
22707      * @param {Number} endRow The index of the last row in the range
22708      */
22709     deselectRange : function(startRow, endRow, preventViewNotify){
22710         if(this.locked) {
22711             return;
22712         }
22713         for(var i = startRow; i <= endRow; i++){
22714             this.deselectRow(i, preventViewNotify);
22715         }
22716     },
22717
22718     /**
22719      * Selects a row.
22720      * @param {Number} row The index of the row to select
22721      * @param {Boolean} keepExisting (optional) True to keep existing selections
22722      */
22723     selectRow : function(index, keepExisting, preventViewNotify){
22724         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22725             return;
22726         }
22727         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22728             if(!keepExisting || this.singleSelect){
22729                 this.clearSelections();
22730             }
22731             var r = this.grid.dataSource.getAt(index);
22732             this.selections.add(r);
22733             this.last = this.lastActive = index;
22734             if(!preventViewNotify){
22735                 this.grid.getView().onRowSelect(index);
22736             }
22737             this.fireEvent("rowselect", this, index, r);
22738             this.fireEvent("selectionchange", this);
22739         }
22740     },
22741
22742     /**
22743      * Deselects a row.
22744      * @param {Number} row The index of the row to deselect
22745      */
22746     deselectRow : function(index, preventViewNotify){
22747         if(this.locked) {
22748             return;
22749         }
22750         if(this.last == index){
22751             this.last = false;
22752         }
22753         if(this.lastActive == index){
22754             this.lastActive = false;
22755         }
22756         var r = this.grid.dataSource.getAt(index);
22757         this.selections.remove(r);
22758         if(!preventViewNotify){
22759             this.grid.getView().onRowDeselect(index);
22760         }
22761         this.fireEvent("rowdeselect", this, index);
22762         this.fireEvent("selectionchange", this);
22763     },
22764
22765     // private
22766     restoreLast : function(){
22767         if(this._last){
22768             this.last = this._last;
22769         }
22770     },
22771
22772     // private
22773     acceptsNav : function(row, col, cm){
22774         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22775     },
22776
22777     // private
22778     onEditorKey : function(field, e){
22779         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22780         if(k == e.TAB){
22781             e.stopEvent();
22782             ed.completeEdit();
22783             if(e.shiftKey){
22784                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22785             }else{
22786                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22787             }
22788         }else if(k == e.ENTER && !e.ctrlKey){
22789             e.stopEvent();
22790             ed.completeEdit();
22791             if(e.shiftKey){
22792                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22793             }else{
22794                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22795             }
22796         }else if(k == e.ESC){
22797             ed.cancelEdit();
22798         }
22799         if(newCell){
22800             g.startEditing(newCell[0], newCell[1]);
22801         }
22802     }
22803 });/*
22804  * Based on:
22805  * Ext JS Library 1.1.1
22806  * Copyright(c) 2006-2007, Ext JS, LLC.
22807  *
22808  * Originally Released Under LGPL - original licence link has changed is not relivant.
22809  *
22810  * Fork - LGPL
22811  * <script type="text/javascript">
22812  */
22813  
22814 /**
22815  * @class Roo.bootstrap.PagingToolbar
22816  * @extends Roo.bootstrap.NavSimplebar
22817  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22818  * @constructor
22819  * Create a new PagingToolbar
22820  * @param {Object} config The config object
22821  * @param {Roo.data.Store} store
22822  */
22823 Roo.bootstrap.PagingToolbar = function(config)
22824 {
22825     // old args format still supported... - xtype is prefered..
22826         // created from xtype...
22827     
22828     this.ds = config.dataSource;
22829     
22830     if (config.store && !this.ds) {
22831         this.store= Roo.factory(config.store, Roo.data);
22832         this.ds = this.store;
22833         this.ds.xmodule = this.xmodule || false;
22834     }
22835     
22836     this.toolbarItems = [];
22837     if (config.items) {
22838         this.toolbarItems = config.items;
22839     }
22840     
22841     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22842     
22843     this.cursor = 0;
22844     
22845     if (this.ds) { 
22846         this.bind(this.ds);
22847     }
22848     
22849     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22850     
22851 };
22852
22853 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22854     /**
22855      * @cfg {Roo.data.Store} dataSource
22856      * The underlying data store providing the paged data
22857      */
22858     /**
22859      * @cfg {String/HTMLElement/Element} container
22860      * container The id or element that will contain the toolbar
22861      */
22862     /**
22863      * @cfg {Boolean} displayInfo
22864      * True to display the displayMsg (defaults to false)
22865      */
22866     /**
22867      * @cfg {Number} pageSize
22868      * The number of records to display per page (defaults to 20)
22869      */
22870     pageSize: 20,
22871     /**
22872      * @cfg {String} displayMsg
22873      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22874      */
22875     displayMsg : 'Displaying {0} - {1} of {2}',
22876     /**
22877      * @cfg {String} emptyMsg
22878      * The message to display when no records are found (defaults to "No data to display")
22879      */
22880     emptyMsg : 'No data to display',
22881     /**
22882      * Customizable piece of the default paging text (defaults to "Page")
22883      * @type String
22884      */
22885     beforePageText : "Page",
22886     /**
22887      * Customizable piece of the default paging text (defaults to "of %0")
22888      * @type String
22889      */
22890     afterPageText : "of {0}",
22891     /**
22892      * Customizable piece of the default paging text (defaults to "First Page")
22893      * @type String
22894      */
22895     firstText : "First Page",
22896     /**
22897      * Customizable piece of the default paging text (defaults to "Previous Page")
22898      * @type String
22899      */
22900     prevText : "Previous Page",
22901     /**
22902      * Customizable piece of the default paging text (defaults to "Next Page")
22903      * @type String
22904      */
22905     nextText : "Next Page",
22906     /**
22907      * Customizable piece of the default paging text (defaults to "Last Page")
22908      * @type String
22909      */
22910     lastText : "Last Page",
22911     /**
22912      * Customizable piece of the default paging text (defaults to "Refresh")
22913      * @type String
22914      */
22915     refreshText : "Refresh",
22916
22917     buttons : false,
22918     // private
22919     onRender : function(ct, position) 
22920     {
22921         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22922         this.navgroup.parentId = this.id;
22923         this.navgroup.onRender(this.el, null);
22924         // add the buttons to the navgroup
22925         
22926         if(this.displayInfo){
22927             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22928             this.displayEl = this.el.select('.x-paging-info', true).first();
22929 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22930 //            this.displayEl = navel.el.select('span',true).first();
22931         }
22932         
22933         var _this = this;
22934         
22935         if(this.buttons){
22936             Roo.each(_this.buttons, function(e){ // this might need to use render????
22937                Roo.factory(e).onRender(_this.el, null);
22938             });
22939         }
22940             
22941         Roo.each(_this.toolbarItems, function(e) {
22942             _this.navgroup.addItem(e);
22943         });
22944         
22945         
22946         this.first = this.navgroup.addItem({
22947             tooltip: this.firstText,
22948             cls: "prev",
22949             icon : 'fa fa-backward',
22950             disabled: true,
22951             preventDefault: true,
22952             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22953         });
22954         
22955         this.prev =  this.navgroup.addItem({
22956             tooltip: this.prevText,
22957             cls: "prev",
22958             icon : 'fa fa-step-backward',
22959             disabled: true,
22960             preventDefault: true,
22961             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22962         });
22963     //this.addSeparator();
22964         
22965         
22966         var field = this.navgroup.addItem( {
22967             tagtype : 'span',
22968             cls : 'x-paging-position',
22969             
22970             html : this.beforePageText  +
22971                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22972                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22973          } ); //?? escaped?
22974         
22975         this.field = field.el.select('input', true).first();
22976         this.field.on("keydown", this.onPagingKeydown, this);
22977         this.field.on("focus", function(){this.dom.select();});
22978     
22979     
22980         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22981         //this.field.setHeight(18);
22982         //this.addSeparator();
22983         this.next = this.navgroup.addItem({
22984             tooltip: this.nextText,
22985             cls: "next",
22986             html : ' <i class="fa fa-step-forward">',
22987             disabled: true,
22988             preventDefault: true,
22989             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22990         });
22991         this.last = this.navgroup.addItem({
22992             tooltip: this.lastText,
22993             icon : 'fa fa-forward',
22994             cls: "next",
22995             disabled: true,
22996             preventDefault: true,
22997             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22998         });
22999     //this.addSeparator();
23000         this.loading = this.navgroup.addItem({
23001             tooltip: this.refreshText,
23002             icon: 'fa fa-refresh',
23003             preventDefault: true,
23004             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23005         });
23006         
23007     },
23008
23009     // private
23010     updateInfo : function(){
23011         if(this.displayEl){
23012             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23013             var msg = count == 0 ?
23014                 this.emptyMsg :
23015                 String.format(
23016                     this.displayMsg,
23017                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23018                 );
23019             this.displayEl.update(msg);
23020         }
23021     },
23022
23023     // private
23024     onLoad : function(ds, r, o){
23025        this.cursor = o.params ? o.params.start : 0;
23026        var d = this.getPageData(),
23027             ap = d.activePage,
23028             ps = d.pages;
23029         
23030        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23031        this.field.dom.value = ap;
23032        this.first.setDisabled(ap == 1);
23033        this.prev.setDisabled(ap == 1);
23034        this.next.setDisabled(ap == ps);
23035        this.last.setDisabled(ap == ps);
23036        this.loading.enable();
23037        this.updateInfo();
23038     },
23039
23040     // private
23041     getPageData : function(){
23042         var total = this.ds.getTotalCount();
23043         return {
23044             total : total,
23045             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23046             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23047         };
23048     },
23049
23050     // private
23051     onLoadError : function(){
23052         this.loading.enable();
23053     },
23054
23055     // private
23056     onPagingKeydown : function(e){
23057         var k = e.getKey();
23058         var d = this.getPageData();
23059         if(k == e.RETURN){
23060             var v = this.field.dom.value, pageNum;
23061             if(!v || isNaN(pageNum = parseInt(v, 10))){
23062                 this.field.dom.value = d.activePage;
23063                 return;
23064             }
23065             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23066             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23067             e.stopEvent();
23068         }
23069         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))
23070         {
23071           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23072           this.field.dom.value = pageNum;
23073           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23074           e.stopEvent();
23075         }
23076         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23077         {
23078           var v = this.field.dom.value, pageNum; 
23079           var increment = (e.shiftKey) ? 10 : 1;
23080           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23081                 increment *= -1;
23082           }
23083           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23084             this.field.dom.value = d.activePage;
23085             return;
23086           }
23087           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23088           {
23089             this.field.dom.value = parseInt(v, 10) + increment;
23090             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23091             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23092           }
23093           e.stopEvent();
23094         }
23095     },
23096
23097     // private
23098     beforeLoad : function(){
23099         if(this.loading){
23100             this.loading.disable();
23101         }
23102     },
23103
23104     // private
23105     onClick : function(which){
23106         
23107         var ds = this.ds;
23108         if (!ds) {
23109             return;
23110         }
23111         
23112         switch(which){
23113             case "first":
23114                 ds.load({params:{start: 0, limit: this.pageSize}});
23115             break;
23116             case "prev":
23117                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23118             break;
23119             case "next":
23120                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23121             break;
23122             case "last":
23123                 var total = ds.getTotalCount();
23124                 var extra = total % this.pageSize;
23125                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23126                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23127             break;
23128             case "refresh":
23129                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23130             break;
23131         }
23132     },
23133
23134     /**
23135      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23136      * @param {Roo.data.Store} store The data store to unbind
23137      */
23138     unbind : function(ds){
23139         ds.un("beforeload", this.beforeLoad, this);
23140         ds.un("load", this.onLoad, this);
23141         ds.un("loadexception", this.onLoadError, this);
23142         ds.un("remove", this.updateInfo, this);
23143         ds.un("add", this.updateInfo, this);
23144         this.ds = undefined;
23145     },
23146
23147     /**
23148      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23149      * @param {Roo.data.Store} store The data store to bind
23150      */
23151     bind : function(ds){
23152         ds.on("beforeload", this.beforeLoad, this);
23153         ds.on("load", this.onLoad, this);
23154         ds.on("loadexception", this.onLoadError, this);
23155         ds.on("remove", this.updateInfo, this);
23156         ds.on("add", this.updateInfo, this);
23157         this.ds = ds;
23158     }
23159 });/*
23160  * - LGPL
23161  *
23162  * element
23163  * 
23164  */
23165
23166 /**
23167  * @class Roo.bootstrap.MessageBar
23168  * @extends Roo.bootstrap.Component
23169  * Bootstrap MessageBar class
23170  * @cfg {String} html contents of the MessageBar
23171  * @cfg {String} weight (info | success | warning | danger) default info
23172  * @cfg {String} beforeClass insert the bar before the given class
23173  * @cfg {Boolean} closable (true | false) default false
23174  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23175  * 
23176  * @constructor
23177  * Create a new Element
23178  * @param {Object} config The config object
23179  */
23180
23181 Roo.bootstrap.MessageBar = function(config){
23182     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23183 };
23184
23185 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23186     
23187     html: '',
23188     weight: 'info',
23189     closable: false,
23190     fixed: false,
23191     beforeClass: 'bootstrap-sticky-wrap',
23192     
23193     getAutoCreate : function(){
23194         
23195         var cfg = {
23196             tag: 'div',
23197             cls: 'alert alert-dismissable alert-' + this.weight,
23198             cn: [
23199                 {
23200                     tag: 'span',
23201                     cls: 'message',
23202                     html: this.html || ''
23203                 }
23204             ]
23205         };
23206         
23207         if(this.fixed){
23208             cfg.cls += ' alert-messages-fixed';
23209         }
23210         
23211         if(this.closable){
23212             cfg.cn.push({
23213                 tag: 'button',
23214                 cls: 'close',
23215                 html: 'x'
23216             });
23217         }
23218         
23219         return cfg;
23220     },
23221     
23222     onRender : function(ct, position)
23223     {
23224         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23225         
23226         if(!this.el){
23227             var cfg = Roo.apply({},  this.getAutoCreate());
23228             cfg.id = Roo.id();
23229             
23230             if (this.cls) {
23231                 cfg.cls += ' ' + this.cls;
23232             }
23233             if (this.style) {
23234                 cfg.style = this.style;
23235             }
23236             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23237             
23238             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23239         }
23240         
23241         this.el.select('>button.close').on('click', this.hide, this);
23242         
23243     },
23244     
23245     show : function()
23246     {
23247         if (!this.rendered) {
23248             this.render();
23249         }
23250         
23251         this.el.show();
23252         
23253         this.fireEvent('show', this);
23254         
23255     },
23256     
23257     hide : function()
23258     {
23259         if (!this.rendered) {
23260             this.render();
23261         }
23262         
23263         this.el.hide();
23264         
23265         this.fireEvent('hide', this);
23266     },
23267     
23268     update : function()
23269     {
23270 //        var e = this.el.dom.firstChild;
23271 //        
23272 //        if(this.closable){
23273 //            e = e.nextSibling;
23274 //        }
23275 //        
23276 //        e.data = this.html || '';
23277
23278         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23279     }
23280    
23281 });
23282
23283  
23284
23285      /*
23286  * - LGPL
23287  *
23288  * Graph
23289  * 
23290  */
23291
23292
23293 /**
23294  * @class Roo.bootstrap.Graph
23295  * @extends Roo.bootstrap.Component
23296  * Bootstrap Graph class
23297 > Prameters
23298  -sm {number} sm 4
23299  -md {number} md 5
23300  @cfg {String} graphtype  bar | vbar | pie
23301  @cfg {number} g_x coodinator | centre x (pie)
23302  @cfg {number} g_y coodinator | centre y (pie)
23303  @cfg {number} g_r radius (pie)
23304  @cfg {number} g_height height of the chart (respected by all elements in the set)
23305  @cfg {number} g_width width of the chart (respected by all elements in the set)
23306  @cfg {Object} title The title of the chart
23307     
23308  -{Array}  values
23309  -opts (object) options for the chart 
23310      o {
23311      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23312      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23313      o vgutter (number)
23314      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.
23315      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23316      o to
23317      o stretch (boolean)
23318      o }
23319  -opts (object) options for the pie
23320      o{
23321      o cut
23322      o startAngle (number)
23323      o endAngle (number)
23324      } 
23325  *
23326  * @constructor
23327  * Create a new Input
23328  * @param {Object} config The config object
23329  */
23330
23331 Roo.bootstrap.Graph = function(config){
23332     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23333     
23334     this.addEvents({
23335         // img events
23336         /**
23337          * @event click
23338          * The img click event for the img.
23339          * @param {Roo.EventObject} e
23340          */
23341         "click" : true
23342     });
23343 };
23344
23345 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23346     
23347     sm: 4,
23348     md: 5,
23349     graphtype: 'bar',
23350     g_height: 250,
23351     g_width: 400,
23352     g_x: 50,
23353     g_y: 50,
23354     g_r: 30,
23355     opts:{
23356         //g_colors: this.colors,
23357         g_type: 'soft',
23358         g_gutter: '20%'
23359
23360     },
23361     title : false,
23362
23363     getAutoCreate : function(){
23364         
23365         var cfg = {
23366             tag: 'div',
23367             html : null
23368         };
23369         
23370         
23371         return  cfg;
23372     },
23373
23374     onRender : function(ct,position){
23375         
23376         
23377         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23378         
23379         if (typeof(Raphael) == 'undefined') {
23380             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23381             return;
23382         }
23383         
23384         this.raphael = Raphael(this.el.dom);
23385         
23386                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23387                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23388                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23389                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23390                 /*
23391                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23392                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23393                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23394                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23395                 
23396                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23397                 r.barchart(330, 10, 300, 220, data1);
23398                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23399                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23400                 */
23401                 
23402                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23403                 // r.barchart(30, 30, 560, 250,  xdata, {
23404                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23405                 //     axis : "0 0 1 1",
23406                 //     axisxlabels :  xdata
23407                 //     //yvalues : cols,
23408                    
23409                 // });
23410 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23411 //        
23412 //        this.load(null,xdata,{
23413 //                axis : "0 0 1 1",
23414 //                axisxlabels :  xdata
23415 //                });
23416
23417     },
23418
23419     load : function(graphtype,xdata,opts)
23420     {
23421         this.raphael.clear();
23422         if(!graphtype) {
23423             graphtype = this.graphtype;
23424         }
23425         if(!opts){
23426             opts = this.opts;
23427         }
23428         var r = this.raphael,
23429             fin = function () {
23430                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23431             },
23432             fout = function () {
23433                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23434             },
23435             pfin = function() {
23436                 this.sector.stop();
23437                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23438
23439                 if (this.label) {
23440                     this.label[0].stop();
23441                     this.label[0].attr({ r: 7.5 });
23442                     this.label[1].attr({ "font-weight": 800 });
23443                 }
23444             },
23445             pfout = function() {
23446                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23447
23448                 if (this.label) {
23449                     this.label[0].animate({ r: 5 }, 500, "bounce");
23450                     this.label[1].attr({ "font-weight": 400 });
23451                 }
23452             };
23453
23454         switch(graphtype){
23455             case 'bar':
23456                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23457                 break;
23458             case 'hbar':
23459                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23460                 break;
23461             case 'pie':
23462 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23463 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23464 //            
23465                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23466                 
23467                 break;
23468
23469         }
23470         
23471         if(this.title){
23472             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23473         }
23474         
23475     },
23476     
23477     setTitle: function(o)
23478     {
23479         this.title = o;
23480     },
23481     
23482     initEvents: function() {
23483         
23484         if(!this.href){
23485             this.el.on('click', this.onClick, this);
23486         }
23487     },
23488     
23489     onClick : function(e)
23490     {
23491         Roo.log('img onclick');
23492         this.fireEvent('click', this, e);
23493     }
23494    
23495 });
23496
23497  
23498 /*
23499  * - LGPL
23500  *
23501  * numberBox
23502  * 
23503  */
23504 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23505
23506 /**
23507  * @class Roo.bootstrap.dash.NumberBox
23508  * @extends Roo.bootstrap.Component
23509  * Bootstrap NumberBox class
23510  * @cfg {String} headline Box headline
23511  * @cfg {String} content Box content
23512  * @cfg {String} icon Box icon
23513  * @cfg {String} footer Footer text
23514  * @cfg {String} fhref Footer href
23515  * 
23516  * @constructor
23517  * Create a new NumberBox
23518  * @param {Object} config The config object
23519  */
23520
23521
23522 Roo.bootstrap.dash.NumberBox = function(config){
23523     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23524     
23525 };
23526
23527 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23528     
23529     headline : '',
23530     content : '',
23531     icon : '',
23532     footer : '',
23533     fhref : '',
23534     ficon : '',
23535     
23536     getAutoCreate : function(){
23537         
23538         var cfg = {
23539             tag : 'div',
23540             cls : 'small-box ',
23541             cn : [
23542                 {
23543                     tag : 'div',
23544                     cls : 'inner',
23545                     cn :[
23546                         {
23547                             tag : 'h3',
23548                             cls : 'roo-headline',
23549                             html : this.headline
23550                         },
23551                         {
23552                             tag : 'p',
23553                             cls : 'roo-content',
23554                             html : this.content
23555                         }
23556                     ]
23557                 }
23558             ]
23559         };
23560         
23561         if(this.icon){
23562             cfg.cn.push({
23563                 tag : 'div',
23564                 cls : 'icon',
23565                 cn :[
23566                     {
23567                         tag : 'i',
23568                         cls : 'ion ' + this.icon
23569                     }
23570                 ]
23571             });
23572         }
23573         
23574         if(this.footer){
23575             var footer = {
23576                 tag : 'a',
23577                 cls : 'small-box-footer',
23578                 href : this.fhref || '#',
23579                 html : this.footer
23580             };
23581             
23582             cfg.cn.push(footer);
23583             
23584         }
23585         
23586         return  cfg;
23587     },
23588
23589     onRender : function(ct,position){
23590         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23591
23592
23593        
23594                 
23595     },
23596
23597     setHeadline: function (value)
23598     {
23599         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23600     },
23601     
23602     setFooter: function (value, href)
23603     {
23604         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23605         
23606         if(href){
23607             this.el.select('a.small-box-footer',true).first().attr('href', href);
23608         }
23609         
23610     },
23611
23612     setContent: function (value)
23613     {
23614         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23615     },
23616
23617     initEvents: function() 
23618     {   
23619         
23620     }
23621     
23622 });
23623
23624  
23625 /*
23626  * - LGPL
23627  *
23628  * TabBox
23629  * 
23630  */
23631 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23632
23633 /**
23634  * @class Roo.bootstrap.dash.TabBox
23635  * @extends Roo.bootstrap.Component
23636  * Bootstrap TabBox class
23637  * @cfg {String} title Title of the TabBox
23638  * @cfg {String} icon Icon of the TabBox
23639  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23640  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23641  * 
23642  * @constructor
23643  * Create a new TabBox
23644  * @param {Object} config The config object
23645  */
23646
23647
23648 Roo.bootstrap.dash.TabBox = function(config){
23649     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23650     this.addEvents({
23651         // raw events
23652         /**
23653          * @event addpane
23654          * When a pane is added
23655          * @param {Roo.bootstrap.dash.TabPane} pane
23656          */
23657         "addpane" : true,
23658         /**
23659          * @event activatepane
23660          * When a pane is activated
23661          * @param {Roo.bootstrap.dash.TabPane} pane
23662          */
23663         "activatepane" : true
23664         
23665          
23666     });
23667     
23668     this.panes = [];
23669 };
23670
23671 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23672
23673     title : '',
23674     icon : false,
23675     showtabs : true,
23676     tabScrollable : false,
23677     
23678     getChildContainer : function()
23679     {
23680         return this.el.select('.tab-content', true).first();
23681     },
23682     
23683     getAutoCreate : function(){
23684         
23685         var header = {
23686             tag: 'li',
23687             cls: 'pull-left header',
23688             html: this.title,
23689             cn : []
23690         };
23691         
23692         if(this.icon){
23693             header.cn.push({
23694                 tag: 'i',
23695                 cls: 'fa ' + this.icon
23696             });
23697         }
23698         
23699         var h = {
23700             tag: 'ul',
23701             cls: 'nav nav-tabs pull-right',
23702             cn: [
23703                 header
23704             ]
23705         };
23706         
23707         if(this.tabScrollable){
23708             h = {
23709                 tag: 'div',
23710                 cls: 'tab-header',
23711                 cn: [
23712                     {
23713                         tag: 'ul',
23714                         cls: 'nav nav-tabs pull-right',
23715                         cn: [
23716                             header
23717                         ]
23718                     }
23719                 ]
23720             };
23721         }
23722         
23723         var cfg = {
23724             tag: 'div',
23725             cls: 'nav-tabs-custom',
23726             cn: [
23727                 h,
23728                 {
23729                     tag: 'div',
23730                     cls: 'tab-content no-padding',
23731                     cn: []
23732                 }
23733             ]
23734         };
23735
23736         return  cfg;
23737     },
23738     initEvents : function()
23739     {
23740         //Roo.log('add add pane handler');
23741         this.on('addpane', this.onAddPane, this);
23742     },
23743      /**
23744      * Updates the box title
23745      * @param {String} html to set the title to.
23746      */
23747     setTitle : function(value)
23748     {
23749         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23750     },
23751     onAddPane : function(pane)
23752     {
23753         this.panes.push(pane);
23754         //Roo.log('addpane');
23755         //Roo.log(pane);
23756         // tabs are rendere left to right..
23757         if(!this.showtabs){
23758             return;
23759         }
23760         
23761         var ctr = this.el.select('.nav-tabs', true).first();
23762          
23763          
23764         var existing = ctr.select('.nav-tab',true);
23765         var qty = existing.getCount();;
23766         
23767         
23768         var tab = ctr.createChild({
23769             tag : 'li',
23770             cls : 'nav-tab' + (qty ? '' : ' active'),
23771             cn : [
23772                 {
23773                     tag : 'a',
23774                     href:'#',
23775                     html : pane.title
23776                 }
23777             ]
23778         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23779         pane.tab = tab;
23780         
23781         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23782         if (!qty) {
23783             pane.el.addClass('active');
23784         }
23785         
23786                 
23787     },
23788     onTabClick : function(ev,un,ob,pane)
23789     {
23790         //Roo.log('tab - prev default');
23791         ev.preventDefault();
23792         
23793         
23794         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23795         pane.tab.addClass('active');
23796         //Roo.log(pane.title);
23797         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23798         // technically we should have a deactivate event.. but maybe add later.
23799         // and it should not de-activate the selected tab...
23800         this.fireEvent('activatepane', pane);
23801         pane.el.addClass('active');
23802         pane.fireEvent('activate');
23803         
23804         
23805     },
23806     
23807     getActivePane : function()
23808     {
23809         var r = false;
23810         Roo.each(this.panes, function(p) {
23811             if(p.el.hasClass('active')){
23812                 r = p;
23813                 return false;
23814             }
23815             
23816             return;
23817         });
23818         
23819         return r;
23820     }
23821     
23822     
23823 });
23824
23825  
23826 /*
23827  * - LGPL
23828  *
23829  * Tab pane
23830  * 
23831  */
23832 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23833 /**
23834  * @class Roo.bootstrap.TabPane
23835  * @extends Roo.bootstrap.Component
23836  * Bootstrap TabPane class
23837  * @cfg {Boolean} active (false | true) Default false
23838  * @cfg {String} title title of panel
23839
23840  * 
23841  * @constructor
23842  * Create a new TabPane
23843  * @param {Object} config The config object
23844  */
23845
23846 Roo.bootstrap.dash.TabPane = function(config){
23847     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23848     
23849     this.addEvents({
23850         // raw events
23851         /**
23852          * @event activate
23853          * When a pane is activated
23854          * @param {Roo.bootstrap.dash.TabPane} pane
23855          */
23856         "activate" : true
23857          
23858     });
23859 };
23860
23861 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23862     
23863     active : false,
23864     title : '',
23865     
23866     // the tabBox that this is attached to.
23867     tab : false,
23868      
23869     getAutoCreate : function() 
23870     {
23871         var cfg = {
23872             tag: 'div',
23873             cls: 'tab-pane'
23874         };
23875         
23876         if(this.active){
23877             cfg.cls += ' active';
23878         }
23879         
23880         return cfg;
23881     },
23882     initEvents  : function()
23883     {
23884         //Roo.log('trigger add pane handler');
23885         this.parent().fireEvent('addpane', this)
23886     },
23887     
23888      /**
23889      * Updates the tab title 
23890      * @param {String} html to set the title to.
23891      */
23892     setTitle: function(str)
23893     {
23894         if (!this.tab) {
23895             return;
23896         }
23897         this.title = str;
23898         this.tab.select('a', true).first().dom.innerHTML = str;
23899         
23900     }
23901     
23902     
23903     
23904 });
23905
23906  
23907
23908
23909  /*
23910  * - LGPL
23911  *
23912  * menu
23913  * 
23914  */
23915 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23916
23917 /**
23918  * @class Roo.bootstrap.menu.Menu
23919  * @extends Roo.bootstrap.Component
23920  * Bootstrap Menu class - container for Menu
23921  * @cfg {String} html Text of the menu
23922  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23923  * @cfg {String} icon Font awesome icon
23924  * @cfg {String} pos Menu align to (top | bottom) default bottom
23925  * 
23926  * 
23927  * @constructor
23928  * Create a new Menu
23929  * @param {Object} config The config object
23930  */
23931
23932
23933 Roo.bootstrap.menu.Menu = function(config){
23934     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23935     
23936     this.addEvents({
23937         /**
23938          * @event beforeshow
23939          * Fires before this menu is displayed
23940          * @param {Roo.bootstrap.menu.Menu} this
23941          */
23942         beforeshow : true,
23943         /**
23944          * @event beforehide
23945          * Fires before this menu is hidden
23946          * @param {Roo.bootstrap.menu.Menu} this
23947          */
23948         beforehide : true,
23949         /**
23950          * @event show
23951          * Fires after this menu is displayed
23952          * @param {Roo.bootstrap.menu.Menu} this
23953          */
23954         show : true,
23955         /**
23956          * @event hide
23957          * Fires after this menu is hidden
23958          * @param {Roo.bootstrap.menu.Menu} this
23959          */
23960         hide : true,
23961         /**
23962          * @event click
23963          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23964          * @param {Roo.bootstrap.menu.Menu} this
23965          * @param {Roo.EventObject} e
23966          */
23967         click : true
23968     });
23969     
23970 };
23971
23972 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23973     
23974     submenu : false,
23975     html : '',
23976     weight : 'default',
23977     icon : false,
23978     pos : 'bottom',
23979     
23980     
23981     getChildContainer : function() {
23982         if(this.isSubMenu){
23983             return this.el;
23984         }
23985         
23986         return this.el.select('ul.dropdown-menu', true).first();  
23987     },
23988     
23989     getAutoCreate : function()
23990     {
23991         var text = [
23992             {
23993                 tag : 'span',
23994                 cls : 'roo-menu-text',
23995                 html : this.html
23996             }
23997         ];
23998         
23999         if(this.icon){
24000             text.unshift({
24001                 tag : 'i',
24002                 cls : 'fa ' + this.icon
24003             })
24004         }
24005         
24006         
24007         var cfg = {
24008             tag : 'div',
24009             cls : 'btn-group',
24010             cn : [
24011                 {
24012                     tag : 'button',
24013                     cls : 'dropdown-button btn btn-' + this.weight,
24014                     cn : text
24015                 },
24016                 {
24017                     tag : 'button',
24018                     cls : 'dropdown-toggle btn btn-' + this.weight,
24019                     cn : [
24020                         {
24021                             tag : 'span',
24022                             cls : 'caret'
24023                         }
24024                     ]
24025                 },
24026                 {
24027                     tag : 'ul',
24028                     cls : 'dropdown-menu'
24029                 }
24030             ]
24031             
24032         };
24033         
24034         if(this.pos == 'top'){
24035             cfg.cls += ' dropup';
24036         }
24037         
24038         if(this.isSubMenu){
24039             cfg = {
24040                 tag : 'ul',
24041                 cls : 'dropdown-menu'
24042             }
24043         }
24044         
24045         return cfg;
24046     },
24047     
24048     onRender : function(ct, position)
24049     {
24050         this.isSubMenu = ct.hasClass('dropdown-submenu');
24051         
24052         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24053     },
24054     
24055     initEvents : function() 
24056     {
24057         if(this.isSubMenu){
24058             return;
24059         }
24060         
24061         this.hidden = true;
24062         
24063         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24064         this.triggerEl.on('click', this.onTriggerPress, this);
24065         
24066         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24067         this.buttonEl.on('click', this.onClick, this);
24068         
24069     },
24070     
24071     list : function()
24072     {
24073         if(this.isSubMenu){
24074             return this.el;
24075         }
24076         
24077         return this.el.select('ul.dropdown-menu', true).first();
24078     },
24079     
24080     onClick : function(e)
24081     {
24082         this.fireEvent("click", this, e);
24083     },
24084     
24085     onTriggerPress  : function(e)
24086     {   
24087         if (this.isVisible()) {
24088             this.hide();
24089         } else {
24090             this.show();
24091         }
24092     },
24093     
24094     isVisible : function(){
24095         return !this.hidden;
24096     },
24097     
24098     show : function()
24099     {
24100         this.fireEvent("beforeshow", this);
24101         
24102         this.hidden = false;
24103         this.el.addClass('open');
24104         
24105         Roo.get(document).on("mouseup", this.onMouseUp, this);
24106         
24107         this.fireEvent("show", this);
24108         
24109         
24110     },
24111     
24112     hide : function()
24113     {
24114         this.fireEvent("beforehide", this);
24115         
24116         this.hidden = true;
24117         this.el.removeClass('open');
24118         
24119         Roo.get(document).un("mouseup", this.onMouseUp);
24120         
24121         this.fireEvent("hide", this);
24122     },
24123     
24124     onMouseUp : function()
24125     {
24126         this.hide();
24127     }
24128     
24129 });
24130
24131  
24132  /*
24133  * - LGPL
24134  *
24135  * menu item
24136  * 
24137  */
24138 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24139
24140 /**
24141  * @class Roo.bootstrap.menu.Item
24142  * @extends Roo.bootstrap.Component
24143  * Bootstrap MenuItem class
24144  * @cfg {Boolean} submenu (true | false) default false
24145  * @cfg {String} html text of the item
24146  * @cfg {String} href the link
24147  * @cfg {Boolean} disable (true | false) default false
24148  * @cfg {Boolean} preventDefault (true | false) default true
24149  * @cfg {String} icon Font awesome icon
24150  * @cfg {String} pos Submenu align to (left | right) default right 
24151  * 
24152  * 
24153  * @constructor
24154  * Create a new Item
24155  * @param {Object} config The config object
24156  */
24157
24158
24159 Roo.bootstrap.menu.Item = function(config){
24160     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24161     this.addEvents({
24162         /**
24163          * @event mouseover
24164          * Fires when the mouse is hovering over this menu
24165          * @param {Roo.bootstrap.menu.Item} this
24166          * @param {Roo.EventObject} e
24167          */
24168         mouseover : true,
24169         /**
24170          * @event mouseout
24171          * Fires when the mouse exits this menu
24172          * @param {Roo.bootstrap.menu.Item} this
24173          * @param {Roo.EventObject} e
24174          */
24175         mouseout : true,
24176         // raw events
24177         /**
24178          * @event click
24179          * The raw click event for the entire grid.
24180          * @param {Roo.EventObject} e
24181          */
24182         click : true
24183     });
24184 };
24185
24186 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24187     
24188     submenu : false,
24189     href : '',
24190     html : '',
24191     preventDefault: true,
24192     disable : false,
24193     icon : false,
24194     pos : 'right',
24195     
24196     getAutoCreate : function()
24197     {
24198         var text = [
24199             {
24200                 tag : 'span',
24201                 cls : 'roo-menu-item-text',
24202                 html : this.html
24203             }
24204         ];
24205         
24206         if(this.icon){
24207             text.unshift({
24208                 tag : 'i',
24209                 cls : 'fa ' + this.icon
24210             })
24211         }
24212         
24213         var cfg = {
24214             tag : 'li',
24215             cn : [
24216                 {
24217                     tag : 'a',
24218                     href : this.href || '#',
24219                     cn : text
24220                 }
24221             ]
24222         };
24223         
24224         if(this.disable){
24225             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24226         }
24227         
24228         if(this.submenu){
24229             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24230             
24231             if(this.pos == 'left'){
24232                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24233             }
24234         }
24235         
24236         return cfg;
24237     },
24238     
24239     initEvents : function() 
24240     {
24241         this.el.on('mouseover', this.onMouseOver, this);
24242         this.el.on('mouseout', this.onMouseOut, this);
24243         
24244         this.el.select('a', true).first().on('click', this.onClick, this);
24245         
24246     },
24247     
24248     onClick : function(e)
24249     {
24250         if(this.preventDefault){
24251             e.preventDefault();
24252         }
24253         
24254         this.fireEvent("click", this, e);
24255     },
24256     
24257     onMouseOver : function(e)
24258     {
24259         if(this.submenu && this.pos == 'left'){
24260             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24261         }
24262         
24263         this.fireEvent("mouseover", this, e);
24264     },
24265     
24266     onMouseOut : function(e)
24267     {
24268         this.fireEvent("mouseout", this, e);
24269     }
24270 });
24271
24272  
24273
24274  /*
24275  * - LGPL
24276  *
24277  * menu separator
24278  * 
24279  */
24280 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24281
24282 /**
24283  * @class Roo.bootstrap.menu.Separator
24284  * @extends Roo.bootstrap.Component
24285  * Bootstrap Separator class
24286  * 
24287  * @constructor
24288  * Create a new Separator
24289  * @param {Object} config The config object
24290  */
24291
24292
24293 Roo.bootstrap.menu.Separator = function(config){
24294     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24295 };
24296
24297 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24298     
24299     getAutoCreate : function(){
24300         var cfg = {
24301             tag : 'li',
24302             cls: 'divider'
24303         };
24304         
24305         return cfg;
24306     }
24307    
24308 });
24309
24310  
24311
24312  /*
24313  * - LGPL
24314  *
24315  * Tooltip
24316  * 
24317  */
24318
24319 /**
24320  * @class Roo.bootstrap.Tooltip
24321  * Bootstrap Tooltip class
24322  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24323  * to determine which dom element triggers the tooltip.
24324  * 
24325  * It needs to add support for additional attributes like tooltip-position
24326  * 
24327  * @constructor
24328  * Create a new Toolti
24329  * @param {Object} config The config object
24330  */
24331
24332 Roo.bootstrap.Tooltip = function(config){
24333     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24334 };
24335
24336 Roo.apply(Roo.bootstrap.Tooltip, {
24337     /**
24338      * @function init initialize tooltip monitoring.
24339      * @static
24340      */
24341     currentEl : false,
24342     currentTip : false,
24343     currentRegion : false,
24344     
24345     //  init : delay?
24346     
24347     init : function()
24348     {
24349         Roo.get(document).on('mouseover', this.enter ,this);
24350         Roo.get(document).on('mouseout', this.leave, this);
24351          
24352         
24353         this.currentTip = new Roo.bootstrap.Tooltip();
24354     },
24355     
24356     enter : function(ev)
24357     {
24358         var dom = ev.getTarget();
24359         
24360         //Roo.log(['enter',dom]);
24361         var el = Roo.fly(dom);
24362         if (this.currentEl) {
24363             //Roo.log(dom);
24364             //Roo.log(this.currentEl);
24365             //Roo.log(this.currentEl.contains(dom));
24366             if (this.currentEl == el) {
24367                 return;
24368             }
24369             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24370                 return;
24371             }
24372
24373         }
24374         
24375         if (this.currentTip.el) {
24376             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24377         }    
24378         //Roo.log(ev);
24379         
24380         if(!el || el.dom == document){
24381             return;
24382         }
24383         
24384         var bindEl = el;
24385         
24386         // you can not look for children, as if el is the body.. then everythign is the child..
24387         if (!el.attr('tooltip')) { //
24388             if (!el.select("[tooltip]").elements.length) {
24389                 return;
24390             }
24391             // is the mouse over this child...?
24392             bindEl = el.select("[tooltip]").first();
24393             var xy = ev.getXY();
24394             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24395                 //Roo.log("not in region.");
24396                 return;
24397             }
24398             //Roo.log("child element over..");
24399             
24400         }
24401         this.currentEl = bindEl;
24402         this.currentTip.bind(bindEl);
24403         this.currentRegion = Roo.lib.Region.getRegion(dom);
24404         this.currentTip.enter();
24405         
24406     },
24407     leave : function(ev)
24408     {
24409         var dom = ev.getTarget();
24410         //Roo.log(['leave',dom]);
24411         if (!this.currentEl) {
24412             return;
24413         }
24414         
24415         
24416         if (dom != this.currentEl.dom) {
24417             return;
24418         }
24419         var xy = ev.getXY();
24420         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24421             return;
24422         }
24423         // only activate leave if mouse cursor is outside... bounding box..
24424         
24425         
24426         
24427         
24428         if (this.currentTip) {
24429             this.currentTip.leave();
24430         }
24431         //Roo.log('clear currentEl');
24432         this.currentEl = false;
24433         
24434         
24435     },
24436     alignment : {
24437         'left' : ['r-l', [-2,0], 'right'],
24438         'right' : ['l-r', [2,0], 'left'],
24439         'bottom' : ['t-b', [0,2], 'top'],
24440         'top' : [ 'b-t', [0,-2], 'bottom']
24441     }
24442     
24443 });
24444
24445
24446 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24447     
24448     
24449     bindEl : false,
24450     
24451     delay : null, // can be { show : 300 , hide: 500}
24452     
24453     timeout : null,
24454     
24455     hoverState : null, //???
24456     
24457     placement : 'bottom', 
24458     
24459     getAutoCreate : function(){
24460     
24461         var cfg = {
24462            cls : 'tooltip',
24463            role : 'tooltip',
24464            cn : [
24465                 {
24466                     cls : 'tooltip-arrow'
24467                 },
24468                 {
24469                     cls : 'tooltip-inner'
24470                 }
24471            ]
24472         };
24473         
24474         return cfg;
24475     },
24476     bind : function(el)
24477     {
24478         this.bindEl = el;
24479     },
24480       
24481     
24482     enter : function () {
24483        
24484         if (this.timeout != null) {
24485             clearTimeout(this.timeout);
24486         }
24487         
24488         this.hoverState = 'in';
24489          //Roo.log("enter - show");
24490         if (!this.delay || !this.delay.show) {
24491             this.show();
24492             return;
24493         }
24494         var _t = this;
24495         this.timeout = setTimeout(function () {
24496             if (_t.hoverState == 'in') {
24497                 _t.show();
24498             }
24499         }, this.delay.show);
24500     },
24501     leave : function()
24502     {
24503         clearTimeout(this.timeout);
24504     
24505         this.hoverState = 'out';
24506          if (!this.delay || !this.delay.hide) {
24507             this.hide();
24508             return;
24509         }
24510        
24511         var _t = this;
24512         this.timeout = setTimeout(function () {
24513             //Roo.log("leave - timeout");
24514             
24515             if (_t.hoverState == 'out') {
24516                 _t.hide();
24517                 Roo.bootstrap.Tooltip.currentEl = false;
24518             }
24519         }, delay);
24520     },
24521     
24522     show : function ()
24523     {
24524         if (!this.el) {
24525             this.render(document.body);
24526         }
24527         // set content.
24528         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24529         
24530         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24531         
24532         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24533         
24534         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24535         
24536         var placement = typeof this.placement == 'function' ?
24537             this.placement.call(this, this.el, on_el) :
24538             this.placement;
24539             
24540         var autoToken = /\s?auto?\s?/i;
24541         var autoPlace = autoToken.test(placement);
24542         if (autoPlace) {
24543             placement = placement.replace(autoToken, '') || 'top';
24544         }
24545         
24546         //this.el.detach()
24547         //this.el.setXY([0,0]);
24548         this.el.show();
24549         //this.el.dom.style.display='block';
24550         
24551         //this.el.appendTo(on_el);
24552         
24553         var p = this.getPosition();
24554         var box = this.el.getBox();
24555         
24556         if (autoPlace) {
24557             // fixme..
24558         }
24559         
24560         var align = Roo.bootstrap.Tooltip.alignment[placement];
24561         
24562         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24563         
24564         if(placement == 'top' || placement == 'bottom'){
24565             if(xy[0] < 0){
24566                 placement = 'right';
24567             }
24568             
24569             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24570                 placement = 'left';
24571             }
24572             
24573             var scroll = Roo.select('body', true).first().getScroll();
24574             
24575             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24576                 placement = 'top';
24577             }
24578             
24579         }
24580         
24581         align = Roo.bootstrap.Tooltip.alignment[placement];
24582         
24583         this.el.alignTo(this.bindEl, align[0],align[1]);
24584         //var arrow = this.el.select('.arrow',true).first();
24585         //arrow.set(align[2], 
24586         
24587         this.el.addClass(placement);
24588         
24589         this.el.addClass('in fade');
24590         
24591         this.hoverState = null;
24592         
24593         if (this.el.hasClass('fade')) {
24594             // fade it?
24595         }
24596         
24597     },
24598     hide : function()
24599     {
24600          
24601         if (!this.el) {
24602             return;
24603         }
24604         //this.el.setXY([0,0]);
24605         this.el.removeClass('in');
24606         //this.el.hide();
24607         
24608     }
24609     
24610 });
24611  
24612
24613  /*
24614  * - LGPL
24615  *
24616  * Location Picker
24617  * 
24618  */
24619
24620 /**
24621  * @class Roo.bootstrap.LocationPicker
24622  * @extends Roo.bootstrap.Component
24623  * Bootstrap LocationPicker class
24624  * @cfg {Number} latitude Position when init default 0
24625  * @cfg {Number} longitude Position when init default 0
24626  * @cfg {Number} zoom default 15
24627  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24628  * @cfg {Boolean} mapTypeControl default false
24629  * @cfg {Boolean} disableDoubleClickZoom default false
24630  * @cfg {Boolean} scrollwheel default true
24631  * @cfg {Boolean} streetViewControl default false
24632  * @cfg {Number} radius default 0
24633  * @cfg {String} locationName
24634  * @cfg {Boolean} draggable default true
24635  * @cfg {Boolean} enableAutocomplete default false
24636  * @cfg {Boolean} enableReverseGeocode default true
24637  * @cfg {String} markerTitle
24638  * 
24639  * @constructor
24640  * Create a new LocationPicker
24641  * @param {Object} config The config object
24642  */
24643
24644
24645 Roo.bootstrap.LocationPicker = function(config){
24646     
24647     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24648     
24649     this.addEvents({
24650         /**
24651          * @event initial
24652          * Fires when the picker initialized.
24653          * @param {Roo.bootstrap.LocationPicker} this
24654          * @param {Google Location} location
24655          */
24656         initial : true,
24657         /**
24658          * @event positionchanged
24659          * Fires when the picker position changed.
24660          * @param {Roo.bootstrap.LocationPicker} this
24661          * @param {Google Location} location
24662          */
24663         positionchanged : true,
24664         /**
24665          * @event resize
24666          * Fires when the map resize.
24667          * @param {Roo.bootstrap.LocationPicker} this
24668          */
24669         resize : true,
24670         /**
24671          * @event show
24672          * Fires when the map show.
24673          * @param {Roo.bootstrap.LocationPicker} this
24674          */
24675         show : true,
24676         /**
24677          * @event hide
24678          * Fires when the map hide.
24679          * @param {Roo.bootstrap.LocationPicker} this
24680          */
24681         hide : true,
24682         /**
24683          * @event mapClick
24684          * Fires when click the map.
24685          * @param {Roo.bootstrap.LocationPicker} this
24686          * @param {Map event} e
24687          */
24688         mapClick : true,
24689         /**
24690          * @event mapRightClick
24691          * Fires when right click the map.
24692          * @param {Roo.bootstrap.LocationPicker} this
24693          * @param {Map event} e
24694          */
24695         mapRightClick : true,
24696         /**
24697          * @event markerClick
24698          * Fires when click the marker.
24699          * @param {Roo.bootstrap.LocationPicker} this
24700          * @param {Map event} e
24701          */
24702         markerClick : true,
24703         /**
24704          * @event markerRightClick
24705          * Fires when right click the marker.
24706          * @param {Roo.bootstrap.LocationPicker} this
24707          * @param {Map event} e
24708          */
24709         markerRightClick : true,
24710         /**
24711          * @event OverlayViewDraw
24712          * Fires when OverlayView Draw
24713          * @param {Roo.bootstrap.LocationPicker} this
24714          */
24715         OverlayViewDraw : true,
24716         /**
24717          * @event OverlayViewOnAdd
24718          * Fires when OverlayView Draw
24719          * @param {Roo.bootstrap.LocationPicker} this
24720          */
24721         OverlayViewOnAdd : true,
24722         /**
24723          * @event OverlayViewOnRemove
24724          * Fires when OverlayView Draw
24725          * @param {Roo.bootstrap.LocationPicker} this
24726          */
24727         OverlayViewOnRemove : true,
24728         /**
24729          * @event OverlayViewShow
24730          * Fires when OverlayView Draw
24731          * @param {Roo.bootstrap.LocationPicker} this
24732          * @param {Pixel} cpx
24733          */
24734         OverlayViewShow : true,
24735         /**
24736          * @event OverlayViewHide
24737          * Fires when OverlayView Draw
24738          * @param {Roo.bootstrap.LocationPicker} this
24739          */
24740         OverlayViewHide : true,
24741         /**
24742          * @event loadexception
24743          * Fires when load google lib failed.
24744          * @param {Roo.bootstrap.LocationPicker} this
24745          */
24746         loadexception : true
24747     });
24748         
24749 };
24750
24751 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24752     
24753     gMapContext: false,
24754     
24755     latitude: 0,
24756     longitude: 0,
24757     zoom: 15,
24758     mapTypeId: false,
24759     mapTypeControl: false,
24760     disableDoubleClickZoom: false,
24761     scrollwheel: true,
24762     streetViewControl: false,
24763     radius: 0,
24764     locationName: '',
24765     draggable: true,
24766     enableAutocomplete: false,
24767     enableReverseGeocode: true,
24768     markerTitle: '',
24769     
24770     getAutoCreate: function()
24771     {
24772
24773         var cfg = {
24774             tag: 'div',
24775             cls: 'roo-location-picker'
24776         };
24777         
24778         return cfg
24779     },
24780     
24781     initEvents: function(ct, position)
24782     {       
24783         if(!this.el.getWidth() || this.isApplied()){
24784             return;
24785         }
24786         
24787         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24788         
24789         this.initial();
24790     },
24791     
24792     initial: function()
24793     {
24794         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24795             this.fireEvent('loadexception', this);
24796             return;
24797         }
24798         
24799         if(!this.mapTypeId){
24800             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24801         }
24802         
24803         this.gMapContext = this.GMapContext();
24804         
24805         this.initOverlayView();
24806         
24807         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24808         
24809         var _this = this;
24810                 
24811         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24812             _this.setPosition(_this.gMapContext.marker.position);
24813         });
24814         
24815         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24816             _this.fireEvent('mapClick', this, event);
24817             
24818         });
24819
24820         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24821             _this.fireEvent('mapRightClick', this, event);
24822             
24823         });
24824         
24825         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24826             _this.fireEvent('markerClick', this, event);
24827             
24828         });
24829
24830         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24831             _this.fireEvent('markerRightClick', this, event);
24832             
24833         });
24834         
24835         this.setPosition(this.gMapContext.location);
24836         
24837         this.fireEvent('initial', this, this.gMapContext.location);
24838     },
24839     
24840     initOverlayView: function()
24841     {
24842         var _this = this;
24843         
24844         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24845             
24846             draw: function()
24847             {
24848                 _this.fireEvent('OverlayViewDraw', _this);
24849             },
24850             
24851             onAdd: function()
24852             {
24853                 _this.fireEvent('OverlayViewOnAdd', _this);
24854             },
24855             
24856             onRemove: function()
24857             {
24858                 _this.fireEvent('OverlayViewOnRemove', _this);
24859             },
24860             
24861             show: function(cpx)
24862             {
24863                 _this.fireEvent('OverlayViewShow', _this, cpx);
24864             },
24865             
24866             hide: function()
24867             {
24868                 _this.fireEvent('OverlayViewHide', _this);
24869             }
24870             
24871         });
24872     },
24873     
24874     fromLatLngToContainerPixel: function(event)
24875     {
24876         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24877     },
24878     
24879     isApplied: function() 
24880     {
24881         return this.getGmapContext() == false ? false : true;
24882     },
24883     
24884     getGmapContext: function() 
24885     {
24886         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24887     },
24888     
24889     GMapContext: function() 
24890     {
24891         var position = new google.maps.LatLng(this.latitude, this.longitude);
24892         
24893         var _map = new google.maps.Map(this.el.dom, {
24894             center: position,
24895             zoom: this.zoom,
24896             mapTypeId: this.mapTypeId,
24897             mapTypeControl: this.mapTypeControl,
24898             disableDoubleClickZoom: this.disableDoubleClickZoom,
24899             scrollwheel: this.scrollwheel,
24900             streetViewControl: this.streetViewControl,
24901             locationName: this.locationName,
24902             draggable: this.draggable,
24903             enableAutocomplete: this.enableAutocomplete,
24904             enableReverseGeocode: this.enableReverseGeocode
24905         });
24906         
24907         var _marker = new google.maps.Marker({
24908             position: position,
24909             map: _map,
24910             title: this.markerTitle,
24911             draggable: this.draggable
24912         });
24913         
24914         return {
24915             map: _map,
24916             marker: _marker,
24917             circle: null,
24918             location: position,
24919             radius: this.radius,
24920             locationName: this.locationName,
24921             addressComponents: {
24922                 formatted_address: null,
24923                 addressLine1: null,
24924                 addressLine2: null,
24925                 streetName: null,
24926                 streetNumber: null,
24927                 city: null,
24928                 district: null,
24929                 state: null,
24930                 stateOrProvince: null
24931             },
24932             settings: this,
24933             domContainer: this.el.dom,
24934             geodecoder: new google.maps.Geocoder()
24935         };
24936     },
24937     
24938     drawCircle: function(center, radius, options) 
24939     {
24940         if (this.gMapContext.circle != null) {
24941             this.gMapContext.circle.setMap(null);
24942         }
24943         if (radius > 0) {
24944             radius *= 1;
24945             options = Roo.apply({}, options, {
24946                 strokeColor: "#0000FF",
24947                 strokeOpacity: .35,
24948                 strokeWeight: 2,
24949                 fillColor: "#0000FF",
24950                 fillOpacity: .2
24951             });
24952             
24953             options.map = this.gMapContext.map;
24954             options.radius = radius;
24955             options.center = center;
24956             this.gMapContext.circle = new google.maps.Circle(options);
24957             return this.gMapContext.circle;
24958         }
24959         
24960         return null;
24961     },
24962     
24963     setPosition: function(location) 
24964     {
24965         this.gMapContext.location = location;
24966         this.gMapContext.marker.setPosition(location);
24967         this.gMapContext.map.panTo(location);
24968         this.drawCircle(location, this.gMapContext.radius, {});
24969         
24970         var _this = this;
24971         
24972         if (this.gMapContext.settings.enableReverseGeocode) {
24973             this.gMapContext.geodecoder.geocode({
24974                 latLng: this.gMapContext.location
24975             }, function(results, status) {
24976                 
24977                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24978                     _this.gMapContext.locationName = results[0].formatted_address;
24979                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24980                     
24981                     _this.fireEvent('positionchanged', this, location);
24982                 }
24983             });
24984             
24985             return;
24986         }
24987         
24988         this.fireEvent('positionchanged', this, location);
24989     },
24990     
24991     resize: function()
24992     {
24993         google.maps.event.trigger(this.gMapContext.map, "resize");
24994         
24995         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24996         
24997         this.fireEvent('resize', this);
24998     },
24999     
25000     setPositionByLatLng: function(latitude, longitude)
25001     {
25002         this.setPosition(new google.maps.LatLng(latitude, longitude));
25003     },
25004     
25005     getCurrentPosition: function() 
25006     {
25007         return {
25008             latitude: this.gMapContext.location.lat(),
25009             longitude: this.gMapContext.location.lng()
25010         };
25011     },
25012     
25013     getAddressName: function() 
25014     {
25015         return this.gMapContext.locationName;
25016     },
25017     
25018     getAddressComponents: function() 
25019     {
25020         return this.gMapContext.addressComponents;
25021     },
25022     
25023     address_component_from_google_geocode: function(address_components) 
25024     {
25025         var result = {};
25026         
25027         for (var i = 0; i < address_components.length; i++) {
25028             var component = address_components[i];
25029             if (component.types.indexOf("postal_code") >= 0) {
25030                 result.postalCode = component.short_name;
25031             } else if (component.types.indexOf("street_number") >= 0) {
25032                 result.streetNumber = component.short_name;
25033             } else if (component.types.indexOf("route") >= 0) {
25034                 result.streetName = component.short_name;
25035             } else if (component.types.indexOf("neighborhood") >= 0) {
25036                 result.city = component.short_name;
25037             } else if (component.types.indexOf("locality") >= 0) {
25038                 result.city = component.short_name;
25039             } else if (component.types.indexOf("sublocality") >= 0) {
25040                 result.district = component.short_name;
25041             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25042                 result.stateOrProvince = component.short_name;
25043             } else if (component.types.indexOf("country") >= 0) {
25044                 result.country = component.short_name;
25045             }
25046         }
25047         
25048         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25049         result.addressLine2 = "";
25050         return result;
25051     },
25052     
25053     setZoomLevel: function(zoom)
25054     {
25055         this.gMapContext.map.setZoom(zoom);
25056     },
25057     
25058     show: function()
25059     {
25060         if(!this.el){
25061             return;
25062         }
25063         
25064         this.el.show();
25065         
25066         this.resize();
25067         
25068         this.fireEvent('show', this);
25069     },
25070     
25071     hide: function()
25072     {
25073         if(!this.el){
25074             return;
25075         }
25076         
25077         this.el.hide();
25078         
25079         this.fireEvent('hide', this);
25080     }
25081     
25082 });
25083
25084 Roo.apply(Roo.bootstrap.LocationPicker, {
25085     
25086     OverlayView : function(map, options)
25087     {
25088         options = options || {};
25089         
25090         this.setMap(map);
25091     }
25092     
25093     
25094 });/*
25095  * - LGPL
25096  *
25097  * Alert
25098  * 
25099  */
25100
25101 /**
25102  * @class Roo.bootstrap.Alert
25103  * @extends Roo.bootstrap.Component
25104  * Bootstrap Alert class
25105  * @cfg {String} title The title of alert
25106  * @cfg {String} html The content of alert
25107  * @cfg {String} weight (  success | info | warning | danger )
25108  * @cfg {String} faicon font-awesomeicon
25109  * 
25110  * @constructor
25111  * Create a new alert
25112  * @param {Object} config The config object
25113  */
25114
25115
25116 Roo.bootstrap.Alert = function(config){
25117     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25118     
25119 };
25120
25121 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25122     
25123     title: '',
25124     html: '',
25125     weight: false,
25126     faicon: false,
25127     
25128     getAutoCreate : function()
25129     {
25130         
25131         var cfg = {
25132             tag : 'div',
25133             cls : 'alert',
25134             cn : [
25135                 {
25136                     tag : 'i',
25137                     cls : 'roo-alert-icon'
25138                     
25139                 },
25140                 {
25141                     tag : 'b',
25142                     cls : 'roo-alert-title',
25143                     html : this.title
25144                 },
25145                 {
25146                     tag : 'span',
25147                     cls : 'roo-alert-text',
25148                     html : this.html
25149                 }
25150             ]
25151         };
25152         
25153         if(this.faicon){
25154             cfg.cn[0].cls += ' fa ' + this.faicon;
25155         }
25156         
25157         if(this.weight){
25158             cfg.cls += ' alert-' + this.weight;
25159         }
25160         
25161         return cfg;
25162     },
25163     
25164     initEvents: function() 
25165     {
25166         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25167     },
25168     
25169     setTitle : function(str)
25170     {
25171         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25172     },
25173     
25174     setText : function(str)
25175     {
25176         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25177     },
25178     
25179     setWeight : function(weight)
25180     {
25181         if(this.weight){
25182             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25183         }
25184         
25185         this.weight = weight;
25186         
25187         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25188     },
25189     
25190     setIcon : function(icon)
25191     {
25192         if(this.faicon){
25193             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25194         }
25195         
25196         this.faicon = icon;
25197         
25198         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25199     },
25200     
25201     hide: function() 
25202     {
25203         this.el.hide();   
25204     },
25205     
25206     show: function() 
25207     {  
25208         this.el.show();   
25209     }
25210     
25211 });
25212
25213  
25214 /*
25215 * Licence: LGPL
25216 */
25217
25218 /**
25219  * @class Roo.bootstrap.UploadCropbox
25220  * @extends Roo.bootstrap.Component
25221  * Bootstrap UploadCropbox class
25222  * @cfg {String} emptyText show when image has been loaded
25223  * @cfg {String} rotateNotify show when image too small to rotate
25224  * @cfg {Number} errorTimeout default 3000
25225  * @cfg {Number} minWidth default 300
25226  * @cfg {Number} minHeight default 300
25227  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25228  * @cfg {Boolean} isDocument (true|false) default false
25229  * @cfg {String} url action url
25230  * @cfg {String} paramName default 'imageUpload'
25231  * @cfg {String} method default POST
25232  * @cfg {Boolean} loadMask (true|false) default true
25233  * @cfg {Boolean} loadingText default 'Loading...'
25234  * 
25235  * @constructor
25236  * Create a new UploadCropbox
25237  * @param {Object} config The config object
25238  */
25239
25240 Roo.bootstrap.UploadCropbox = function(config){
25241     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25242     
25243     this.addEvents({
25244         /**
25245          * @event beforeselectfile
25246          * Fire before select file
25247          * @param {Roo.bootstrap.UploadCropbox} this
25248          */
25249         "beforeselectfile" : true,
25250         /**
25251          * @event initial
25252          * Fire after initEvent
25253          * @param {Roo.bootstrap.UploadCropbox} this
25254          */
25255         "initial" : true,
25256         /**
25257          * @event crop
25258          * Fire after initEvent
25259          * @param {Roo.bootstrap.UploadCropbox} this
25260          * @param {String} data
25261          */
25262         "crop" : true,
25263         /**
25264          * @event prepare
25265          * Fire when preparing the file data
25266          * @param {Roo.bootstrap.UploadCropbox} this
25267          * @param {Object} file
25268          */
25269         "prepare" : true,
25270         /**
25271          * @event exception
25272          * Fire when get exception
25273          * @param {Roo.bootstrap.UploadCropbox} this
25274          * @param {XMLHttpRequest} xhr
25275          */
25276         "exception" : true,
25277         /**
25278          * @event beforeloadcanvas
25279          * Fire before load the canvas
25280          * @param {Roo.bootstrap.UploadCropbox} this
25281          * @param {String} src
25282          */
25283         "beforeloadcanvas" : true,
25284         /**
25285          * @event trash
25286          * Fire when trash image
25287          * @param {Roo.bootstrap.UploadCropbox} this
25288          */
25289         "trash" : true,
25290         /**
25291          * @event download
25292          * Fire when download the image
25293          * @param {Roo.bootstrap.UploadCropbox} this
25294          */
25295         "download" : true,
25296         /**
25297          * @event footerbuttonclick
25298          * Fire when footerbuttonclick
25299          * @param {Roo.bootstrap.UploadCropbox} this
25300          * @param {String} type
25301          */
25302         "footerbuttonclick" : true,
25303         /**
25304          * @event resize
25305          * Fire when resize
25306          * @param {Roo.bootstrap.UploadCropbox} this
25307          */
25308         "resize" : true,
25309         /**
25310          * @event rotate
25311          * Fire when rotate the image
25312          * @param {Roo.bootstrap.UploadCropbox} this
25313          * @param {String} pos
25314          */
25315         "rotate" : true,
25316         /**
25317          * @event inspect
25318          * Fire when inspect the file
25319          * @param {Roo.bootstrap.UploadCropbox} this
25320          * @param {Object} file
25321          */
25322         "inspect" : true,
25323         /**
25324          * @event upload
25325          * Fire when xhr upload the file
25326          * @param {Roo.bootstrap.UploadCropbox} this
25327          * @param {Object} data
25328          */
25329         "upload" : true,
25330         /**
25331          * @event arrange
25332          * Fire when arrange the file data
25333          * @param {Roo.bootstrap.UploadCropbox} this
25334          * @param {Object} formData
25335          */
25336         "arrange" : true
25337     });
25338     
25339     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25340 };
25341
25342 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25343     
25344     emptyText : 'Click to upload image',
25345     rotateNotify : 'Image is too small to rotate',
25346     errorTimeout : 3000,
25347     scale : 0,
25348     baseScale : 1,
25349     rotate : 0,
25350     dragable : false,
25351     pinching : false,
25352     mouseX : 0,
25353     mouseY : 0,
25354     cropData : false,
25355     minWidth : 300,
25356     minHeight : 300,
25357     file : false,
25358     exif : {},
25359     baseRotate : 1,
25360     cropType : 'image/jpeg',
25361     buttons : false,
25362     canvasLoaded : false,
25363     isDocument : false,
25364     method : 'POST',
25365     paramName : 'imageUpload',
25366     loadMask : true,
25367     loadingText : 'Loading...',
25368     maskEl : false,
25369     
25370     getAutoCreate : function()
25371     {
25372         var cfg = {
25373             tag : 'div',
25374             cls : 'roo-upload-cropbox',
25375             cn : [
25376                 {
25377                     tag : 'input',
25378                     cls : 'roo-upload-cropbox-selector',
25379                     type : 'file'
25380                 },
25381                 {
25382                     tag : 'div',
25383                     cls : 'roo-upload-cropbox-body',
25384                     style : 'cursor:pointer',
25385                     cn : [
25386                         {
25387                             tag : 'div',
25388                             cls : 'roo-upload-cropbox-preview'
25389                         },
25390                         {
25391                             tag : 'div',
25392                             cls : 'roo-upload-cropbox-thumb'
25393                         },
25394                         {
25395                             tag : 'div',
25396                             cls : 'roo-upload-cropbox-empty-notify',
25397                             html : this.emptyText
25398                         },
25399                         {
25400                             tag : 'div',
25401                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25402                             html : this.rotateNotify
25403                         }
25404                     ]
25405                 },
25406                 {
25407                     tag : 'div',
25408                     cls : 'roo-upload-cropbox-footer',
25409                     cn : {
25410                         tag : 'div',
25411                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25412                         cn : []
25413                     }
25414                 }
25415             ]
25416         };
25417         
25418         return cfg;
25419     },
25420     
25421     onRender : function(ct, position)
25422     {
25423         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25424         
25425         if (this.buttons.length) {
25426             
25427             Roo.each(this.buttons, function(bb) {
25428                 
25429                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25430                 
25431                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25432                 
25433             }, this);
25434         }
25435         
25436         if(this.loadMask){
25437             this.maskEl = this.el;
25438         }
25439     },
25440     
25441     initEvents : function()
25442     {
25443         this.urlAPI = (window.createObjectURL && window) || 
25444                                 (window.URL && URL.revokeObjectURL && URL) || 
25445                                 (window.webkitURL && webkitURL);
25446                         
25447         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25448         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25449         
25450         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25451         this.selectorEl.hide();
25452         
25453         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25454         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25455         
25456         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25457         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25458         this.thumbEl.hide();
25459         
25460         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25461         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25462         
25463         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25464         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25465         this.errorEl.hide();
25466         
25467         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25468         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25469         this.footerEl.hide();
25470         
25471         this.setThumbBoxSize();
25472         
25473         this.bind();
25474         
25475         this.resize();
25476         
25477         this.fireEvent('initial', this);
25478     },
25479
25480     bind : function()
25481     {
25482         var _this = this;
25483         
25484         window.addEventListener("resize", function() { _this.resize(); } );
25485         
25486         this.bodyEl.on('click', this.beforeSelectFile, this);
25487         
25488         if(Roo.isTouch){
25489             this.bodyEl.on('touchstart', this.onTouchStart, this);
25490             this.bodyEl.on('touchmove', this.onTouchMove, this);
25491             this.bodyEl.on('touchend', this.onTouchEnd, this);
25492         }
25493         
25494         if(!Roo.isTouch){
25495             this.bodyEl.on('mousedown', this.onMouseDown, this);
25496             this.bodyEl.on('mousemove', this.onMouseMove, this);
25497             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25498             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25499             Roo.get(document).on('mouseup', this.onMouseUp, this);
25500         }
25501         
25502         this.selectorEl.on('change', this.onFileSelected, this);
25503     },
25504     
25505     reset : function()
25506     {    
25507         this.scale = 0;
25508         this.baseScale = 1;
25509         this.rotate = 0;
25510         this.baseRotate = 1;
25511         this.dragable = false;
25512         this.pinching = false;
25513         this.mouseX = 0;
25514         this.mouseY = 0;
25515         this.cropData = false;
25516         this.notifyEl.dom.innerHTML = this.emptyText;
25517         
25518         this.selectorEl.dom.value = '';
25519         
25520     },
25521     
25522     resize : function()
25523     {
25524         if(this.fireEvent('resize', this) != false){
25525             this.setThumbBoxPosition();
25526             this.setCanvasPosition();
25527         }
25528     },
25529     
25530     onFooterButtonClick : function(e, el, o, type)
25531     {
25532         switch (type) {
25533             case 'rotate-left' :
25534                 this.onRotateLeft(e);
25535                 break;
25536             case 'rotate-right' :
25537                 this.onRotateRight(e);
25538                 break;
25539             case 'picture' :
25540                 this.beforeSelectFile(e);
25541                 break;
25542             case 'trash' :
25543                 this.trash(e);
25544                 break;
25545             case 'crop' :
25546                 this.crop(e);
25547                 break;
25548             case 'download' :
25549                 this.download(e);
25550                 break;
25551             default :
25552                 break;
25553         }
25554         
25555         this.fireEvent('footerbuttonclick', this, type);
25556     },
25557     
25558     beforeSelectFile : function(e)
25559     {
25560         e.preventDefault();
25561         
25562         if(this.fireEvent('beforeselectfile', this) != false){
25563             this.selectorEl.dom.click();
25564         }
25565     },
25566     
25567     onFileSelected : function(e)
25568     {
25569         e.preventDefault();
25570         
25571         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25572             return;
25573         }
25574         
25575         var file = this.selectorEl.dom.files[0];
25576         
25577         if(this.fireEvent('inspect', this, file) != false){
25578             this.prepare(file);
25579         }
25580         
25581     },
25582     
25583     trash : function(e)
25584     {
25585         this.fireEvent('trash', this);
25586     },
25587     
25588     download : function(e)
25589     {
25590         this.fireEvent('download', this);
25591     },
25592     
25593     loadCanvas : function(src)
25594     {   
25595         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25596             
25597             this.reset();
25598             
25599             this.imageEl = document.createElement('img');
25600             
25601             var _this = this;
25602             
25603             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25604             
25605             this.imageEl.src = src;
25606         }
25607     },
25608     
25609     onLoadCanvas : function()
25610     {   
25611         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25612         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25613         
25614         this.bodyEl.un('click', this.beforeSelectFile, this);
25615         
25616         this.notifyEl.hide();
25617         this.thumbEl.show();
25618         this.footerEl.show();
25619         
25620         this.baseRotateLevel();
25621         
25622         if(this.isDocument){
25623             this.setThumbBoxSize();
25624         }
25625         
25626         this.setThumbBoxPosition();
25627         
25628         this.baseScaleLevel();
25629         
25630         this.draw();
25631         
25632         this.resize();
25633         
25634         this.canvasLoaded = true;
25635         
25636         if(this.loadMask){
25637             this.maskEl.unmask();
25638         }
25639         
25640     },
25641     
25642     setCanvasPosition : function()
25643     {   
25644         if(!this.canvasEl){
25645             return;
25646         }
25647         
25648         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25649         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25650         
25651         this.previewEl.setLeft(pw);
25652         this.previewEl.setTop(ph);
25653         
25654     },
25655     
25656     onMouseDown : function(e)
25657     {   
25658         e.stopEvent();
25659         
25660         this.dragable = true;
25661         this.pinching = false;
25662         
25663         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25664             this.dragable = false;
25665             return;
25666         }
25667         
25668         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25669         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25670         
25671     },
25672     
25673     onMouseMove : function(e)
25674     {   
25675         e.stopEvent();
25676         
25677         if(!this.canvasLoaded){
25678             return;
25679         }
25680         
25681         if (!this.dragable){
25682             return;
25683         }
25684         
25685         var minX = Math.ceil(this.thumbEl.getLeft(true));
25686         var minY = Math.ceil(this.thumbEl.getTop(true));
25687         
25688         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25689         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25690         
25691         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25692         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25693         
25694         x = x - this.mouseX;
25695         y = y - this.mouseY;
25696         
25697         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25698         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25699         
25700         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25701         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25702         
25703         this.previewEl.setLeft(bgX);
25704         this.previewEl.setTop(bgY);
25705         
25706         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25707         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25708     },
25709     
25710     onMouseUp : function(e)
25711     {   
25712         e.stopEvent();
25713         
25714         this.dragable = false;
25715     },
25716     
25717     onMouseWheel : function(e)
25718     {   
25719         e.stopEvent();
25720         
25721         this.startScale = this.scale;
25722         
25723         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25724         
25725         if(!this.zoomable()){
25726             this.scale = this.startScale;
25727             return;
25728         }
25729         
25730         this.draw();
25731         
25732         return;
25733     },
25734     
25735     zoomable : function()
25736     {
25737         var minScale = this.thumbEl.getWidth() / this.minWidth;
25738         
25739         if(this.minWidth < this.minHeight){
25740             minScale = this.thumbEl.getHeight() / this.minHeight;
25741         }
25742         
25743         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25744         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25745         
25746         if(
25747                 this.isDocument &&
25748                 (this.rotate == 0 || this.rotate == 180) && 
25749                 (
25750                     width > this.imageEl.OriginWidth || 
25751                     height > this.imageEl.OriginHeight ||
25752                     (width < this.minWidth && height < this.minHeight)
25753                 )
25754         ){
25755             return false;
25756         }
25757         
25758         if(
25759                 this.isDocument &&
25760                 (this.rotate == 90 || this.rotate == 270) && 
25761                 (
25762                     width > this.imageEl.OriginWidth || 
25763                     height > this.imageEl.OriginHeight ||
25764                     (width < this.minHeight && height < this.minWidth)
25765                 )
25766         ){
25767             return false;
25768         }
25769         
25770         if(
25771                 !this.isDocument &&
25772                 (this.rotate == 0 || this.rotate == 180) && 
25773                 (
25774                     width < this.minWidth || 
25775                     width > this.imageEl.OriginWidth || 
25776                     height < this.minHeight || 
25777                     height > this.imageEl.OriginHeight
25778                 )
25779         ){
25780             return false;
25781         }
25782         
25783         if(
25784                 !this.isDocument &&
25785                 (this.rotate == 90 || this.rotate == 270) && 
25786                 (
25787                     width < this.minHeight || 
25788                     width > this.imageEl.OriginWidth || 
25789                     height < this.minWidth || 
25790                     height > this.imageEl.OriginHeight
25791                 )
25792         ){
25793             return false;
25794         }
25795         
25796         return true;
25797         
25798     },
25799     
25800     onRotateLeft : function(e)
25801     {   
25802         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25803             
25804             var minScale = this.thumbEl.getWidth() / this.minWidth;
25805             
25806             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25807             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25808             
25809             this.startScale = this.scale;
25810             
25811             while (this.getScaleLevel() < minScale){
25812             
25813                 this.scale = this.scale + 1;
25814                 
25815                 if(!this.zoomable()){
25816                     break;
25817                 }
25818                 
25819                 if(
25820                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25821                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25822                 ){
25823                     continue;
25824                 }
25825                 
25826                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25827
25828                 this.draw();
25829                 
25830                 return;
25831             }
25832             
25833             this.scale = this.startScale;
25834             
25835             this.onRotateFail();
25836             
25837             return false;
25838         }
25839         
25840         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25841
25842         if(this.isDocument){
25843             this.setThumbBoxSize();
25844             this.setThumbBoxPosition();
25845             this.setCanvasPosition();
25846         }
25847         
25848         this.draw();
25849         
25850         this.fireEvent('rotate', this, 'left');
25851         
25852     },
25853     
25854     onRotateRight : function(e)
25855     {
25856         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25857             
25858             var minScale = this.thumbEl.getWidth() / this.minWidth;
25859         
25860             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25861             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25862             
25863             this.startScale = this.scale;
25864             
25865             while (this.getScaleLevel() < minScale){
25866             
25867                 this.scale = this.scale + 1;
25868                 
25869                 if(!this.zoomable()){
25870                     break;
25871                 }
25872                 
25873                 if(
25874                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25875                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25876                 ){
25877                     continue;
25878                 }
25879                 
25880                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25881
25882                 this.draw();
25883                 
25884                 return;
25885             }
25886             
25887             this.scale = this.startScale;
25888             
25889             this.onRotateFail();
25890             
25891             return false;
25892         }
25893         
25894         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25895
25896         if(this.isDocument){
25897             this.setThumbBoxSize();
25898             this.setThumbBoxPosition();
25899             this.setCanvasPosition();
25900         }
25901         
25902         this.draw();
25903         
25904         this.fireEvent('rotate', this, 'right');
25905     },
25906     
25907     onRotateFail : function()
25908     {
25909         this.errorEl.show(true);
25910         
25911         var _this = this;
25912         
25913         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25914     },
25915     
25916     draw : function()
25917     {
25918         this.previewEl.dom.innerHTML = '';
25919         
25920         var canvasEl = document.createElement("canvas");
25921         
25922         var contextEl = canvasEl.getContext("2d");
25923         
25924         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25925         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25926         var center = this.imageEl.OriginWidth / 2;
25927         
25928         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25929             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25930             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25931             center = this.imageEl.OriginHeight / 2;
25932         }
25933         
25934         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25935         
25936         contextEl.translate(center, center);
25937         contextEl.rotate(this.rotate * Math.PI / 180);
25938
25939         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25940         
25941         this.canvasEl = document.createElement("canvas");
25942         
25943         this.contextEl = this.canvasEl.getContext("2d");
25944         
25945         switch (this.rotate) {
25946             case 0 :
25947                 
25948                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25949                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25950                 
25951                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25952                 
25953                 break;
25954             case 90 : 
25955                 
25956                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25957                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25958                 
25959                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25960                     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);
25961                     break;
25962                 }
25963                 
25964                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25965                 
25966                 break;
25967             case 180 :
25968                 
25969                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25970                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25971                 
25972                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25973                     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);
25974                     break;
25975                 }
25976                 
25977                 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);
25978                 
25979                 break;
25980             case 270 :
25981                 
25982                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25983                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25984         
25985                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25986                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25987                     break;
25988                 }
25989                 
25990                 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);
25991                 
25992                 break;
25993             default : 
25994                 break;
25995         }
25996         
25997         this.previewEl.appendChild(this.canvasEl);
25998         
25999         this.setCanvasPosition();
26000     },
26001     
26002     crop : function()
26003     {
26004         if(!this.canvasLoaded){
26005             return;
26006         }
26007         
26008         var imageCanvas = document.createElement("canvas");
26009         
26010         var imageContext = imageCanvas.getContext("2d");
26011         
26012         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26013         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26014         
26015         var center = imageCanvas.width / 2;
26016         
26017         imageContext.translate(center, center);
26018         
26019         imageContext.rotate(this.rotate * Math.PI / 180);
26020         
26021         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26022         
26023         var canvas = document.createElement("canvas");
26024         
26025         var context = canvas.getContext("2d");
26026                 
26027         canvas.width = this.minWidth;
26028         canvas.height = this.minHeight;
26029
26030         switch (this.rotate) {
26031             case 0 :
26032                 
26033                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26034                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26035                 
26036                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26037                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26038                 
26039                 var targetWidth = this.minWidth - 2 * x;
26040                 var targetHeight = this.minHeight - 2 * y;
26041                 
26042                 var scale = 1;
26043                 
26044                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26045                     scale = targetWidth / width;
26046                 }
26047                 
26048                 if(x > 0 && y == 0){
26049                     scale = targetHeight / height;
26050                 }
26051                 
26052                 if(x > 0 && y > 0){
26053                     scale = targetWidth / width;
26054                     
26055                     if(width < height){
26056                         scale = targetHeight / height;
26057                     }
26058                 }
26059                 
26060                 context.scale(scale, scale);
26061                 
26062                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26063                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26064
26065                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26066                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26067
26068                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26069                 
26070                 break;
26071             case 90 : 
26072                 
26073                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26074                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26075                 
26076                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26077                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26078                 
26079                 var targetWidth = this.minWidth - 2 * x;
26080                 var targetHeight = this.minHeight - 2 * y;
26081                 
26082                 var scale = 1;
26083                 
26084                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26085                     scale = targetWidth / width;
26086                 }
26087                 
26088                 if(x > 0 && y == 0){
26089                     scale = targetHeight / height;
26090                 }
26091                 
26092                 if(x > 0 && y > 0){
26093                     scale = targetWidth / width;
26094                     
26095                     if(width < height){
26096                         scale = targetHeight / height;
26097                     }
26098                 }
26099                 
26100                 context.scale(scale, scale);
26101                 
26102                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26103                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26104
26105                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26106                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26107                 
26108                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26109                 
26110                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26111                 
26112                 break;
26113             case 180 :
26114                 
26115                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26116                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26117                 
26118                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26119                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26120                 
26121                 var targetWidth = this.minWidth - 2 * x;
26122                 var targetHeight = this.minHeight - 2 * y;
26123                 
26124                 var scale = 1;
26125                 
26126                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26127                     scale = targetWidth / width;
26128                 }
26129                 
26130                 if(x > 0 && y == 0){
26131                     scale = targetHeight / height;
26132                 }
26133                 
26134                 if(x > 0 && y > 0){
26135                     scale = targetWidth / width;
26136                     
26137                     if(width < height){
26138                         scale = targetHeight / height;
26139                     }
26140                 }
26141                 
26142                 context.scale(scale, scale);
26143                 
26144                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26145                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26146
26147                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26148                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26149
26150                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26151                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26152                 
26153                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26154                 
26155                 break;
26156             case 270 :
26157                 
26158                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26159                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26160                 
26161                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26162                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26163                 
26164                 var targetWidth = this.minWidth - 2 * x;
26165                 var targetHeight = this.minHeight - 2 * y;
26166                 
26167                 var scale = 1;
26168                 
26169                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26170                     scale = targetWidth / width;
26171                 }
26172                 
26173                 if(x > 0 && y == 0){
26174                     scale = targetHeight / height;
26175                 }
26176                 
26177                 if(x > 0 && y > 0){
26178                     scale = targetWidth / width;
26179                     
26180                     if(width < height){
26181                         scale = targetHeight / height;
26182                     }
26183                 }
26184                 
26185                 context.scale(scale, scale);
26186                 
26187                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26188                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26189
26190                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26191                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26192                 
26193                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26194                 
26195                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26196                 
26197                 break;
26198             default : 
26199                 break;
26200         }
26201         
26202         this.cropData = canvas.toDataURL(this.cropType);
26203         
26204         if(this.fireEvent('crop', this, this.cropData) !== false){
26205             this.process(this.file, this.cropData);
26206         }
26207         
26208         return;
26209         
26210     },
26211     
26212     setThumbBoxSize : function()
26213     {
26214         var width, height;
26215         
26216         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26217             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26218             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26219             
26220             this.minWidth = width;
26221             this.minHeight = height;
26222             
26223             if(this.rotate == 90 || this.rotate == 270){
26224                 this.minWidth = height;
26225                 this.minHeight = width;
26226             }
26227         }
26228         
26229         height = 300;
26230         width = Math.ceil(this.minWidth * height / this.minHeight);
26231         
26232         if(this.minWidth > this.minHeight){
26233             width = 300;
26234             height = Math.ceil(this.minHeight * width / this.minWidth);
26235         }
26236         
26237         this.thumbEl.setStyle({
26238             width : width + 'px',
26239             height : height + 'px'
26240         });
26241
26242         return;
26243             
26244     },
26245     
26246     setThumbBoxPosition : function()
26247     {
26248         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26249         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26250         
26251         this.thumbEl.setLeft(x);
26252         this.thumbEl.setTop(y);
26253         
26254     },
26255     
26256     baseRotateLevel : function()
26257     {
26258         this.baseRotate = 1;
26259         
26260         if(
26261                 typeof(this.exif) != 'undefined' &&
26262                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26263                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26264         ){
26265             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26266         }
26267         
26268         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26269         
26270     },
26271     
26272     baseScaleLevel : function()
26273     {
26274         var width, height;
26275         
26276         if(this.isDocument){
26277             
26278             if(this.baseRotate == 6 || this.baseRotate == 8){
26279             
26280                 height = this.thumbEl.getHeight();
26281                 this.baseScale = height / this.imageEl.OriginWidth;
26282
26283                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26284                     width = this.thumbEl.getWidth();
26285                     this.baseScale = width / this.imageEl.OriginHeight;
26286                 }
26287
26288                 return;
26289             }
26290
26291             height = this.thumbEl.getHeight();
26292             this.baseScale = height / this.imageEl.OriginHeight;
26293
26294             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26295                 width = this.thumbEl.getWidth();
26296                 this.baseScale = width / this.imageEl.OriginWidth;
26297             }
26298
26299             return;
26300         }
26301         
26302         if(this.baseRotate == 6 || this.baseRotate == 8){
26303             
26304             width = this.thumbEl.getHeight();
26305             this.baseScale = width / this.imageEl.OriginHeight;
26306             
26307             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26308                 height = this.thumbEl.getWidth();
26309                 this.baseScale = height / this.imageEl.OriginHeight;
26310             }
26311             
26312             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26313                 height = this.thumbEl.getWidth();
26314                 this.baseScale = height / this.imageEl.OriginHeight;
26315                 
26316                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26317                     width = this.thumbEl.getHeight();
26318                     this.baseScale = width / this.imageEl.OriginWidth;
26319                 }
26320             }
26321             
26322             return;
26323         }
26324         
26325         width = this.thumbEl.getWidth();
26326         this.baseScale = width / this.imageEl.OriginWidth;
26327         
26328         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26329             height = this.thumbEl.getHeight();
26330             this.baseScale = height / this.imageEl.OriginHeight;
26331         }
26332         
26333         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26334             
26335             height = this.thumbEl.getHeight();
26336             this.baseScale = height / this.imageEl.OriginHeight;
26337             
26338             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26339                 width = this.thumbEl.getWidth();
26340                 this.baseScale = width / this.imageEl.OriginWidth;
26341             }
26342             
26343         }
26344         
26345         return;
26346     },
26347     
26348     getScaleLevel : function()
26349     {
26350         return this.baseScale * Math.pow(1.1, this.scale);
26351     },
26352     
26353     onTouchStart : function(e)
26354     {
26355         if(!this.canvasLoaded){
26356             this.beforeSelectFile(e);
26357             return;
26358         }
26359         
26360         var touches = e.browserEvent.touches;
26361         
26362         if(!touches){
26363             return;
26364         }
26365         
26366         if(touches.length == 1){
26367             this.onMouseDown(e);
26368             return;
26369         }
26370         
26371         if(touches.length != 2){
26372             return;
26373         }
26374         
26375         var coords = [];
26376         
26377         for(var i = 0, finger; finger = touches[i]; i++){
26378             coords.push(finger.pageX, finger.pageY);
26379         }
26380         
26381         var x = Math.pow(coords[0] - coords[2], 2);
26382         var y = Math.pow(coords[1] - coords[3], 2);
26383         
26384         this.startDistance = Math.sqrt(x + y);
26385         
26386         this.startScale = this.scale;
26387         
26388         this.pinching = true;
26389         this.dragable = false;
26390         
26391     },
26392     
26393     onTouchMove : function(e)
26394     {
26395         if(!this.pinching && !this.dragable){
26396             return;
26397         }
26398         
26399         var touches = e.browserEvent.touches;
26400         
26401         if(!touches){
26402             return;
26403         }
26404         
26405         if(this.dragable){
26406             this.onMouseMove(e);
26407             return;
26408         }
26409         
26410         var coords = [];
26411         
26412         for(var i = 0, finger; finger = touches[i]; i++){
26413             coords.push(finger.pageX, finger.pageY);
26414         }
26415         
26416         var x = Math.pow(coords[0] - coords[2], 2);
26417         var y = Math.pow(coords[1] - coords[3], 2);
26418         
26419         this.endDistance = Math.sqrt(x + y);
26420         
26421         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26422         
26423         if(!this.zoomable()){
26424             this.scale = this.startScale;
26425             return;
26426         }
26427         
26428         this.draw();
26429         
26430     },
26431     
26432     onTouchEnd : function(e)
26433     {
26434         this.pinching = false;
26435         this.dragable = false;
26436         
26437     },
26438     
26439     process : function(file, crop)
26440     {
26441         if(this.loadMask){
26442             this.maskEl.mask(this.loadingText);
26443         }
26444         
26445         this.xhr = new XMLHttpRequest();
26446         
26447         file.xhr = this.xhr;
26448
26449         this.xhr.open(this.method, this.url, true);
26450         
26451         var headers = {
26452             "Accept": "application/json",
26453             "Cache-Control": "no-cache",
26454             "X-Requested-With": "XMLHttpRequest"
26455         };
26456         
26457         for (var headerName in headers) {
26458             var headerValue = headers[headerName];
26459             if (headerValue) {
26460                 this.xhr.setRequestHeader(headerName, headerValue);
26461             }
26462         }
26463         
26464         var _this = this;
26465         
26466         this.xhr.onload = function()
26467         {
26468             _this.xhrOnLoad(_this.xhr);
26469         }
26470         
26471         this.xhr.onerror = function()
26472         {
26473             _this.xhrOnError(_this.xhr);
26474         }
26475         
26476         var formData = new FormData();
26477
26478         formData.append('returnHTML', 'NO');
26479         
26480         if(crop){
26481             formData.append('crop', crop);
26482         }
26483         
26484         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26485             formData.append(this.paramName, file, file.name);
26486         }
26487         
26488         if(typeof(file.filename) != 'undefined'){
26489             formData.append('filename', file.filename);
26490         }
26491         
26492         if(typeof(file.mimetype) != 'undefined'){
26493             formData.append('mimetype', file.mimetype);
26494         }
26495         
26496         if(this.fireEvent('arrange', this, formData) != false){
26497             this.xhr.send(formData);
26498         };
26499     },
26500     
26501     xhrOnLoad : function(xhr)
26502     {
26503         if(this.loadMask){
26504             this.maskEl.unmask();
26505         }
26506         
26507         if (xhr.readyState !== 4) {
26508             this.fireEvent('exception', this, xhr);
26509             return;
26510         }
26511
26512         var response = Roo.decode(xhr.responseText);
26513         
26514         if(!response.success){
26515             this.fireEvent('exception', this, xhr);
26516             return;
26517         }
26518         
26519         var response = Roo.decode(xhr.responseText);
26520         
26521         this.fireEvent('upload', this, response);
26522         
26523     },
26524     
26525     xhrOnError : function()
26526     {
26527         if(this.loadMask){
26528             this.maskEl.unmask();
26529         }
26530         
26531         Roo.log('xhr on error');
26532         
26533         var response = Roo.decode(xhr.responseText);
26534           
26535         Roo.log(response);
26536         
26537     },
26538     
26539     prepare : function(file)
26540     {   
26541         if(this.loadMask){
26542             this.maskEl.mask(this.loadingText);
26543         }
26544         
26545         this.file = false;
26546         this.exif = {};
26547         
26548         if(typeof(file) === 'string'){
26549             this.loadCanvas(file);
26550             return;
26551         }
26552         
26553         if(!file || !this.urlAPI){
26554             return;
26555         }
26556         
26557         this.file = file;
26558         this.cropType = file.type;
26559         
26560         var _this = this;
26561         
26562         if(this.fireEvent('prepare', this, this.file) != false){
26563             
26564             var reader = new FileReader();
26565             
26566             reader.onload = function (e) {
26567                 if (e.target.error) {
26568                     Roo.log(e.target.error);
26569                     return;
26570                 }
26571                 
26572                 var buffer = e.target.result,
26573                     dataView = new DataView(buffer),
26574                     offset = 2,
26575                     maxOffset = dataView.byteLength - 4,
26576                     markerBytes,
26577                     markerLength;
26578                 
26579                 if (dataView.getUint16(0) === 0xffd8) {
26580                     while (offset < maxOffset) {
26581                         markerBytes = dataView.getUint16(offset);
26582                         
26583                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26584                             markerLength = dataView.getUint16(offset + 2) + 2;
26585                             if (offset + markerLength > dataView.byteLength) {
26586                                 Roo.log('Invalid meta data: Invalid segment size.');
26587                                 break;
26588                             }
26589                             
26590                             if(markerBytes == 0xffe1){
26591                                 _this.parseExifData(
26592                                     dataView,
26593                                     offset,
26594                                     markerLength
26595                                 );
26596                             }
26597                             
26598                             offset += markerLength;
26599                             
26600                             continue;
26601                         }
26602                         
26603                         break;
26604                     }
26605                     
26606                 }
26607                 
26608                 var url = _this.urlAPI.createObjectURL(_this.file);
26609                 
26610                 _this.loadCanvas(url);
26611                 
26612                 return;
26613             }
26614             
26615             reader.readAsArrayBuffer(this.file);
26616             
26617         }
26618         
26619     },
26620     
26621     parseExifData : function(dataView, offset, length)
26622     {
26623         var tiffOffset = offset + 10,
26624             littleEndian,
26625             dirOffset;
26626     
26627         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26628             // No Exif data, might be XMP data instead
26629             return;
26630         }
26631         
26632         // Check for the ASCII code for "Exif" (0x45786966):
26633         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26634             // No Exif data, might be XMP data instead
26635             return;
26636         }
26637         if (tiffOffset + 8 > dataView.byteLength) {
26638             Roo.log('Invalid Exif data: Invalid segment size.');
26639             return;
26640         }
26641         // Check for the two null bytes:
26642         if (dataView.getUint16(offset + 8) !== 0x0000) {
26643             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26644             return;
26645         }
26646         // Check the byte alignment:
26647         switch (dataView.getUint16(tiffOffset)) {
26648         case 0x4949:
26649             littleEndian = true;
26650             break;
26651         case 0x4D4D:
26652             littleEndian = false;
26653             break;
26654         default:
26655             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26656             return;
26657         }
26658         // Check for the TIFF tag marker (0x002A):
26659         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26660             Roo.log('Invalid Exif data: Missing TIFF marker.');
26661             return;
26662         }
26663         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26664         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26665         
26666         this.parseExifTags(
26667             dataView,
26668             tiffOffset,
26669             tiffOffset + dirOffset,
26670             littleEndian
26671         );
26672     },
26673     
26674     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26675     {
26676         var tagsNumber,
26677             dirEndOffset,
26678             i;
26679         if (dirOffset + 6 > dataView.byteLength) {
26680             Roo.log('Invalid Exif data: Invalid directory offset.');
26681             return;
26682         }
26683         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26684         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26685         if (dirEndOffset + 4 > dataView.byteLength) {
26686             Roo.log('Invalid Exif data: Invalid directory size.');
26687             return;
26688         }
26689         for (i = 0; i < tagsNumber; i += 1) {
26690             this.parseExifTag(
26691                 dataView,
26692                 tiffOffset,
26693                 dirOffset + 2 + 12 * i, // tag offset
26694                 littleEndian
26695             );
26696         }
26697         // Return the offset to the next directory:
26698         return dataView.getUint32(dirEndOffset, littleEndian);
26699     },
26700     
26701     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26702     {
26703         var tag = dataView.getUint16(offset, littleEndian);
26704         
26705         this.exif[tag] = this.getExifValue(
26706             dataView,
26707             tiffOffset,
26708             offset,
26709             dataView.getUint16(offset + 2, littleEndian), // tag type
26710             dataView.getUint32(offset + 4, littleEndian), // tag length
26711             littleEndian
26712         );
26713     },
26714     
26715     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26716     {
26717         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26718             tagSize,
26719             dataOffset,
26720             values,
26721             i,
26722             str,
26723             c;
26724     
26725         if (!tagType) {
26726             Roo.log('Invalid Exif data: Invalid tag type.');
26727             return;
26728         }
26729         
26730         tagSize = tagType.size * length;
26731         // Determine if the value is contained in the dataOffset bytes,
26732         // or if the value at the dataOffset is a pointer to the actual data:
26733         dataOffset = tagSize > 4 ?
26734                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26735         if (dataOffset + tagSize > dataView.byteLength) {
26736             Roo.log('Invalid Exif data: Invalid data offset.');
26737             return;
26738         }
26739         if (length === 1) {
26740             return tagType.getValue(dataView, dataOffset, littleEndian);
26741         }
26742         values = [];
26743         for (i = 0; i < length; i += 1) {
26744             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26745         }
26746         
26747         if (tagType.ascii) {
26748             str = '';
26749             // Concatenate the chars:
26750             for (i = 0; i < values.length; i += 1) {
26751                 c = values[i];
26752                 // Ignore the terminating NULL byte(s):
26753                 if (c === '\u0000') {
26754                     break;
26755                 }
26756                 str += c;
26757             }
26758             return str;
26759         }
26760         return values;
26761     }
26762     
26763 });
26764
26765 Roo.apply(Roo.bootstrap.UploadCropbox, {
26766     tags : {
26767         'Orientation': 0x0112
26768     },
26769     
26770     Orientation: {
26771             1: 0, //'top-left',
26772 //            2: 'top-right',
26773             3: 180, //'bottom-right',
26774 //            4: 'bottom-left',
26775 //            5: 'left-top',
26776             6: 90, //'right-top',
26777 //            7: 'right-bottom',
26778             8: 270 //'left-bottom'
26779     },
26780     
26781     exifTagTypes : {
26782         // byte, 8-bit unsigned int:
26783         1: {
26784             getValue: function (dataView, dataOffset) {
26785                 return dataView.getUint8(dataOffset);
26786             },
26787             size: 1
26788         },
26789         // ascii, 8-bit byte:
26790         2: {
26791             getValue: function (dataView, dataOffset) {
26792                 return String.fromCharCode(dataView.getUint8(dataOffset));
26793             },
26794             size: 1,
26795             ascii: true
26796         },
26797         // short, 16 bit int:
26798         3: {
26799             getValue: function (dataView, dataOffset, littleEndian) {
26800                 return dataView.getUint16(dataOffset, littleEndian);
26801             },
26802             size: 2
26803         },
26804         // long, 32 bit int:
26805         4: {
26806             getValue: function (dataView, dataOffset, littleEndian) {
26807                 return dataView.getUint32(dataOffset, littleEndian);
26808             },
26809             size: 4
26810         },
26811         // rational = two long values, first is numerator, second is denominator:
26812         5: {
26813             getValue: function (dataView, dataOffset, littleEndian) {
26814                 return dataView.getUint32(dataOffset, littleEndian) /
26815                     dataView.getUint32(dataOffset + 4, littleEndian);
26816             },
26817             size: 8
26818         },
26819         // slong, 32 bit signed int:
26820         9: {
26821             getValue: function (dataView, dataOffset, littleEndian) {
26822                 return dataView.getInt32(dataOffset, littleEndian);
26823             },
26824             size: 4
26825         },
26826         // srational, two slongs, first is numerator, second is denominator:
26827         10: {
26828             getValue: function (dataView, dataOffset, littleEndian) {
26829                 return dataView.getInt32(dataOffset, littleEndian) /
26830                     dataView.getInt32(dataOffset + 4, littleEndian);
26831             },
26832             size: 8
26833         }
26834     },
26835     
26836     footer : {
26837         STANDARD : [
26838             {
26839                 tag : 'div',
26840                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26841                 action : 'rotate-left',
26842                 cn : [
26843                     {
26844                         tag : 'button',
26845                         cls : 'btn btn-default',
26846                         html : '<i class="fa fa-undo"></i>'
26847                     }
26848                 ]
26849             },
26850             {
26851                 tag : 'div',
26852                 cls : 'btn-group roo-upload-cropbox-picture',
26853                 action : 'picture',
26854                 cn : [
26855                     {
26856                         tag : 'button',
26857                         cls : 'btn btn-default',
26858                         html : '<i class="fa fa-picture-o"></i>'
26859                     }
26860                 ]
26861             },
26862             {
26863                 tag : 'div',
26864                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26865                 action : 'rotate-right',
26866                 cn : [
26867                     {
26868                         tag : 'button',
26869                         cls : 'btn btn-default',
26870                         html : '<i class="fa fa-repeat"></i>'
26871                     }
26872                 ]
26873             }
26874         ],
26875         DOCUMENT : [
26876             {
26877                 tag : 'div',
26878                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26879                 action : 'rotate-left',
26880                 cn : [
26881                     {
26882                         tag : 'button',
26883                         cls : 'btn btn-default',
26884                         html : '<i class="fa fa-undo"></i>'
26885                     }
26886                 ]
26887             },
26888             {
26889                 tag : 'div',
26890                 cls : 'btn-group roo-upload-cropbox-download',
26891                 action : 'download',
26892                 cn : [
26893                     {
26894                         tag : 'button',
26895                         cls : 'btn btn-default',
26896                         html : '<i class="fa fa-download"></i>'
26897                     }
26898                 ]
26899             },
26900             {
26901                 tag : 'div',
26902                 cls : 'btn-group roo-upload-cropbox-crop',
26903                 action : 'crop',
26904                 cn : [
26905                     {
26906                         tag : 'button',
26907                         cls : 'btn btn-default',
26908                         html : '<i class="fa fa-crop"></i>'
26909                     }
26910                 ]
26911             },
26912             {
26913                 tag : 'div',
26914                 cls : 'btn-group roo-upload-cropbox-trash',
26915                 action : 'trash',
26916                 cn : [
26917                     {
26918                         tag : 'button',
26919                         cls : 'btn btn-default',
26920                         html : '<i class="fa fa-trash"></i>'
26921                     }
26922                 ]
26923             },
26924             {
26925                 tag : 'div',
26926                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26927                 action : 'rotate-right',
26928                 cn : [
26929                     {
26930                         tag : 'button',
26931                         cls : 'btn btn-default',
26932                         html : '<i class="fa fa-repeat"></i>'
26933                     }
26934                 ]
26935             }
26936         ],
26937         ROTATOR : [
26938             {
26939                 tag : 'div',
26940                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26941                 action : 'rotate-left',
26942                 cn : [
26943                     {
26944                         tag : 'button',
26945                         cls : 'btn btn-default',
26946                         html : '<i class="fa fa-undo"></i>'
26947                     }
26948                 ]
26949             },
26950             {
26951                 tag : 'div',
26952                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26953                 action : 'rotate-right',
26954                 cn : [
26955                     {
26956                         tag : 'button',
26957                         cls : 'btn btn-default',
26958                         html : '<i class="fa fa-repeat"></i>'
26959                     }
26960                 ]
26961             }
26962         ]
26963     }
26964 });
26965
26966 /*
26967 * Licence: LGPL
26968 */
26969
26970 /**
26971  * @class Roo.bootstrap.DocumentManager
26972  * @extends Roo.bootstrap.Component
26973  * Bootstrap DocumentManager class
26974  * @cfg {String} paramName default 'imageUpload'
26975  * @cfg {String} method default POST
26976  * @cfg {String} url action url
26977  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26978  * @cfg {Boolean} multiple multiple upload default true
26979  * @cfg {Number} thumbSize default 300
26980  * @cfg {String} fieldLabel
26981  * @cfg {Number} labelWidth default 4
26982  * @cfg {String} labelAlign (left|top) default left
26983  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26984  * 
26985  * @constructor
26986  * Create a new DocumentManager
26987  * @param {Object} config The config object
26988  */
26989
26990 Roo.bootstrap.DocumentManager = function(config){
26991     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26992     
26993     this.addEvents({
26994         /**
26995          * @event initial
26996          * Fire when initial the DocumentManager
26997          * @param {Roo.bootstrap.DocumentManager} this
26998          */
26999         "initial" : true,
27000         /**
27001          * @event inspect
27002          * inspect selected file
27003          * @param {Roo.bootstrap.DocumentManager} this
27004          * @param {File} file
27005          */
27006         "inspect" : true,
27007         /**
27008          * @event exception
27009          * Fire when xhr load exception
27010          * @param {Roo.bootstrap.DocumentManager} this
27011          * @param {XMLHttpRequest} xhr
27012          */
27013         "exception" : true,
27014         /**
27015          * @event prepare
27016          * prepare the form data
27017          * @param {Roo.bootstrap.DocumentManager} this
27018          * @param {Object} formData
27019          */
27020         "prepare" : true,
27021         /**
27022          * @event remove
27023          * Fire when remove the file
27024          * @param {Roo.bootstrap.DocumentManager} this
27025          * @param {Object} file
27026          */
27027         "remove" : true,
27028         /**
27029          * @event refresh
27030          * Fire after refresh the file
27031          * @param {Roo.bootstrap.DocumentManager} this
27032          */
27033         "refresh" : true,
27034         /**
27035          * @event click
27036          * Fire after click the image
27037          * @param {Roo.bootstrap.DocumentManager} this
27038          * @param {Object} file
27039          */
27040         "click" : true,
27041         /**
27042          * @event edit
27043          * Fire when upload a image and editable set to true
27044          * @param {Roo.bootstrap.DocumentManager} this
27045          * @param {Object} file
27046          */
27047         "edit" : true,
27048         /**
27049          * @event beforeselectfile
27050          * Fire before select file
27051          * @param {Roo.bootstrap.DocumentManager} this
27052          */
27053         "beforeselectfile" : true,
27054         /**
27055          * @event process
27056          * Fire before process file
27057          * @param {Roo.bootstrap.DocumentManager} this
27058          * @param {Object} file
27059          */
27060         "process" : true
27061         
27062     });
27063 };
27064
27065 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27066     
27067     boxes : 0,
27068     inputName : '',
27069     thumbSize : 300,
27070     multiple : true,
27071     files : [],
27072     method : 'POST',
27073     url : '',
27074     paramName : 'imageUpload',
27075     fieldLabel : '',
27076     labelWidth : 4,
27077     labelAlign : 'left',
27078     editable : true,
27079     delegates : [],
27080     
27081     
27082     xhr : false, 
27083     
27084     getAutoCreate : function()
27085     {   
27086         var managerWidget = {
27087             tag : 'div',
27088             cls : 'roo-document-manager',
27089             cn : [
27090                 {
27091                     tag : 'input',
27092                     cls : 'roo-document-manager-selector',
27093                     type : 'file'
27094                 },
27095                 {
27096                     tag : 'div',
27097                     cls : 'roo-document-manager-uploader',
27098                     cn : [
27099                         {
27100                             tag : 'div',
27101                             cls : 'roo-document-manager-upload-btn',
27102                             html : '<i class="fa fa-plus"></i>'
27103                         }
27104                     ]
27105                     
27106                 }
27107             ]
27108         };
27109         
27110         var content = [
27111             {
27112                 tag : 'div',
27113                 cls : 'column col-md-12',
27114                 cn : managerWidget
27115             }
27116         ];
27117         
27118         if(this.fieldLabel.length){
27119             
27120             content = [
27121                 {
27122                     tag : 'div',
27123                     cls : 'column col-md-12',
27124                     html : this.fieldLabel
27125                 },
27126                 {
27127                     tag : 'div',
27128                     cls : 'column col-md-12',
27129                     cn : managerWidget
27130                 }
27131             ];
27132
27133             if(this.labelAlign == 'left'){
27134                 content = [
27135                     {
27136                         tag : 'div',
27137                         cls : 'column col-md-' + this.labelWidth,
27138                         html : this.fieldLabel
27139                     },
27140                     {
27141                         tag : 'div',
27142                         cls : 'column col-md-' + (12 - this.labelWidth),
27143                         cn : managerWidget
27144                     }
27145                 ];
27146                 
27147             }
27148         }
27149         
27150         var cfg = {
27151             tag : 'div',
27152             cls : 'row clearfix',
27153             cn : content
27154         };
27155         
27156         return cfg;
27157         
27158     },
27159     
27160     initEvents : function()
27161     {
27162         this.managerEl = this.el.select('.roo-document-manager', true).first();
27163         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27164         
27165         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27166         this.selectorEl.hide();
27167         
27168         if(this.multiple){
27169             this.selectorEl.attr('multiple', 'multiple');
27170         }
27171         
27172         this.selectorEl.on('change', this.onFileSelected, this);
27173         
27174         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27175         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27176         
27177         this.uploader.on('click', this.onUploaderClick, this);
27178         
27179         this.renderProgressDialog();
27180         
27181         var _this = this;
27182         
27183         window.addEventListener("resize", function() { _this.refresh(); } );
27184         
27185         this.fireEvent('initial', this);
27186     },
27187     
27188     renderProgressDialog : function()
27189     {
27190         var _this = this;
27191         
27192         this.progressDialog = new Roo.bootstrap.Modal({
27193             cls : 'roo-document-manager-progress-dialog',
27194             allow_close : false,
27195             title : '',
27196             buttons : [
27197                 {
27198                     name  :'cancel',
27199                     weight : 'danger',
27200                     html : 'Cancel'
27201                 }
27202             ], 
27203             listeners : { 
27204                 btnclick : function() {
27205                     _this.uploadCancel();
27206                     this.hide();
27207                 }
27208             }
27209         });
27210          
27211         this.progressDialog.render(Roo.get(document.body));
27212          
27213         this.progress = new Roo.bootstrap.Progress({
27214             cls : 'roo-document-manager-progress',
27215             active : true,
27216             striped : true
27217         });
27218         
27219         this.progress.render(this.progressDialog.getChildContainer());
27220         
27221         this.progressBar = new Roo.bootstrap.ProgressBar({
27222             cls : 'roo-document-manager-progress-bar',
27223             aria_valuenow : 0,
27224             aria_valuemin : 0,
27225             aria_valuemax : 12,
27226             panel : 'success'
27227         });
27228         
27229         this.progressBar.render(this.progress.getChildContainer());
27230     },
27231     
27232     onUploaderClick : function(e)
27233     {
27234         e.preventDefault();
27235      
27236         if(this.fireEvent('beforeselectfile', this) != false){
27237             this.selectorEl.dom.click();
27238         }
27239         
27240     },
27241     
27242     onFileSelected : function(e)
27243     {
27244         e.preventDefault();
27245         
27246         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27247             return;
27248         }
27249         
27250         Roo.each(this.selectorEl.dom.files, function(file){
27251             if(this.fireEvent('inspect', this, file) != false){
27252                 this.files.push(file);
27253             }
27254         }, this);
27255         
27256         this.queue();
27257         
27258     },
27259     
27260     queue : function()
27261     {
27262         this.selectorEl.dom.value = '';
27263         
27264         if(!this.files.length){
27265             return;
27266         }
27267         
27268         if(this.boxes > 0 && this.files.length > this.boxes){
27269             this.files = this.files.slice(0, this.boxes);
27270         }
27271         
27272         this.uploader.show();
27273         
27274         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27275             this.uploader.hide();
27276         }
27277         
27278         var _this = this;
27279         
27280         var files = [];
27281         
27282         var docs = [];
27283         
27284         Roo.each(this.files, function(file){
27285             
27286             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27287                 var f = this.renderPreview(file);
27288                 files.push(f);
27289                 return;
27290             }
27291             
27292             if(file.type.indexOf('image') != -1){
27293                 this.delegates.push(
27294                     (function(){
27295                         _this.process(file);
27296                     }).createDelegate(this)
27297                 );
27298         
27299                 return;
27300             }
27301             
27302             docs.push(
27303                 (function(){
27304                     _this.process(file);
27305                 }).createDelegate(this)
27306             );
27307             
27308         }, this);
27309         
27310         this.files = files;
27311         
27312         this.delegates = this.delegates.concat(docs);
27313         
27314         if(!this.delegates.length){
27315             this.refresh();
27316             return;
27317         }
27318         
27319         this.progressBar.aria_valuemax = this.delegates.length;
27320         
27321         this.arrange();
27322         
27323         return;
27324     },
27325     
27326     arrange : function()
27327     {
27328         if(!this.delegates.length){
27329             this.progressDialog.hide();
27330             this.refresh();
27331             return;
27332         }
27333         
27334         var delegate = this.delegates.shift();
27335         
27336         this.progressDialog.show();
27337         
27338         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27339         
27340         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27341         
27342         delegate();
27343     },
27344     
27345     refresh : function()
27346     {
27347         this.uploader.show();
27348         
27349         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27350             this.uploader.hide();
27351         }
27352         
27353         Roo.isTouch ? this.closable(false) : this.closable(true);
27354         
27355         this.fireEvent('refresh', this);
27356     },
27357     
27358     onRemove : function(e, el, o)
27359     {
27360         e.preventDefault();
27361         
27362         this.fireEvent('remove', this, o);
27363         
27364     },
27365     
27366     remove : function(o)
27367     {
27368         var files = [];
27369         
27370         Roo.each(this.files, function(file){
27371             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27372                 files.push(file);
27373                 return;
27374             }
27375
27376             o.target.remove();
27377
27378         }, this);
27379         
27380         this.files = files;
27381         
27382         this.refresh();
27383     },
27384     
27385     clear : function()
27386     {
27387         Roo.each(this.files, function(file){
27388             if(!file.target){
27389                 return;
27390             }
27391             
27392             file.target.remove();
27393
27394         }, this);
27395         
27396         this.files = [];
27397         
27398         this.refresh();
27399     },
27400     
27401     onClick : function(e, el, o)
27402     {
27403         e.preventDefault();
27404         
27405         this.fireEvent('click', this, o);
27406         
27407     },
27408     
27409     closable : function(closable)
27410     {
27411         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27412             
27413             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27414             
27415             if(closable){
27416                 el.show();
27417                 return;
27418             }
27419             
27420             el.hide();
27421             
27422         }, this);
27423     },
27424     
27425     xhrOnLoad : function(xhr)
27426     {
27427         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27428             el.remove();
27429         }, this);
27430         
27431         if (xhr.readyState !== 4) {
27432             this.arrange();
27433             this.fireEvent('exception', this, xhr);
27434             return;
27435         }
27436
27437         var response = Roo.decode(xhr.responseText);
27438         
27439         if(!response.success){
27440             this.arrange();
27441             this.fireEvent('exception', this, xhr);
27442             return;
27443         }
27444         
27445         var file = this.renderPreview(response.data);
27446         
27447         this.files.push(file);
27448         
27449         this.arrange();
27450         
27451     },
27452     
27453     xhrOnError : function(xhr)
27454     {
27455         Roo.log('xhr on error');
27456         
27457         var response = Roo.decode(xhr.responseText);
27458           
27459         Roo.log(response);
27460         
27461         this.arrange();
27462     },
27463     
27464     process : function(file)
27465     {
27466         if(this.fireEvent('process', this, file) !== false){
27467             if(this.editable && file.type.indexOf('image') != -1){
27468                 this.fireEvent('edit', this, file);
27469                 return;
27470             }
27471
27472             this.uploadStart(file, false);
27473
27474             return;
27475         }
27476         
27477     },
27478     
27479     uploadStart : function(file, crop)
27480     {
27481         this.xhr = new XMLHttpRequest();
27482         
27483         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27484             this.arrange();
27485             return;
27486         }
27487         
27488         file.xhr = this.xhr;
27489             
27490         this.managerEl.createChild({
27491             tag : 'div',
27492             cls : 'roo-document-manager-loading',
27493             cn : [
27494                 {
27495                     tag : 'div',
27496                     tooltip : file.name,
27497                     cls : 'roo-document-manager-thumb',
27498                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27499                 }
27500             ]
27501
27502         });
27503
27504         this.xhr.open(this.method, this.url, true);
27505         
27506         var headers = {
27507             "Accept": "application/json",
27508             "Cache-Control": "no-cache",
27509             "X-Requested-With": "XMLHttpRequest"
27510         };
27511         
27512         for (var headerName in headers) {
27513             var headerValue = headers[headerName];
27514             if (headerValue) {
27515                 this.xhr.setRequestHeader(headerName, headerValue);
27516             }
27517         }
27518         
27519         var _this = this;
27520         
27521         this.xhr.onload = function()
27522         {
27523             _this.xhrOnLoad(_this.xhr);
27524         }
27525         
27526         this.xhr.onerror = function()
27527         {
27528             _this.xhrOnError(_this.xhr);
27529         }
27530         
27531         var formData = new FormData();
27532
27533         formData.append('returnHTML', 'NO');
27534         
27535         if(crop){
27536             formData.append('crop', crop);
27537         }
27538         
27539         formData.append(this.paramName, file, file.name);
27540         
27541         if(this.fireEvent('prepare', this, formData) != false){
27542             this.xhr.send(formData);
27543         };
27544     },
27545     
27546     uploadCancel : function()
27547     {
27548         if (this.xhr) {
27549             this.xhr.abort();
27550         }
27551         
27552         
27553         this.delegates = [];
27554         
27555         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27556             el.remove();
27557         }, this);
27558         
27559         this.arrange();
27560     },
27561     
27562     renderPreview : function(file)
27563     {
27564         if(typeof(file.target) != 'undefined' && file.target){
27565             return file;
27566         }
27567         
27568         var previewEl = this.managerEl.createChild({
27569             tag : 'div',
27570             cls : 'roo-document-manager-preview',
27571             cn : [
27572                 {
27573                     tag : 'div',
27574                     tooltip : file.filename,
27575                     cls : 'roo-document-manager-thumb',
27576                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27577                 },
27578                 {
27579                     tag : 'button',
27580                     cls : 'close',
27581                     html : '<i class="fa fa-times-circle"></i>'
27582                 }
27583             ]
27584         });
27585
27586         var close = previewEl.select('button.close', true).first();
27587
27588         close.on('click', this.onRemove, this, file);
27589
27590         file.target = previewEl;
27591
27592         var image = previewEl.select('img', true).first();
27593         
27594         var _this = this;
27595         
27596         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27597         
27598         image.on('click', this.onClick, this, file);
27599         
27600         return file;
27601         
27602     },
27603     
27604     onPreviewLoad : function(file, image)
27605     {
27606         if(typeof(file.target) == 'undefined' || !file.target){
27607             return;
27608         }
27609         
27610         var width = image.dom.naturalWidth || image.dom.width;
27611         var height = image.dom.naturalHeight || image.dom.height;
27612         
27613         if(width > height){
27614             file.target.addClass('wide');
27615             return;
27616         }
27617         
27618         file.target.addClass('tall');
27619         return;
27620         
27621     },
27622     
27623     uploadFromSource : function(file, crop)
27624     {
27625         this.xhr = new XMLHttpRequest();
27626         
27627         this.managerEl.createChild({
27628             tag : 'div',
27629             cls : 'roo-document-manager-loading',
27630             cn : [
27631                 {
27632                     tag : 'div',
27633                     tooltip : file.name,
27634                     cls : 'roo-document-manager-thumb',
27635                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27636                 }
27637             ]
27638
27639         });
27640
27641         this.xhr.open(this.method, this.url, true);
27642         
27643         var headers = {
27644             "Accept": "application/json",
27645             "Cache-Control": "no-cache",
27646             "X-Requested-With": "XMLHttpRequest"
27647         };
27648         
27649         for (var headerName in headers) {
27650             var headerValue = headers[headerName];
27651             if (headerValue) {
27652                 this.xhr.setRequestHeader(headerName, headerValue);
27653             }
27654         }
27655         
27656         var _this = this;
27657         
27658         this.xhr.onload = function()
27659         {
27660             _this.xhrOnLoad(_this.xhr);
27661         }
27662         
27663         this.xhr.onerror = function()
27664         {
27665             _this.xhrOnError(_this.xhr);
27666         }
27667         
27668         var formData = new FormData();
27669
27670         formData.append('returnHTML', 'NO');
27671         
27672         formData.append('crop', crop);
27673         
27674         if(typeof(file.filename) != 'undefined'){
27675             formData.append('filename', file.filename);
27676         }
27677         
27678         if(typeof(file.mimetype) != 'undefined'){
27679             formData.append('mimetype', file.mimetype);
27680         }
27681         
27682         if(this.fireEvent('prepare', this, formData) != false){
27683             this.xhr.send(formData);
27684         };
27685     }
27686 });
27687
27688 /*
27689 * Licence: LGPL
27690 */
27691
27692 /**
27693  * @class Roo.bootstrap.DocumentViewer
27694  * @extends Roo.bootstrap.Component
27695  * Bootstrap DocumentViewer class
27696  * 
27697  * @constructor
27698  * Create a new DocumentViewer
27699  * @param {Object} config The config object
27700  */
27701
27702 Roo.bootstrap.DocumentViewer = function(config){
27703     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27704     
27705     this.addEvents({
27706         /**
27707          * @event initial
27708          * Fire after initEvent
27709          * @param {Roo.bootstrap.DocumentViewer} this
27710          */
27711         "initial" : true,
27712         /**
27713          * @event click
27714          * Fire after click
27715          * @param {Roo.bootstrap.DocumentViewer} this
27716          */
27717         "click" : true,
27718         /**
27719          * @event trash
27720          * Fire after trash button
27721          * @param {Roo.bootstrap.DocumentViewer} this
27722          */
27723         "trash" : true
27724         
27725     });
27726 };
27727
27728 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27729     
27730     getAutoCreate : function()
27731     {
27732         var cfg = {
27733             tag : 'div',
27734             cls : 'roo-document-viewer',
27735             cn : [
27736                 {
27737                     tag : 'div',
27738                     cls : 'roo-document-viewer-body',
27739                     cn : [
27740                         {
27741                             tag : 'div',
27742                             cls : 'roo-document-viewer-thumb',
27743                             cn : [
27744                                 {
27745                                     tag : 'img',
27746                                     cls : 'roo-document-viewer-image'
27747                                 }
27748                             ]
27749                         }
27750                     ]
27751                 },
27752                 {
27753                     tag : 'div',
27754                     cls : 'roo-document-viewer-footer',
27755                     cn : {
27756                         tag : 'div',
27757                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27758                         cn : [
27759                             {
27760                                 tag : 'div',
27761                                 cls : 'btn-group',
27762                                 cn : [
27763                                     {
27764                                         tag : 'button',
27765                                         cls : 'btn btn-default roo-document-viewer-trash',
27766                                         html : '<i class="fa fa-trash"></i>'
27767                                     }
27768                                 ]
27769                             }
27770                         ]
27771                     }
27772                 }
27773             ]
27774         };
27775         
27776         return cfg;
27777     },
27778     
27779     initEvents : function()
27780     {
27781         
27782         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27783         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27784         
27785         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27786         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27787         
27788         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27789         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27790         
27791         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27792         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27793         
27794         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27795         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27796         
27797         this.bodyEl.on('click', this.onClick, this);
27798         
27799         this.trashBtn.on('click', this.onTrash, this);
27800         
27801     },
27802     
27803     initial : function()
27804     {
27805 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27806         
27807         
27808         this.fireEvent('initial', this);
27809         
27810     },
27811     
27812     onClick : function(e)
27813     {
27814         e.preventDefault();
27815         
27816         this.fireEvent('click', this);
27817     },
27818     
27819     onTrash : function(e)
27820     {
27821         e.preventDefault();
27822         
27823         this.fireEvent('trash', this);
27824     }
27825     
27826 });
27827 /*
27828  * - LGPL
27829  *
27830  * nav progress bar
27831  * 
27832  */
27833
27834 /**
27835  * @class Roo.bootstrap.NavProgressBar
27836  * @extends Roo.bootstrap.Component
27837  * Bootstrap NavProgressBar class
27838  * 
27839  * @constructor
27840  * Create a new nav progress bar
27841  * @param {Object} config The config object
27842  */
27843
27844 Roo.bootstrap.NavProgressBar = function(config){
27845     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27846
27847     this.bullets = this.bullets || [];
27848    
27849 //    Roo.bootstrap.NavProgressBar.register(this);
27850      this.addEvents({
27851         /**
27852              * @event changed
27853              * Fires when the active item changes
27854              * @param {Roo.bootstrap.NavProgressBar} this
27855              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27856              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27857          */
27858         'changed': true
27859      });
27860     
27861 };
27862
27863 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27864     
27865     bullets : [],
27866     barItems : [],
27867     
27868     getAutoCreate : function()
27869     {
27870         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27871         
27872         cfg = {
27873             tag : 'div',
27874             cls : 'roo-navigation-bar-group',
27875             cn : [
27876                 {
27877                     tag : 'div',
27878                     cls : 'roo-navigation-top-bar'
27879                 },
27880                 {
27881                     tag : 'div',
27882                     cls : 'roo-navigation-bullets-bar',
27883                     cn : [
27884                         {
27885                             tag : 'ul',
27886                             cls : 'roo-navigation-bar'
27887                         }
27888                     ]
27889                 },
27890                 
27891                 {
27892                     tag : 'div',
27893                     cls : 'roo-navigation-bottom-bar'
27894                 }
27895             ]
27896             
27897         };
27898         
27899         return cfg;
27900         
27901     },
27902     
27903     initEvents: function() 
27904     {
27905         
27906     },
27907     
27908     onRender : function(ct, position) 
27909     {
27910         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27911         
27912         if(this.bullets.length){
27913             Roo.each(this.bullets, function(b){
27914                this.addItem(b);
27915             }, this);
27916         }
27917         
27918         this.format();
27919         
27920     },
27921     
27922     addItem : function(cfg)
27923     {
27924         var item = new Roo.bootstrap.NavProgressItem(cfg);
27925         
27926         item.parentId = this.id;
27927         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27928         
27929         if(cfg.html){
27930             var top = new Roo.bootstrap.Element({
27931                 tag : 'div',
27932                 cls : 'roo-navigation-bar-text'
27933             });
27934             
27935             var bottom = new Roo.bootstrap.Element({
27936                 tag : 'div',
27937                 cls : 'roo-navigation-bar-text'
27938             });
27939             
27940             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27941             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27942             
27943             var topText = new Roo.bootstrap.Element({
27944                 tag : 'span',
27945                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27946             });
27947             
27948             var bottomText = new Roo.bootstrap.Element({
27949                 tag : 'span',
27950                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27951             });
27952             
27953             topText.onRender(top.el, null);
27954             bottomText.onRender(bottom.el, null);
27955             
27956             item.topEl = top;
27957             item.bottomEl = bottom;
27958         }
27959         
27960         this.barItems.push(item);
27961         
27962         return item;
27963     },
27964     
27965     getActive : function()
27966     {
27967         var active = false;
27968         
27969         Roo.each(this.barItems, function(v){
27970             
27971             if (!v.isActive()) {
27972                 return;
27973             }
27974             
27975             active = v;
27976             return false;
27977             
27978         });
27979         
27980         return active;
27981     },
27982     
27983     setActiveItem : function(item)
27984     {
27985         var prev = false;
27986         
27987         Roo.each(this.barItems, function(v){
27988             if (v.rid == item.rid) {
27989                 return ;
27990             }
27991             
27992             if (v.isActive()) {
27993                 v.setActive(false);
27994                 prev = v;
27995             }
27996         });
27997
27998         item.setActive(true);
27999         
28000         this.fireEvent('changed', this, item, prev);
28001     },
28002     
28003     getBarItem: function(rid)
28004     {
28005         var ret = false;
28006         
28007         Roo.each(this.barItems, function(e) {
28008             if (e.rid != rid) {
28009                 return;
28010             }
28011             
28012             ret =  e;
28013             return false;
28014         });
28015         
28016         return ret;
28017     },
28018     
28019     indexOfItem : function(item)
28020     {
28021         var index = false;
28022         
28023         Roo.each(this.barItems, function(v, i){
28024             
28025             if (v.rid != item.rid) {
28026                 return;
28027             }
28028             
28029             index = i;
28030             return false
28031         });
28032         
28033         return index;
28034     },
28035     
28036     setActiveNext : function()
28037     {
28038         var i = this.indexOfItem(this.getActive());
28039         
28040         if (i > this.barItems.length) {
28041             return;
28042         }
28043         
28044         this.setActiveItem(this.barItems[i+1]);
28045     },
28046     
28047     setActivePrev : function()
28048     {
28049         var i = this.indexOfItem(this.getActive());
28050         
28051         if (i  < 1) {
28052             return;
28053         }
28054         
28055         this.setActiveItem(this.barItems[i-1]);
28056     },
28057     
28058     format : function()
28059     {
28060         if(!this.barItems.length){
28061             return;
28062         }
28063      
28064         var width = 100 / this.barItems.length;
28065         
28066         Roo.each(this.barItems, function(i){
28067             i.el.setStyle('width', width + '%');
28068             i.topEl.el.setStyle('width', width + '%');
28069             i.bottomEl.el.setStyle('width', width + '%');
28070         }, this);
28071         
28072     }
28073     
28074 });
28075 /*
28076  * - LGPL
28077  *
28078  * Nav Progress Item
28079  * 
28080  */
28081
28082 /**
28083  * @class Roo.bootstrap.NavProgressItem
28084  * @extends Roo.bootstrap.Component
28085  * Bootstrap NavProgressItem class
28086  * @cfg {String} rid the reference id
28087  * @cfg {Boolean} active (true|false) Is item active default false
28088  * @cfg {Boolean} disabled (true|false) Is item active default false
28089  * @cfg {String} html
28090  * @cfg {String} position (top|bottom) text position default bottom
28091  * @cfg {String} icon show icon instead of number
28092  * 
28093  * @constructor
28094  * Create a new NavProgressItem
28095  * @param {Object} config The config object
28096  */
28097 Roo.bootstrap.NavProgressItem = function(config){
28098     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28099     this.addEvents({
28100         // raw events
28101         /**
28102          * @event click
28103          * The raw click event for the entire grid.
28104          * @param {Roo.bootstrap.NavProgressItem} this
28105          * @param {Roo.EventObject} e
28106          */
28107         "click" : true
28108     });
28109    
28110 };
28111
28112 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28113     
28114     rid : '',
28115     active : false,
28116     disabled : false,
28117     html : '',
28118     position : 'bottom',
28119     icon : false,
28120     
28121     getAutoCreate : function()
28122     {
28123         var iconCls = 'roo-navigation-bar-item-icon';
28124         
28125         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28126         
28127         var cfg = {
28128             tag: 'li',
28129             cls: 'roo-navigation-bar-item',
28130             cn : [
28131                 {
28132                     tag : 'i',
28133                     cls : iconCls
28134                 }
28135             ]
28136         };
28137         
28138         if(this.active){
28139             cfg.cls += ' active';
28140         }
28141         if(this.disabled){
28142             cfg.cls += ' disabled';
28143         }
28144         
28145         return cfg;
28146     },
28147     
28148     disable : function()
28149     {
28150         this.setDisabled(true);
28151     },
28152     
28153     enable : function()
28154     {
28155         this.setDisabled(false);
28156     },
28157     
28158     initEvents: function() 
28159     {
28160         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28161         
28162         this.iconEl.on('click', this.onClick, this);
28163     },
28164     
28165     onClick : function(e)
28166     {
28167         e.preventDefault();
28168         
28169         if(this.disabled){
28170             return;
28171         }
28172         
28173         if(this.fireEvent('click', this, e) === false){
28174             return;
28175         };
28176         
28177         this.parent().setActiveItem(this);
28178     },
28179     
28180     isActive: function () 
28181     {
28182         return this.active;
28183     },
28184     
28185     setActive : function(state)
28186     {
28187         if(this.active == state){
28188             return;
28189         }
28190         
28191         this.active = state;
28192         
28193         if (state) {
28194             this.el.addClass('active');
28195             return;
28196         }
28197         
28198         this.el.removeClass('active');
28199         
28200         return;
28201     },
28202     
28203     setDisabled : function(state)
28204     {
28205         if(this.disabled == state){
28206             return;
28207         }
28208         
28209         this.disabled = state;
28210         
28211         if (state) {
28212             this.el.addClass('disabled');
28213             return;
28214         }
28215         
28216         this.el.removeClass('disabled');
28217     },
28218     
28219     tooltipEl : function()
28220     {
28221         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28222     }
28223 });
28224  
28225
28226  /*
28227  * - LGPL
28228  *
28229  * FieldLabel
28230  * 
28231  */
28232
28233 /**
28234  * @class Roo.bootstrap.FieldLabel
28235  * @extends Roo.bootstrap.Component
28236  * Bootstrap FieldLabel class
28237  * @cfg {String} html contents of the element
28238  * @cfg {String} tag tag of the element default label
28239  * @cfg {String} cls class of the element
28240  * @cfg {String} target label target 
28241  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28242  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28243  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28244  * @cfg {String} iconTooltip default "This field is required"
28245  * 
28246  * @constructor
28247  * Create a new FieldLabel
28248  * @param {Object} config The config object
28249  */
28250
28251 Roo.bootstrap.FieldLabel = function(config){
28252     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28253     
28254     this.addEvents({
28255             /**
28256              * @event invalid
28257              * Fires after the field has been marked as invalid.
28258              * @param {Roo.form.FieldLabel} this
28259              * @param {String} msg The validation message
28260              */
28261             invalid : true,
28262             /**
28263              * @event valid
28264              * Fires after the field has been validated with no errors.
28265              * @param {Roo.form.FieldLabel} this
28266              */
28267             valid : true
28268         });
28269 };
28270
28271 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28272     
28273     tag: 'label',
28274     cls: '',
28275     html: '',
28276     target: '',
28277     allowBlank : true,
28278     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28279     validClass : 'text-success fa fa-lg fa-check',
28280     iconTooltip : 'This field is required',
28281     
28282     getAutoCreate : function(){
28283         
28284         var cfg = {
28285             tag : this.tag,
28286             cls : 'roo-bootstrap-field-label ' + this.cls,
28287             for : this.target,
28288             cn : [
28289                 {
28290                     tag : 'i',
28291                     cls : '',
28292                     tooltip : this.iconTooltip
28293                 },
28294                 {
28295                     tag : 'span',
28296                     html : this.html
28297                 }
28298             ] 
28299         };
28300         
28301         return cfg;
28302     },
28303     
28304     initEvents: function() 
28305     {
28306         Roo.bootstrap.Element.superclass.initEvents.call(this);
28307         
28308         this.iconEl = this.el.select('i', true).first();
28309         
28310         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28311         
28312         Roo.bootstrap.FieldLabel.register(this);
28313     },
28314     
28315     /**
28316      * Mark this field as valid
28317      */
28318     markValid : function()
28319     {
28320         this.iconEl.show();
28321         
28322         this.iconEl.removeClass(this.invalidClass);
28323         
28324         this.iconEl.addClass(this.validClass);
28325         
28326         this.fireEvent('valid', this);
28327     },
28328     
28329     /**
28330      * Mark this field as invalid
28331      * @param {String} msg The validation message
28332      */
28333     markInvalid : function(msg)
28334     {
28335         this.iconEl.show();
28336         
28337         this.iconEl.removeClass(this.validClass);
28338         
28339         this.iconEl.addClass(this.invalidClass);
28340         
28341         this.fireEvent('invalid', this, msg);
28342     }
28343     
28344    
28345 });
28346
28347 Roo.apply(Roo.bootstrap.FieldLabel, {
28348     
28349     groups: {},
28350     
28351      /**
28352     * register a FieldLabel Group
28353     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28354     */
28355     register : function(label)
28356     {
28357         if(this.groups.hasOwnProperty(label.target)){
28358             return;
28359         }
28360      
28361         this.groups[label.target] = label;
28362         
28363     },
28364     /**
28365     * fetch a FieldLabel Group based on the target
28366     * @param {string} target
28367     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28368     */
28369     get: function(target) {
28370         if (typeof(this.groups[target]) == 'undefined') {
28371             return false;
28372         }
28373         
28374         return this.groups[target] ;
28375     }
28376 });
28377
28378  
28379
28380  /*
28381  * - LGPL
28382  *
28383  * page DateSplitField.
28384  * 
28385  */
28386
28387
28388 /**
28389  * @class Roo.bootstrap.DateSplitField
28390  * @extends Roo.bootstrap.Component
28391  * Bootstrap DateSplitField class
28392  * @cfg {string} fieldLabel - the label associated
28393  * @cfg {Number} labelWidth set the width of label (0-12)
28394  * @cfg {String} labelAlign (top|left)
28395  * @cfg {Boolean} dayAllowBlank (true|false) default false
28396  * @cfg {Boolean} monthAllowBlank (true|false) default false
28397  * @cfg {Boolean} yearAllowBlank (true|false) default false
28398  * @cfg {string} dayPlaceholder 
28399  * @cfg {string} monthPlaceholder
28400  * @cfg {string} yearPlaceholder
28401  * @cfg {string} dayFormat default 'd'
28402  * @cfg {string} monthFormat default 'm'
28403  * @cfg {string} yearFormat default 'Y'
28404
28405  *     
28406  * @constructor
28407  * Create a new DateSplitField
28408  * @param {Object} config The config object
28409  */
28410
28411 Roo.bootstrap.DateSplitField = function(config){
28412     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28413     
28414     this.addEvents({
28415         // raw events
28416          /**
28417          * @event years
28418          * getting the data of years
28419          * @param {Roo.bootstrap.DateSplitField} this
28420          * @param {Object} years
28421          */
28422         "years" : true,
28423         /**
28424          * @event days
28425          * getting the data of days
28426          * @param {Roo.bootstrap.DateSplitField} this
28427          * @param {Object} days
28428          */
28429         "days" : true,
28430         /**
28431          * @event invalid
28432          * Fires after the field has been marked as invalid.
28433          * @param {Roo.form.Field} this
28434          * @param {String} msg The validation message
28435          */
28436         invalid : true,
28437        /**
28438          * @event valid
28439          * Fires after the field has been validated with no errors.
28440          * @param {Roo.form.Field} this
28441          */
28442         valid : true
28443     });
28444 };
28445
28446 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28447     
28448     fieldLabel : '',
28449     labelAlign : 'top',
28450     labelWidth : 3,
28451     dayAllowBlank : false,
28452     monthAllowBlank : false,
28453     yearAllowBlank : false,
28454     dayPlaceholder : '',
28455     monthPlaceholder : '',
28456     yearPlaceholder : '',
28457     dayFormat : 'd',
28458     monthFormat : 'm',
28459     yearFormat : 'Y',
28460     isFormField : true,
28461     
28462     getAutoCreate : function()
28463     {
28464         var cfg = {
28465             tag : 'div',
28466             cls : 'row roo-date-split-field-group',
28467             cn : [
28468                 {
28469                     tag : 'input',
28470                     type : 'hidden',
28471                     cls : 'form-hidden-field roo-date-split-field-group-value',
28472                     name : this.name
28473                 }
28474             ]
28475         };
28476         
28477         if(this.fieldLabel){
28478             cfg.cn.push({
28479                 tag : 'div',
28480                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28481                 cn : [
28482                     {
28483                         tag : 'label',
28484                         html : this.fieldLabel
28485                     }
28486                 ]
28487             });
28488         }
28489         
28490         Roo.each(['day', 'month', 'year'], function(t){
28491             cfg.cn.push({
28492                 tag : 'div',
28493                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28494             });
28495         }, this);
28496         
28497         return cfg;
28498     },
28499     
28500     inputEl: function ()
28501     {
28502         return this.el.select('.roo-date-split-field-group-value', true).first();
28503     },
28504     
28505     onRender : function(ct, position) 
28506     {
28507         var _this = this;
28508         
28509         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28510         
28511         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28512         
28513         this.dayField = new Roo.bootstrap.ComboBox({
28514             allowBlank : this.dayAllowBlank,
28515             alwaysQuery : true,
28516             displayField : 'value',
28517             editable : false,
28518             fieldLabel : '',
28519             forceSelection : true,
28520             mode : 'local',
28521             placeholder : this.dayPlaceholder,
28522             selectOnFocus : true,
28523             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28524             triggerAction : 'all',
28525             typeAhead : true,
28526             valueField : 'value',
28527             store : new Roo.data.SimpleStore({
28528                 data : (function() {    
28529                     var days = [];
28530                     _this.fireEvent('days', _this, days);
28531                     return days;
28532                 })(),
28533                 fields : [ 'value' ]
28534             }),
28535             listeners : {
28536                 select : function (_self, record, index)
28537                 {
28538                     _this.setValue(_this.getValue());
28539                 }
28540             }
28541         });
28542
28543         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28544         
28545         this.monthField = new Roo.bootstrap.MonthField({
28546             after : '<i class=\"fa fa-calendar\"></i>',
28547             allowBlank : this.monthAllowBlank,
28548             placeholder : this.monthPlaceholder,
28549             readOnly : true,
28550             listeners : {
28551                 render : function (_self)
28552                 {
28553                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28554                         e.preventDefault();
28555                         _self.focus();
28556                     });
28557                 },
28558                 select : function (_self, oldvalue, newvalue)
28559                 {
28560                     _this.setValue(_this.getValue());
28561                 }
28562             }
28563         });
28564         
28565         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28566         
28567         this.yearField = new Roo.bootstrap.ComboBox({
28568             allowBlank : this.yearAllowBlank,
28569             alwaysQuery : true,
28570             displayField : 'value',
28571             editable : false,
28572             fieldLabel : '',
28573             forceSelection : true,
28574             mode : 'local',
28575             placeholder : this.yearPlaceholder,
28576             selectOnFocus : true,
28577             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28578             triggerAction : 'all',
28579             typeAhead : true,
28580             valueField : 'value',
28581             store : new Roo.data.SimpleStore({
28582                 data : (function() {
28583                     var years = [];
28584                     _this.fireEvent('years', _this, years);
28585                     return years;
28586                 })(),
28587                 fields : [ 'value' ]
28588             }),
28589             listeners : {
28590                 select : function (_self, record, index)
28591                 {
28592                     _this.setValue(_this.getValue());
28593                 }
28594             }
28595         });
28596
28597         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28598     },
28599     
28600     setValue : function(v, format)
28601     {
28602         this.inputEl.dom.value = v;
28603         
28604         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28605         
28606         var d = Date.parseDate(v, f);
28607         
28608         if(!d){
28609             this.validate();
28610             return;
28611         }
28612         
28613         this.setDay(d.format(this.dayFormat));
28614         this.setMonth(d.format(this.monthFormat));
28615         this.setYear(d.format(this.yearFormat));
28616         
28617         this.validate();
28618         
28619         return;
28620     },
28621     
28622     setDay : function(v)
28623     {
28624         this.dayField.setValue(v);
28625         this.inputEl.dom.value = this.getValue();
28626         this.validate();
28627         return;
28628     },
28629     
28630     setMonth : function(v)
28631     {
28632         this.monthField.setValue(v, true);
28633         this.inputEl.dom.value = this.getValue();
28634         this.validate();
28635         return;
28636     },
28637     
28638     setYear : function(v)
28639     {
28640         this.yearField.setValue(v);
28641         this.inputEl.dom.value = this.getValue();
28642         this.validate();
28643         return;
28644     },
28645     
28646     getDay : function()
28647     {
28648         return this.dayField.getValue();
28649     },
28650     
28651     getMonth : function()
28652     {
28653         return this.monthField.getValue();
28654     },
28655     
28656     getYear : function()
28657     {
28658         return this.yearField.getValue();
28659     },
28660     
28661     getValue : function()
28662     {
28663         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28664         
28665         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28666         
28667         return date;
28668     },
28669     
28670     reset : function()
28671     {
28672         this.setDay('');
28673         this.setMonth('');
28674         this.setYear('');
28675         this.inputEl.dom.value = '';
28676         this.validate();
28677         return;
28678     },
28679     
28680     validate : function()
28681     {
28682         var d = this.dayField.validate();
28683         var m = this.monthField.validate();
28684         var y = this.yearField.validate();
28685         
28686         var valid = true;
28687         
28688         if(
28689                 (!this.dayAllowBlank && !d) ||
28690                 (!this.monthAllowBlank && !m) ||
28691                 (!this.yearAllowBlank && !y)
28692         ){
28693             valid = false;
28694         }
28695         
28696         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28697             return valid;
28698         }
28699         
28700         if(valid){
28701             this.markValid();
28702             return valid;
28703         }
28704         
28705         this.markInvalid();
28706         
28707         return valid;
28708     },
28709     
28710     markValid : function()
28711     {
28712         
28713         var label = this.el.select('label', true).first();
28714         var icon = this.el.select('i.fa-star', true).first();
28715
28716         if(label && icon){
28717             icon.remove();
28718         }
28719         
28720         this.fireEvent('valid', this);
28721     },
28722     
28723      /**
28724      * Mark this field as invalid
28725      * @param {String} msg The validation message
28726      */
28727     markInvalid : function(msg)
28728     {
28729         
28730         var label = this.el.select('label', true).first();
28731         var icon = this.el.select('i.fa-star', true).first();
28732
28733         if(label && !icon){
28734             this.el.select('.roo-date-split-field-label', true).createChild({
28735                 tag : 'i',
28736                 cls : 'text-danger fa fa-lg fa-star',
28737                 tooltip : 'This field is required',
28738                 style : 'margin-right:5px;'
28739             }, label, true);
28740         }
28741         
28742         this.fireEvent('invalid', this, msg);
28743     },
28744     
28745     clearInvalid : function()
28746     {
28747         var label = this.el.select('label', true).first();
28748         var icon = this.el.select('i.fa-star', true).first();
28749
28750         if(label && icon){
28751             icon.remove();
28752         }
28753         
28754         this.fireEvent('valid', this);
28755     },
28756     
28757     getName: function()
28758     {
28759         return this.name;
28760     }
28761     
28762 });
28763
28764  /**
28765  *
28766  * This is based on 
28767  * http://masonry.desandro.com
28768  *
28769  * The idea is to render all the bricks based on vertical width...
28770  *
28771  * The original code extends 'outlayer' - we might need to use that....
28772  * 
28773  */
28774
28775
28776 /**
28777  * @class Roo.bootstrap.LayoutMasonry
28778  * @extends Roo.bootstrap.Component
28779  * Bootstrap Layout Masonry class
28780  * 
28781  * @constructor
28782  * Create a new Element
28783  * @param {Object} config The config object
28784  */
28785
28786 Roo.bootstrap.LayoutMasonry = function(config){
28787     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28788     
28789     this.bricks = [];
28790     
28791 };
28792
28793 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28794     
28795     /**
28796      * @cfg {Boolean} isLayoutInstant = no animation?
28797      */   
28798     isLayoutInstant : false, // needed?
28799    
28800     /**
28801      * @cfg {Number} boxWidth  width of the columns
28802      */   
28803     boxWidth : 450,
28804     
28805       /**
28806      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28807      */   
28808     boxHeight : 0,
28809     
28810     /**
28811      * @cfg {Number} padWidth padding below box..
28812      */   
28813     padWidth : 10, 
28814     
28815     /**
28816      * @cfg {Number} gutter gutter width..
28817      */   
28818     gutter : 10,
28819     
28820      /**
28821      * @cfg {Number} maxCols maximum number of columns
28822      */   
28823     
28824     maxCols: 0,
28825     
28826     /**
28827      * @cfg {Boolean} isAutoInitial defalut true
28828      */   
28829     isAutoInitial : true, 
28830     
28831     containerWidth: 0,
28832     
28833     /**
28834      * @cfg {Boolean} isHorizontal defalut false
28835      */   
28836     isHorizontal : false, 
28837
28838     currentSize : null,
28839     
28840     tag: 'div',
28841     
28842     cls: '',
28843     
28844     bricks: null, //CompositeElement
28845     
28846     cols : 1,
28847     
28848     _isLayoutInited : false,
28849     
28850 //    isAlternative : false, // only use for vertical layout...
28851     
28852     /**
28853      * @cfg {Number} alternativePadWidth padding below box..
28854      */   
28855     alternativePadWidth : 50, 
28856     
28857     getAutoCreate : function(){
28858         
28859         var cfg = {
28860             tag: this.tag,
28861             cls: 'blog-masonary-wrapper ' + this.cls,
28862             cn : {
28863                 cls : 'mas-boxes masonary'
28864             }
28865         };
28866         
28867         return cfg;
28868     },
28869     
28870     getChildContainer: function( )
28871     {
28872         if (this.boxesEl) {
28873             return this.boxesEl;
28874         }
28875         
28876         this.boxesEl = this.el.select('.mas-boxes').first();
28877         
28878         return this.boxesEl;
28879     },
28880     
28881     
28882     initEvents : function()
28883     {
28884         var _this = this;
28885         
28886         if(this.isAutoInitial){
28887             Roo.log('hook children rendered');
28888             this.on('childrenrendered', function() {
28889                 Roo.log('children rendered');
28890                 _this.initial();
28891             } ,this);
28892         }
28893     },
28894     
28895     initial : function()
28896     {
28897         this.currentSize = this.el.getBox(true);
28898         
28899         Roo.EventManager.onWindowResize(this.resize, this); 
28900
28901         if(!this.isAutoInitial){
28902             this.layout();
28903             return;
28904         }
28905         
28906         this.layout();
28907         
28908         return;
28909         //this.layout.defer(500,this);
28910         
28911     },
28912     
28913     resize : function()
28914     {
28915         Roo.log('resize');
28916         
28917         var cs = this.el.getBox(true);
28918         
28919         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28920             Roo.log("no change in with or X");
28921             return;
28922         }
28923         
28924         this.currentSize = cs;
28925         
28926         this.layout();
28927         
28928     },
28929     
28930     layout : function()
28931     {   
28932         this._resetLayout();
28933         
28934         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28935         
28936         this.layoutItems( isInstant );
28937       
28938         this._isLayoutInited = true;
28939         
28940     },
28941     
28942     _resetLayout : function()
28943     {
28944         if(this.isHorizontal){
28945             this.horizontalMeasureColumns();
28946             return;
28947         }
28948         
28949         this.verticalMeasureColumns();
28950         
28951     },
28952     
28953     verticalMeasureColumns : function()
28954     {
28955         this.getContainerWidth();
28956         
28957 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28958 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28959 //            return;
28960 //        }
28961         
28962         var boxWidth = this.boxWidth + this.padWidth;
28963         
28964         if(this.containerWidth < this.boxWidth){
28965             boxWidth = this.containerWidth
28966         }
28967         
28968         var containerWidth = this.containerWidth;
28969         
28970         var cols = Math.floor(containerWidth / boxWidth);
28971         
28972         this.cols = Math.max( cols, 1 );
28973         
28974         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28975         
28976         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28977         
28978         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28979         
28980         this.colWidth = boxWidth + avail - this.padWidth;
28981         
28982         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28983         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28984     },
28985     
28986     horizontalMeasureColumns : function()
28987     {
28988         this.getContainerWidth();
28989         
28990         var boxWidth = this.boxWidth;
28991         
28992         if(this.containerWidth < boxWidth){
28993             boxWidth = this.containerWidth;
28994         }
28995         
28996         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28997         
28998         this.el.setHeight(boxWidth);
28999         
29000     },
29001     
29002     getContainerWidth : function()
29003     {
29004         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29005     },
29006     
29007     layoutItems : function( isInstant )
29008     {
29009         var items = Roo.apply([], this.bricks);
29010         
29011         if(this.isHorizontal){
29012             this._horizontalLayoutItems( items , isInstant );
29013             return;
29014         }
29015         
29016 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29017 //            this._verticalAlternativeLayoutItems( items , isInstant );
29018 //            return;
29019 //        }
29020         
29021         this._verticalLayoutItems( items , isInstant );
29022         
29023     },
29024     
29025     _verticalLayoutItems : function ( items , isInstant)
29026     {
29027         if ( !items || !items.length ) {
29028             return;
29029         }
29030         
29031         var standard = [
29032             ['xs', 'xs', 'xs', 'tall'],
29033             ['xs', 'xs', 'tall'],
29034             ['xs', 'xs', 'sm'],
29035             ['xs', 'xs', 'xs'],
29036             ['xs', 'tall'],
29037             ['xs', 'sm'],
29038             ['xs', 'xs'],
29039             ['xs'],
29040             
29041             ['sm', 'xs', 'xs'],
29042             ['sm', 'xs'],
29043             ['sm'],
29044             
29045             ['tall', 'xs', 'xs', 'xs'],
29046             ['tall', 'xs', 'xs'],
29047             ['tall', 'xs'],
29048             ['tall']
29049             
29050         ];
29051         
29052         var queue = [];
29053         
29054         var boxes = [];
29055         
29056         var box = [];
29057         
29058         Roo.each(items, function(item, k){
29059             
29060             switch (item.size) {
29061                 // these layouts take up a full box,
29062                 case 'md' :
29063                 case 'md-left' :
29064                 case 'md-right' :
29065                 case 'wide' :
29066                     
29067                     if(box.length){
29068                         boxes.push(box);
29069                         box = [];
29070                     }
29071                     
29072                     boxes.push([item]);
29073                     
29074                     break;
29075                     
29076                 case 'xs' :
29077                 case 'sm' :
29078                 case 'tall' :
29079                     
29080                     box.push(item);
29081                     
29082                     break;
29083                 default :
29084                     break;
29085                     
29086             }
29087             
29088         }, this);
29089         
29090         if(box.length){
29091             boxes.push(box);
29092             box = [];
29093         }
29094         
29095         var filterPattern = function(box, length)
29096         {
29097             if(!box.length){
29098                 return;
29099             }
29100             
29101             var match = false;
29102             
29103             var pattern = box.slice(0, length);
29104             
29105             var format = [];
29106             
29107             Roo.each(pattern, function(i){
29108                 format.push(i.size);
29109             }, this);
29110             
29111             Roo.each(standard, function(s){
29112                 
29113                 if(String(s) != String(format)){
29114                     return;
29115                 }
29116                 
29117                 match = true;
29118                 return false;
29119                 
29120             }, this);
29121             
29122             if(!match && length == 1){
29123                 return;
29124             }
29125             
29126             if(!match){
29127                 filterPattern(box, length - 1);
29128                 return;
29129             }
29130                 
29131             queue.push(pattern);
29132
29133             box = box.slice(length, box.length);
29134
29135             filterPattern(box, 4);
29136
29137             return;
29138             
29139         }
29140         
29141         Roo.each(boxes, function(box, k){
29142             
29143             if(!box.length){
29144                 return;
29145             }
29146             
29147             if(box.length == 1){
29148                 queue.push(box);
29149                 return;
29150             }
29151             
29152             filterPattern(box, 4);
29153             
29154         }, this);
29155         
29156         this._processVerticalLayoutQueue( queue, isInstant );
29157         
29158     },
29159     
29160 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29161 //    {
29162 //        if ( !items || !items.length ) {
29163 //            return;
29164 //        }
29165 //
29166 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29167 //        
29168 //    },
29169     
29170     _horizontalLayoutItems : function ( items , isInstant)
29171     {
29172         if ( !items || !items.length || items.length < 3) {
29173             return;
29174         }
29175         
29176         items.reverse();
29177         
29178         var eItems = items.slice(0, 3);
29179         
29180         items = items.slice(3, items.length);
29181         
29182         var standard = [
29183             ['xs', 'xs', 'xs', 'wide'],
29184             ['xs', 'xs', 'wide'],
29185             ['xs', 'xs', 'sm'],
29186             ['xs', 'xs', 'xs'],
29187             ['xs', 'wide'],
29188             ['xs', 'sm'],
29189             ['xs', 'xs'],
29190             ['xs'],
29191             
29192             ['sm', 'xs', 'xs'],
29193             ['sm', 'xs'],
29194             ['sm'],
29195             
29196             ['wide', 'xs', 'xs', 'xs'],
29197             ['wide', 'xs', 'xs'],
29198             ['wide', 'xs'],
29199             ['wide'],
29200             
29201             ['wide-thin']
29202         ];
29203         
29204         var queue = [];
29205         
29206         var boxes = [];
29207         
29208         var box = [];
29209         
29210         Roo.each(items, function(item, k){
29211             
29212             switch (item.size) {
29213                 case 'md' :
29214                 case 'md-left' :
29215                 case 'md-right' :
29216                 case 'tall' :
29217                     
29218                     if(box.length){
29219                         boxes.push(box);
29220                         box = [];
29221                     }
29222                     
29223                     boxes.push([item]);
29224                     
29225                     break;
29226                     
29227                 case 'xs' :
29228                 case 'sm' :
29229                 case 'wide' :
29230                 case 'wide-thin' :
29231                     
29232                     box.push(item);
29233                     
29234                     break;
29235                 default :
29236                     break;
29237                     
29238             }
29239             
29240         }, this);
29241         
29242         if(box.length){
29243             boxes.push(box);
29244             box = [];
29245         }
29246         
29247         var filterPattern = function(box, length)
29248         {
29249             if(!box.length){
29250                 return;
29251             }
29252             
29253             var match = false;
29254             
29255             var pattern = box.slice(0, length);
29256             
29257             var format = [];
29258             
29259             Roo.each(pattern, function(i){
29260                 format.push(i.size);
29261             }, this);
29262             
29263             Roo.each(standard, function(s){
29264                 
29265                 if(String(s) != String(format)){
29266                     return;
29267                 }
29268                 
29269                 match = true;
29270                 return false;
29271                 
29272             }, this);
29273             
29274             if(!match && length == 1){
29275                 return;
29276             }
29277             
29278             if(!match){
29279                 filterPattern(box, length - 1);
29280                 return;
29281             }
29282                 
29283             queue.push(pattern);
29284
29285             box = box.slice(length, box.length);
29286
29287             filterPattern(box, 4);
29288
29289             return;
29290             
29291         }
29292         
29293         Roo.each(boxes, function(box, k){
29294             
29295             if(!box.length){
29296                 return;
29297             }
29298             
29299             if(box.length == 1){
29300                 queue.push(box);
29301                 return;
29302             }
29303             
29304             filterPattern(box, 4);
29305             
29306         }, this);
29307         
29308         
29309         var prune = [];
29310         
29311         var pos = this.el.getBox(true);
29312         
29313         var minX = pos.x;
29314         
29315         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29316         
29317         var hit_end = false;
29318         
29319         Roo.each(queue, function(box){
29320             
29321             if(hit_end){
29322                 
29323                 Roo.each(box, function(b){
29324                 
29325                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29326                     b.el.hide();
29327
29328                 }, this);
29329
29330                 return;
29331             }
29332             
29333             var mx = 0;
29334             
29335             Roo.each(box, function(b){
29336                 
29337                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29338                 b.el.show();
29339
29340                 mx = Math.max(mx, b.x);
29341                 
29342             }, this);
29343             
29344             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29345             
29346             if(maxX < minX){
29347                 
29348                 Roo.each(box, function(b){
29349                 
29350                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29351                     b.el.hide();
29352                     
29353                 }, this);
29354                 
29355                 hit_end = true;
29356                 
29357                 return;
29358             }
29359             
29360             prune.push(box);
29361             
29362         }, this);
29363         
29364         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29365     },
29366     
29367     /** Sets position of item in DOM
29368     * @param {Element} item
29369     * @param {Number} x - horizontal position
29370     * @param {Number} y - vertical position
29371     * @param {Boolean} isInstant - disables transitions
29372     */
29373     _processVerticalLayoutQueue : function( queue, isInstant )
29374     {
29375         var pos = this.el.getBox(true);
29376         var x = pos.x;
29377         var y = pos.y;
29378         var maxY = [];
29379         
29380         for (var i = 0; i < this.cols; i++){
29381             maxY[i] = pos.y;
29382         }
29383         
29384         Roo.each(queue, function(box, k){
29385             
29386             var col = k % this.cols;
29387             
29388             Roo.each(box, function(b,kk){
29389                 
29390                 b.el.position('absolute');
29391                 
29392                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29393                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29394                 
29395                 if(b.size == 'md-left' || b.size == 'md-right'){
29396                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29397                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29398                 }
29399                 
29400                 b.el.setWidth(width);
29401                 b.el.setHeight(height);
29402                 // iframe?
29403                 b.el.select('iframe',true).setSize(width,height);
29404                 
29405             }, this);
29406             
29407             for (var i = 0; i < this.cols; i++){
29408                 
29409                 if(maxY[i] < maxY[col]){
29410                     col = i;
29411                     continue;
29412                 }
29413                 
29414                 col = Math.min(col, i);
29415                 
29416             }
29417             
29418             x = pos.x + col * (this.colWidth + this.padWidth);
29419             
29420             y = maxY[col];
29421             
29422             var positions = [];
29423             
29424             switch (box.length){
29425                 case 1 :
29426                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29427                     break;
29428                 case 2 :
29429                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29430                     break;
29431                 case 3 :
29432                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29433                     break;
29434                 case 4 :
29435                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29436                     break;
29437                 default :
29438                     break;
29439             }
29440             
29441             Roo.each(box, function(b,kk){
29442                 
29443                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29444                 
29445                 var sz = b.el.getSize();
29446                 
29447                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29448                 
29449             }, this);
29450             
29451         }, this);
29452         
29453         var mY = 0;
29454         
29455         for (var i = 0; i < this.cols; i++){
29456             mY = Math.max(mY, maxY[i]);
29457         }
29458         
29459         this.el.setHeight(mY - pos.y);
29460         
29461     },
29462     
29463 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29464 //    {
29465 //        var pos = this.el.getBox(true);
29466 //        var x = pos.x;
29467 //        var y = pos.y;
29468 //        var maxX = pos.right;
29469 //        
29470 //        var maxHeight = 0;
29471 //        
29472 //        Roo.each(items, function(item, k){
29473 //            
29474 //            var c = k % 2;
29475 //            
29476 //            item.el.position('absolute');
29477 //                
29478 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29479 //
29480 //            item.el.setWidth(width);
29481 //
29482 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29483 //
29484 //            item.el.setHeight(height);
29485 //            
29486 //            if(c == 0){
29487 //                item.el.setXY([x, y], isInstant ? false : true);
29488 //            } else {
29489 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29490 //            }
29491 //            
29492 //            y = y + height + this.alternativePadWidth;
29493 //            
29494 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29495 //            
29496 //        }, this);
29497 //        
29498 //        this.el.setHeight(maxHeight);
29499 //        
29500 //    },
29501     
29502     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29503     {
29504         var pos = this.el.getBox(true);
29505         
29506         var minX = pos.x;
29507         var minY = pos.y;
29508         
29509         var maxX = pos.right;
29510         
29511         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29512         
29513         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29514         
29515         Roo.each(queue, function(box, k){
29516             
29517             Roo.each(box, function(b, kk){
29518                 
29519                 b.el.position('absolute');
29520                 
29521                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29522                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29523                 
29524                 if(b.size == 'md-left' || b.size == 'md-right'){
29525                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29526                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29527                 }
29528                 
29529                 b.el.setWidth(width);
29530                 b.el.setHeight(height);
29531                 
29532             }, this);
29533             
29534             if(!box.length){
29535                 return;
29536             }
29537             
29538             var positions = [];
29539             
29540             switch (box.length){
29541                 case 1 :
29542                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29543                     break;
29544                 case 2 :
29545                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29546                     break;
29547                 case 3 :
29548                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29549                     break;
29550                 case 4 :
29551                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29552                     break;
29553                 default :
29554                     break;
29555             }
29556             
29557             Roo.each(box, function(b,kk){
29558                 
29559                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29560                 
29561                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29562                 
29563             }, this);
29564             
29565         }, this);
29566         
29567     },
29568     
29569     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29570     {
29571         Roo.each(eItems, function(b,k){
29572             
29573             b.size = (k == 0) ? 'sm' : 'xs';
29574             b.x = (k == 0) ? 2 : 1;
29575             b.y = (k == 0) ? 2 : 1;
29576             
29577             b.el.position('absolute');
29578             
29579             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29580                 
29581             b.el.setWidth(width);
29582             
29583             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29584             
29585             b.el.setHeight(height);
29586             
29587         }, this);
29588
29589         var positions = [];
29590         
29591         positions.push({
29592             x : maxX - this.unitWidth * 2 - this.gutter,
29593             y : minY
29594         });
29595         
29596         positions.push({
29597             x : maxX - this.unitWidth,
29598             y : minY + (this.unitWidth + this.gutter) * 2
29599         });
29600         
29601         positions.push({
29602             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29603             y : minY
29604         });
29605         
29606         Roo.each(eItems, function(b,k){
29607             
29608             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29609
29610         }, this);
29611         
29612     },
29613     
29614     getVerticalOneBoxColPositions : function(x, y, box)
29615     {
29616         var pos = [];
29617         
29618         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29619         
29620         if(box[0].size == 'md-left'){
29621             rand = 0;
29622         }
29623         
29624         if(box[0].size == 'md-right'){
29625             rand = 1;
29626         }
29627         
29628         pos.push({
29629             x : x + (this.unitWidth + this.gutter) * rand,
29630             y : y
29631         });
29632         
29633         return pos;
29634     },
29635     
29636     getVerticalTwoBoxColPositions : function(x, y, box)
29637     {
29638         var pos = [];
29639         
29640         if(box[0].size == 'xs'){
29641             
29642             pos.push({
29643                 x : x,
29644                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29645             });
29646
29647             pos.push({
29648                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29649                 y : y
29650             });
29651             
29652             return pos;
29653             
29654         }
29655         
29656         pos.push({
29657             x : x,
29658             y : y
29659         });
29660
29661         pos.push({
29662             x : x + (this.unitWidth + this.gutter) * 2,
29663             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29664         });
29665         
29666         return pos;
29667         
29668     },
29669     
29670     getVerticalThreeBoxColPositions : function(x, y, box)
29671     {
29672         var pos = [];
29673         
29674         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29675             
29676             pos.push({
29677                 x : x,
29678                 y : y
29679             });
29680
29681             pos.push({
29682                 x : x + (this.unitWidth + this.gutter) * 1,
29683                 y : y
29684             });
29685             
29686             pos.push({
29687                 x : x + (this.unitWidth + this.gutter) * 2,
29688                 y : y
29689             });
29690             
29691             return pos;
29692             
29693         }
29694         
29695         if(box[0].size == 'xs' && box[1].size == 'xs'){
29696             
29697             pos.push({
29698                 x : x,
29699                 y : y
29700             });
29701
29702             pos.push({
29703                 x : x,
29704                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29705             });
29706             
29707             pos.push({
29708                 x : x + (this.unitWidth + this.gutter) * 1,
29709                 y : y
29710             });
29711             
29712             return pos;
29713             
29714         }
29715         
29716         pos.push({
29717             x : x,
29718             y : y
29719         });
29720
29721         pos.push({
29722             x : x + (this.unitWidth + this.gutter) * 2,
29723             y : y
29724         });
29725
29726         pos.push({
29727             x : x + (this.unitWidth + this.gutter) * 2,
29728             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29729         });
29730             
29731         return pos;
29732         
29733     },
29734     
29735     getVerticalFourBoxColPositions : function(x, y, box)
29736     {
29737         var pos = [];
29738         
29739         if(box[0].size == 'xs'){
29740             
29741             pos.push({
29742                 x : x,
29743                 y : y
29744             });
29745
29746             pos.push({
29747                 x : x,
29748                 y : y + (this.unitHeight + this.gutter) * 1
29749             });
29750             
29751             pos.push({
29752                 x : x,
29753                 y : y + (this.unitHeight + this.gutter) * 2
29754             });
29755             
29756             pos.push({
29757                 x : x + (this.unitWidth + this.gutter) * 1,
29758                 y : y
29759             });
29760             
29761             return pos;
29762             
29763         }
29764         
29765         pos.push({
29766             x : x,
29767             y : y
29768         });
29769
29770         pos.push({
29771             x : x + (this.unitWidth + this.gutter) * 2,
29772             y : y
29773         });
29774
29775         pos.push({
29776             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29777             y : y + (this.unitHeight + this.gutter) * 1
29778         });
29779
29780         pos.push({
29781             x : x + (this.unitWidth + this.gutter) * 2,
29782             y : y + (this.unitWidth + this.gutter) * 2
29783         });
29784
29785         return pos;
29786         
29787     },
29788     
29789     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29790     {
29791         var pos = [];
29792         
29793         if(box[0].size == 'md-left'){
29794             pos.push({
29795                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29796                 y : minY
29797             });
29798             
29799             return pos;
29800         }
29801         
29802         if(box[0].size == 'md-right'){
29803             pos.push({
29804                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29805                 y : minY + (this.unitWidth + this.gutter) * 1
29806             });
29807             
29808             return pos;
29809         }
29810         
29811         var rand = Math.floor(Math.random() * (4 - box[0].y));
29812         
29813         pos.push({
29814             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29815             y : minY + (this.unitWidth + this.gutter) * rand
29816         });
29817         
29818         return pos;
29819         
29820     },
29821     
29822     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29823     {
29824         var pos = [];
29825         
29826         if(box[0].size == 'xs'){
29827             
29828             pos.push({
29829                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29830                 y : minY
29831             });
29832
29833             pos.push({
29834                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29835                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29836             });
29837             
29838             return pos;
29839             
29840         }
29841         
29842         pos.push({
29843             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29844             y : minY
29845         });
29846
29847         pos.push({
29848             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29849             y : minY + (this.unitWidth + this.gutter) * 2
29850         });
29851         
29852         return pos;
29853         
29854     },
29855     
29856     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29857     {
29858         var pos = [];
29859         
29860         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29861             
29862             pos.push({
29863                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29864                 y : minY
29865             });
29866
29867             pos.push({
29868                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29869                 y : minY + (this.unitWidth + this.gutter) * 1
29870             });
29871             
29872             pos.push({
29873                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29874                 y : minY + (this.unitWidth + this.gutter) * 2
29875             });
29876             
29877             return pos;
29878             
29879         }
29880         
29881         if(box[0].size == 'xs' && box[1].size == 'xs'){
29882             
29883             pos.push({
29884                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29885                 y : minY
29886             });
29887
29888             pos.push({
29889                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29890                 y : minY
29891             });
29892             
29893             pos.push({
29894                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29895                 y : minY + (this.unitWidth + this.gutter) * 1
29896             });
29897             
29898             return pos;
29899             
29900         }
29901         
29902         pos.push({
29903             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29904             y : minY
29905         });
29906
29907         pos.push({
29908             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29909             y : minY + (this.unitWidth + this.gutter) * 2
29910         });
29911
29912         pos.push({
29913             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29914             y : minY + (this.unitWidth + this.gutter) * 2
29915         });
29916             
29917         return pos;
29918         
29919     },
29920     
29921     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29922     {
29923         var pos = [];
29924         
29925         if(box[0].size == 'xs'){
29926             
29927             pos.push({
29928                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29929                 y : minY
29930             });
29931
29932             pos.push({
29933                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29934                 y : minY
29935             });
29936             
29937             pos.push({
29938                 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),
29939                 y : minY
29940             });
29941             
29942             pos.push({
29943                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29944                 y : minY + (this.unitWidth + this.gutter) * 1
29945             });
29946             
29947             return pos;
29948             
29949         }
29950         
29951         pos.push({
29952             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29953             y : minY
29954         });
29955         
29956         pos.push({
29957             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29958             y : minY + (this.unitWidth + this.gutter) * 2
29959         });
29960         
29961         pos.push({
29962             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29963             y : minY + (this.unitWidth + this.gutter) * 2
29964         });
29965         
29966         pos.push({
29967             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),
29968             y : minY + (this.unitWidth + this.gutter) * 2
29969         });
29970
29971         return pos;
29972         
29973     }
29974     
29975 });
29976
29977  
29978
29979  /**
29980  *
29981  * This is based on 
29982  * http://masonry.desandro.com
29983  *
29984  * The idea is to render all the bricks based on vertical width...
29985  *
29986  * The original code extends 'outlayer' - we might need to use that....
29987  * 
29988  */
29989
29990
29991 /**
29992  * @class Roo.bootstrap.LayoutMasonryAuto
29993  * @extends Roo.bootstrap.Component
29994  * Bootstrap Layout Masonry class
29995  * 
29996  * @constructor
29997  * Create a new Element
29998  * @param {Object} config The config object
29999  */
30000
30001 Roo.bootstrap.LayoutMasonryAuto = function(config){
30002     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30003 };
30004
30005 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30006     
30007       /**
30008      * @cfg {Boolean} isFitWidth  - resize the width..
30009      */   
30010     isFitWidth : false,  // options..
30011     /**
30012      * @cfg {Boolean} isOriginLeft = left align?
30013      */   
30014     isOriginLeft : true,
30015     /**
30016      * @cfg {Boolean} isOriginTop = top align?
30017      */   
30018     isOriginTop : false,
30019     /**
30020      * @cfg {Boolean} isLayoutInstant = no animation?
30021      */   
30022     isLayoutInstant : false, // needed?
30023     /**
30024      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30025      */   
30026     isResizingContainer : true,
30027     /**
30028      * @cfg {Number} columnWidth  width of the columns 
30029      */   
30030     
30031     columnWidth : 0,
30032     
30033     /**
30034      * @cfg {Number} maxCols maximum number of columns
30035      */   
30036     
30037     maxCols: 0,
30038     /**
30039      * @cfg {Number} padHeight padding below box..
30040      */   
30041     
30042     padHeight : 10, 
30043     
30044     /**
30045      * @cfg {Boolean} isAutoInitial defalut true
30046      */   
30047     
30048     isAutoInitial : true, 
30049     
30050     // private?
30051     gutter : 0,
30052     
30053     containerWidth: 0,
30054     initialColumnWidth : 0,
30055     currentSize : null,
30056     
30057     colYs : null, // array.
30058     maxY : 0,
30059     padWidth: 10,
30060     
30061     
30062     tag: 'div',
30063     cls: '',
30064     bricks: null, //CompositeElement
30065     cols : 0, // array?
30066     // element : null, // wrapped now this.el
30067     _isLayoutInited : null, 
30068     
30069     
30070     getAutoCreate : function(){
30071         
30072         var cfg = {
30073             tag: this.tag,
30074             cls: 'blog-masonary-wrapper ' + this.cls,
30075             cn : {
30076                 cls : 'mas-boxes masonary'
30077             }
30078         };
30079         
30080         return cfg;
30081     },
30082     
30083     getChildContainer: function( )
30084     {
30085         if (this.boxesEl) {
30086             return this.boxesEl;
30087         }
30088         
30089         this.boxesEl = this.el.select('.mas-boxes').first();
30090         
30091         return this.boxesEl;
30092     },
30093     
30094     
30095     initEvents : function()
30096     {
30097         var _this = this;
30098         
30099         if(this.isAutoInitial){
30100             Roo.log('hook children rendered');
30101             this.on('childrenrendered', function() {
30102                 Roo.log('children rendered');
30103                 _this.initial();
30104             } ,this);
30105         }
30106         
30107     },
30108     
30109     initial : function()
30110     {
30111         this.reloadItems();
30112
30113         this.currentSize = this.el.getBox(true);
30114
30115         /// was window resize... - let's see if this works..
30116         Roo.EventManager.onWindowResize(this.resize, this); 
30117
30118         if(!this.isAutoInitial){
30119             this.layout();
30120             return;
30121         }
30122         
30123         this.layout.defer(500,this);
30124     },
30125     
30126     reloadItems: function()
30127     {
30128         this.bricks = this.el.select('.masonry-brick', true);
30129         
30130         this.bricks.each(function(b) {
30131             //Roo.log(b.getSize());
30132             if (!b.attr('originalwidth')) {
30133                 b.attr('originalwidth',  b.getSize().width);
30134             }
30135             
30136         });
30137         
30138         Roo.log(this.bricks.elements.length);
30139     },
30140     
30141     resize : function()
30142     {
30143         Roo.log('resize');
30144         var cs = this.el.getBox(true);
30145         
30146         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30147             Roo.log("no change in with or X");
30148             return;
30149         }
30150         this.currentSize = cs;
30151         this.layout();
30152     },
30153     
30154     layout : function()
30155     {
30156          Roo.log('layout');
30157         this._resetLayout();
30158         //this._manageStamps();
30159       
30160         // don't animate first layout
30161         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30162         this.layoutItems( isInstant );
30163       
30164         // flag for initalized
30165         this._isLayoutInited = true;
30166     },
30167     
30168     layoutItems : function( isInstant )
30169     {
30170         //var items = this._getItemsForLayout( this.items );
30171         // original code supports filtering layout items.. we just ignore it..
30172         
30173         this._layoutItems( this.bricks , isInstant );
30174       
30175         this._postLayout();
30176     },
30177     _layoutItems : function ( items , isInstant)
30178     {
30179        //this.fireEvent( 'layout', this, items );
30180     
30181
30182         if ( !items || !items.elements.length ) {
30183           // no items, emit event with empty array
30184             return;
30185         }
30186
30187         var queue = [];
30188         items.each(function(item) {
30189             Roo.log("layout item");
30190             Roo.log(item);
30191             // get x/y object from method
30192             var position = this._getItemLayoutPosition( item );
30193             // enqueue
30194             position.item = item;
30195             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30196             queue.push( position );
30197         }, this);
30198       
30199         this._processLayoutQueue( queue );
30200     },
30201     /** Sets position of item in DOM
30202     * @param {Element} item
30203     * @param {Number} x - horizontal position
30204     * @param {Number} y - vertical position
30205     * @param {Boolean} isInstant - disables transitions
30206     */
30207     _processLayoutQueue : function( queue )
30208     {
30209         for ( var i=0, len = queue.length; i < len; i++ ) {
30210             var obj = queue[i];
30211             obj.item.position('absolute');
30212             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30213         }
30214     },
30215       
30216     
30217     /**
30218     * Any logic you want to do after each layout,
30219     * i.e. size the container
30220     */
30221     _postLayout : function()
30222     {
30223         this.resizeContainer();
30224     },
30225     
30226     resizeContainer : function()
30227     {
30228         if ( !this.isResizingContainer ) {
30229             return;
30230         }
30231         var size = this._getContainerSize();
30232         if ( size ) {
30233             this.el.setSize(size.width,size.height);
30234             this.boxesEl.setSize(size.width,size.height);
30235         }
30236     },
30237     
30238     
30239     
30240     _resetLayout : function()
30241     {
30242         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30243         this.colWidth = this.el.getWidth();
30244         //this.gutter = this.el.getWidth(); 
30245         
30246         this.measureColumns();
30247
30248         // reset column Y
30249         var i = this.cols;
30250         this.colYs = [];
30251         while (i--) {
30252             this.colYs.push( 0 );
30253         }
30254     
30255         this.maxY = 0;
30256     },
30257
30258     measureColumns : function()
30259     {
30260         this.getContainerWidth();
30261       // if columnWidth is 0, default to outerWidth of first item
30262         if ( !this.columnWidth ) {
30263             var firstItem = this.bricks.first();
30264             Roo.log(firstItem);
30265             this.columnWidth  = this.containerWidth;
30266             if (firstItem && firstItem.attr('originalwidth') ) {
30267                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30268             }
30269             // columnWidth fall back to item of first element
30270             Roo.log("set column width?");
30271                         this.initialColumnWidth = this.columnWidth  ;
30272
30273             // if first elem has no width, default to size of container
30274             
30275         }
30276         
30277         
30278         if (this.initialColumnWidth) {
30279             this.columnWidth = this.initialColumnWidth;
30280         }
30281         
30282         
30283             
30284         // column width is fixed at the top - however if container width get's smaller we should
30285         // reduce it...
30286         
30287         // this bit calcs how man columns..
30288             
30289         var columnWidth = this.columnWidth += this.gutter;
30290       
30291         // calculate columns
30292         var containerWidth = this.containerWidth + this.gutter;
30293         
30294         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30295         // fix rounding errors, typically with gutters
30296         var excess = columnWidth - containerWidth % columnWidth;
30297         
30298         
30299         // if overshoot is less than a pixel, round up, otherwise floor it
30300         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30301         cols = Math[ mathMethod ]( cols );
30302         this.cols = Math.max( cols, 1 );
30303         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30304         
30305          // padding positioning..
30306         var totalColWidth = this.cols * this.columnWidth;
30307         var padavail = this.containerWidth - totalColWidth;
30308         // so for 2 columns - we need 3 'pads'
30309         
30310         var padNeeded = (1+this.cols) * this.padWidth;
30311         
30312         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30313         
30314         this.columnWidth += padExtra
30315         //this.padWidth = Math.floor(padavail /  ( this.cols));
30316         
30317         // adjust colum width so that padding is fixed??
30318         
30319         // we have 3 columns ... total = width * 3
30320         // we have X left over... that should be used by 
30321         
30322         //if (this.expandC) {
30323             
30324         //}
30325         
30326         
30327         
30328     },
30329     
30330     getContainerWidth : function()
30331     {
30332        /* // container is parent if fit width
30333         var container = this.isFitWidth ? this.element.parentNode : this.element;
30334         // check that this.size and size are there
30335         // IE8 triggers resize on body size change, so they might not be
30336         
30337         var size = getSize( container );  //FIXME
30338         this.containerWidth = size && size.innerWidth; //FIXME
30339         */
30340          
30341         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30342         
30343     },
30344     
30345     _getItemLayoutPosition : function( item )  // what is item?
30346     {
30347         // we resize the item to our columnWidth..
30348       
30349         item.setWidth(this.columnWidth);
30350         item.autoBoxAdjust  = false;
30351         
30352         var sz = item.getSize();
30353  
30354         // how many columns does this brick span
30355         var remainder = this.containerWidth % this.columnWidth;
30356         
30357         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30358         // round if off by 1 pixel, otherwise use ceil
30359         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30360         colSpan = Math.min( colSpan, this.cols );
30361         
30362         // normally this should be '1' as we dont' currently allow multi width columns..
30363         
30364         var colGroup = this._getColGroup( colSpan );
30365         // get the minimum Y value from the columns
30366         var minimumY = Math.min.apply( Math, colGroup );
30367         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30368         
30369         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30370          
30371         // position the brick
30372         var position = {
30373             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30374             y: this.currentSize.y + minimumY + this.padHeight
30375         };
30376         
30377         Roo.log(position);
30378         // apply setHeight to necessary columns
30379         var setHeight = minimumY + sz.height + this.padHeight;
30380         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30381         
30382         var setSpan = this.cols + 1 - colGroup.length;
30383         for ( var i = 0; i < setSpan; i++ ) {
30384           this.colYs[ shortColIndex + i ] = setHeight ;
30385         }
30386       
30387         return position;
30388     },
30389     
30390     /**
30391      * @param {Number} colSpan - number of columns the element spans
30392      * @returns {Array} colGroup
30393      */
30394     _getColGroup : function( colSpan )
30395     {
30396         if ( colSpan < 2 ) {
30397           // if brick spans only one column, use all the column Ys
30398           return this.colYs;
30399         }
30400       
30401         var colGroup = [];
30402         // how many different places could this brick fit horizontally
30403         var groupCount = this.cols + 1 - colSpan;
30404         // for each group potential horizontal position
30405         for ( var i = 0; i < groupCount; i++ ) {
30406           // make an array of colY values for that one group
30407           var groupColYs = this.colYs.slice( i, i + colSpan );
30408           // and get the max value of the array
30409           colGroup[i] = Math.max.apply( Math, groupColYs );
30410         }
30411         return colGroup;
30412     },
30413     /*
30414     _manageStamp : function( stamp )
30415     {
30416         var stampSize =  stamp.getSize();
30417         var offset = stamp.getBox();
30418         // get the columns that this stamp affects
30419         var firstX = this.isOriginLeft ? offset.x : offset.right;
30420         var lastX = firstX + stampSize.width;
30421         var firstCol = Math.floor( firstX / this.columnWidth );
30422         firstCol = Math.max( 0, firstCol );
30423         
30424         var lastCol = Math.floor( lastX / this.columnWidth );
30425         // lastCol should not go over if multiple of columnWidth #425
30426         lastCol -= lastX % this.columnWidth ? 0 : 1;
30427         lastCol = Math.min( this.cols - 1, lastCol );
30428         
30429         // set colYs to bottom of the stamp
30430         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30431             stampSize.height;
30432             
30433         for ( var i = firstCol; i <= lastCol; i++ ) {
30434           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30435         }
30436     },
30437     */
30438     
30439     _getContainerSize : function()
30440     {
30441         this.maxY = Math.max.apply( Math, this.colYs );
30442         var size = {
30443             height: this.maxY
30444         };
30445       
30446         if ( this.isFitWidth ) {
30447             size.width = this._getContainerFitWidth();
30448         }
30449       
30450         return size;
30451     },
30452     
30453     _getContainerFitWidth : function()
30454     {
30455         var unusedCols = 0;
30456         // count unused columns
30457         var i = this.cols;
30458         while ( --i ) {
30459           if ( this.colYs[i] !== 0 ) {
30460             break;
30461           }
30462           unusedCols++;
30463         }
30464         // fit container to columns that have been used
30465         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30466     },
30467     
30468     needsResizeLayout : function()
30469     {
30470         var previousWidth = this.containerWidth;
30471         this.getContainerWidth();
30472         return previousWidth !== this.containerWidth;
30473     }
30474  
30475 });
30476
30477  
30478
30479  /*
30480  * - LGPL
30481  *
30482  * element
30483  * 
30484  */
30485
30486 /**
30487  * @class Roo.bootstrap.MasonryBrick
30488  * @extends Roo.bootstrap.Component
30489  * Bootstrap MasonryBrick class
30490  * 
30491  * @constructor
30492  * Create a new MasonryBrick
30493  * @param {Object} config The config object
30494  */
30495
30496 Roo.bootstrap.MasonryBrick = function(config){
30497     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30498     
30499     this.addEvents({
30500         // raw events
30501         /**
30502          * @event click
30503          * When a MasonryBrick is clcik
30504          * @param {Roo.bootstrap.MasonryBrick} this
30505          * @param {Roo.EventObject} e
30506          */
30507         "click" : true
30508     });
30509 };
30510
30511 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30512     
30513     /**
30514      * @cfg {String} title
30515      */   
30516     title : '',
30517     /**
30518      * @cfg {String} html
30519      */   
30520     html : '',
30521     /**
30522      * @cfg {String} bgimage
30523      */   
30524     bgimage : '',
30525     /**
30526      * @cfg {String} videourl
30527      */   
30528     videourl : '',
30529     /**
30530      * @cfg {String} cls
30531      */   
30532     cls : '',
30533     /**
30534      * @cfg {String} href
30535      */   
30536     href : '',
30537     /**
30538      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30539      */   
30540     size : 'xs',
30541     
30542     /**
30543      * @cfg {String} (center|bottom) placetitle
30544      */   
30545     placetitle : '',
30546     
30547     getAutoCreate : function()
30548     {
30549         var cls = 'masonry-brick';
30550         
30551         if(this.href.length){
30552             cls += ' masonry-brick-link';
30553         }
30554         
30555         if(this.bgimage.length){
30556             cls += ' masonry-brick-image';
30557         }
30558         
30559         if(this.size){
30560             cls += ' masonry-' + this.size + '-brick';
30561         }
30562         
30563         if(this.placetitle.length){
30564             
30565             switch (this.placetitle) {
30566                 case 'center' :
30567                     cls += ' masonry-center-title';
30568                     break;
30569                 case 'bottom' :
30570                     cls += ' masonry-bottom-title';
30571                     break;
30572                 default:
30573                     break;
30574             }
30575             
30576         } else {
30577             if(!this.html.length && !this.bgimage.length){
30578                 cls += ' masonry-center-title';
30579             }
30580
30581             if(!this.html.length && this.bgimage.length){
30582                 cls += ' masonry-bottom-title';
30583             }
30584         }
30585         
30586         if(this.cls){
30587             cls += ' ' + this.cls;
30588         }
30589         
30590         var cfg = {
30591             tag: (this.href.length) ? 'a' : 'div',
30592             cls: cls,
30593             cn: [
30594                 {
30595                     tag: 'div',
30596                     cls: 'masonry-brick-paragraph',
30597                     cn: []
30598                 }
30599             ]
30600         };
30601         
30602         if(this.href.length){
30603             cfg.href = this.href;
30604         }
30605         
30606         var cn = cfg.cn[0].cn;
30607         
30608         if(this.title.length){
30609             cn.push({
30610                 tag: 'h4',
30611                 cls: 'masonry-brick-title',
30612                 html: this.title
30613             });
30614         }
30615         
30616         if(this.html.length){
30617             cn.push({
30618                 tag: 'p',
30619                 cls: 'masonry-brick-text',
30620                 html: this.html
30621             });
30622         }  
30623         if (!this.title.length && !this.html.length) {
30624             cfg.cn[0].cls += ' hide';
30625         }
30626         
30627         if(this.bgimage.length){
30628             cfg.cn.push({
30629                 tag: 'img',
30630                 cls: 'masonry-brick-image-view',
30631                 src: this.bgimage
30632             });
30633         }
30634         if(this.videourl.length){
30635             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30636             // youtube support only?
30637             cfg.cn.push({
30638                 tag: 'iframe',
30639                 cls: 'masonry-brick-image-view',
30640                 src: vurl,
30641                 frameborder : 0,
30642                 allowfullscreen : true
30643             });
30644             
30645             
30646         }
30647         return cfg;
30648         
30649     },
30650     
30651     initEvents: function() 
30652     {
30653         switch (this.size) {
30654             case 'xs' :
30655 //                this.intSize = 1;
30656                 this.x = 1;
30657                 this.y = 1;
30658                 break;
30659             case 'sm' :
30660 //                this.intSize = 2;
30661                 this.x = 2;
30662                 this.y = 2;
30663                 break;
30664             case 'md' :
30665             case 'md-left' :
30666             case 'md-right' :
30667 //                this.intSize = 3;
30668                 this.x = 3;
30669                 this.y = 3;
30670                 break;
30671             case 'tall' :
30672 //                this.intSize = 3;
30673                 this.x = 2;
30674                 this.y = 3;
30675                 break;
30676             case 'wide' :
30677 //                this.intSize = 3;
30678                 this.x = 3;
30679                 this.y = 2;
30680                 break;
30681             case 'wide-thin' :
30682 //                this.intSize = 3;
30683                 this.x = 3;
30684                 this.y = 1;
30685                 break;
30686                         
30687             default :
30688                 break;
30689         }
30690         
30691         
30692         
30693         if(Roo.isTouch){
30694             this.el.on('touchstart', this.onTouchStart, this);
30695             this.el.on('touchmove', this.onTouchMove, this);
30696             this.el.on('touchend', this.onTouchEnd, this);
30697             this.el.on('contextmenu', this.onContextMenu, this);
30698         } else {
30699             this.el.on('mouseenter'  ,this.enter, this);
30700             this.el.on('mouseleave', this.leave, this);
30701         }
30702         
30703         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30704             this.parent().bricks.push(this);   
30705         }
30706         
30707     },
30708     
30709     onClick: function(e, el)
30710     {
30711         if(!Roo.isTouch){
30712             return;
30713         }
30714         
30715         var time = this.endTimer - this.startTimer;
30716         
30717         //alert(time);
30718         
30719         if(time < 1000){
30720             return;
30721         }
30722         
30723         e.preventDefault();
30724     },
30725     
30726     enter: function(e, el)
30727     {
30728         e.preventDefault();
30729         
30730         if(this.bgimage.length && this.html.length){
30731             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30732         }
30733     },
30734     
30735     leave: function(e, el)
30736     {
30737         e.preventDefault();
30738         
30739         if(this.bgimage.length && this.html.length){
30740             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30741         }
30742     },
30743     
30744     onTouchStart: function(e, el)
30745     {
30746 //        e.preventDefault();
30747         
30748         this.touchmoved = false;
30749         
30750         if(!this.bgimage.length || !this.html.length){
30751             return;
30752         }
30753         
30754         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30755         
30756         this.timer = new Date().getTime();
30757         
30758     },
30759     
30760     onTouchMove: function(e, el)
30761     {
30762         this.touchmoved = true;
30763     },
30764     
30765     onContextMenu : function(e,el)
30766     {
30767         e.preventDefault();
30768         e.stopPropagation();
30769         return false;
30770     },
30771     
30772     onTouchEnd: function(e, el)
30773     {
30774 //        e.preventDefault();
30775         
30776         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30777         
30778             this.leave(e,el);
30779             
30780             return;
30781         }
30782         
30783         if(!this.bgimage.length || !this.html.length){
30784             
30785             if(this.href.length){
30786                 window.location.href = this.href;
30787             }
30788             
30789             return;
30790         }
30791         
30792         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30793         
30794         window.location.href = this.href;
30795     }
30796     
30797 });
30798
30799  
30800
30801  /*
30802  * - LGPL
30803  *
30804  * element
30805  * 
30806  */
30807
30808 /**
30809  * @class Roo.bootstrap.Brick
30810  * @extends Roo.bootstrap.Component
30811  * Bootstrap Brick class
30812  * 
30813  * @constructor
30814  * Create a new Brick
30815  * @param {Object} config The config object
30816  */
30817
30818 Roo.bootstrap.Brick = function(config){
30819     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30820     
30821     this.addEvents({
30822         // raw events
30823         /**
30824          * @event click
30825          * When a Brick is click
30826          * @param {Roo.bootstrap.Brick} this
30827          * @param {Roo.EventObject} e
30828          */
30829         "click" : true
30830     });
30831 };
30832
30833 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30834     
30835     /**
30836      * @cfg {String} title
30837      */   
30838     title : '',
30839     /**
30840      * @cfg {String} html
30841      */   
30842     html : '',
30843     /**
30844      * @cfg {String} bgimage
30845      */   
30846     bgimage : '',
30847     /**
30848      * @cfg {String} cls
30849      */   
30850     cls : '',
30851     /**
30852      * @cfg {String} href
30853      */   
30854     href : '',
30855     /**
30856      * @cfg {String} video
30857      */   
30858     video : '',
30859     /**
30860      * @cfg {Boolean} square
30861      */   
30862     square : true,
30863     
30864     getAutoCreate : function()
30865     {
30866         var cls = 'roo-brick';
30867         
30868         if(this.href.length){
30869             cls += ' roo-brick-link';
30870         }
30871         
30872         if(this.bgimage.length){
30873             cls += ' roo-brick-image';
30874         }
30875         
30876         if(!this.html.length && !this.bgimage.length){
30877             cls += ' roo-brick-center-title';
30878         }
30879         
30880         if(!this.html.length && this.bgimage.length){
30881             cls += ' roo-brick-bottom-title';
30882         }
30883         
30884         if(this.cls){
30885             cls += ' ' + this.cls;
30886         }
30887         
30888         var cfg = {
30889             tag: (this.href.length) ? 'a' : 'div',
30890             cls: cls,
30891             cn: [
30892                 {
30893                     tag: 'div',
30894                     cls: 'roo-brick-paragraph',
30895                     cn: []
30896                 }
30897             ]
30898         };
30899         
30900         if(this.href.length){
30901             cfg.href = this.href;
30902         }
30903         
30904         var cn = cfg.cn[0].cn;
30905         
30906         if(this.title.length){
30907             cn.push({
30908                 tag: 'h4',
30909                 cls: 'roo-brick-title',
30910                 html: this.title
30911             });
30912         }
30913         
30914         if(this.html.length){
30915             cn.push({
30916                 tag: 'p',
30917                 cls: 'roo-brick-text',
30918                 html: this.html
30919             });
30920         } else {
30921             cn.cls += ' hide';
30922         }
30923         
30924         if(this.bgimage.length){
30925             cfg.cn.push({
30926                 tag: 'img',
30927                 cls: 'roo-brick-image-view',
30928                 src: this.bgimage
30929             });
30930         }
30931         
30932         return cfg;
30933     },
30934     
30935     initEvents: function() 
30936     {
30937         if(this.title.length || this.html.length){
30938             this.el.on('mouseenter'  ,this.enter, this);
30939             this.el.on('mouseleave', this.leave, this);
30940         }
30941         
30942         
30943         Roo.EventManager.onWindowResize(this.resize, this); 
30944         
30945         this.resize();
30946     },
30947     
30948     resize : function()
30949     {
30950         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30951         
30952         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30953 //        paragraph.setHeight(paragraph.getWidth());
30954         
30955         if(this.bgimage.length){
30956             var image = this.el.select('.roo-brick-image-view', true).first();
30957             image.setWidth(paragraph.getWidth());
30958             image.setHeight(paragraph.getWidth());
30959         }
30960         
30961     },
30962     
30963     enter: function(e, el)
30964     {
30965         e.preventDefault();
30966         
30967         if(this.bgimage.length){
30968             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30969             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30970         }
30971     },
30972     
30973     leave: function(e, el)
30974     {
30975         e.preventDefault();
30976         
30977         if(this.bgimage.length){
30978             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30979             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30980         }
30981     }
30982     
30983 });
30984
30985  
30986
30987  /*
30988  * Based on:
30989  * Ext JS Library 1.1.1
30990  * Copyright(c) 2006-2007, Ext JS, LLC.
30991  *
30992  * Originally Released Under LGPL - original licence link has changed is not relivant.
30993  *
30994  * Fork - LGPL
30995  * <script type="text/javascript">
30996  */
30997
30998
30999 /**
31000  * @class Roo.bootstrap.SplitBar
31001  * @extends Roo.util.Observable
31002  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31003  * <br><br>
31004  * Usage:
31005  * <pre><code>
31006 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31007                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31008 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31009 split.minSize = 100;
31010 split.maxSize = 600;
31011 split.animate = true;
31012 split.on('moved', splitterMoved);
31013 </code></pre>
31014  * @constructor
31015  * Create a new SplitBar
31016  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31017  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31018  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31019  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31020                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31021                         position of the SplitBar).
31022  */
31023 Roo.bootstrap.SplitBar = function(cfg){
31024     
31025     /** @private */
31026     
31027     //{
31028     //  dragElement : elm
31029     //  resizingElement: el,
31030         // optional..
31031     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31032     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31033         // existingProxy ???
31034     //}
31035     
31036     this.el = Roo.get(cfg.dragElement, true);
31037     this.el.dom.unselectable = "on";
31038     /** @private */
31039     this.resizingEl = Roo.get(cfg.resizingElement, true);
31040
31041     /**
31042      * @private
31043      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31044      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31045      * @type Number
31046      */
31047     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31048     
31049     /**
31050      * The minimum size of the resizing element. (Defaults to 0)
31051      * @type Number
31052      */
31053     this.minSize = 0;
31054     
31055     /**
31056      * The maximum size of the resizing element. (Defaults to 2000)
31057      * @type Number
31058      */
31059     this.maxSize = 2000;
31060     
31061     /**
31062      * Whether to animate the transition to the new size
31063      * @type Boolean
31064      */
31065     this.animate = false;
31066     
31067     /**
31068      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31069      * @type Boolean
31070      */
31071     this.useShim = false;
31072     
31073     /** @private */
31074     this.shim = null;
31075     
31076     if(!cfg.existingProxy){
31077         /** @private */
31078         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31079     }else{
31080         this.proxy = Roo.get(cfg.existingProxy).dom;
31081     }
31082     /** @private */
31083     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31084     
31085     /** @private */
31086     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31087     
31088     /** @private */
31089     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31090     
31091     /** @private */
31092     this.dragSpecs = {};
31093     
31094     /**
31095      * @private The adapter to use to positon and resize elements
31096      */
31097     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31098     this.adapter.init(this);
31099     
31100     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31101         /** @private */
31102         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31103         this.el.addClass("roo-splitbar-h");
31104     }else{
31105         /** @private */
31106         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31107         this.el.addClass("roo-splitbar-v");
31108     }
31109     
31110     this.addEvents({
31111         /**
31112          * @event resize
31113          * Fires when the splitter is moved (alias for {@link #event-moved})
31114          * @param {Roo.bootstrap.SplitBar} this
31115          * @param {Number} newSize the new width or height
31116          */
31117         "resize" : true,
31118         /**
31119          * @event moved
31120          * Fires when the splitter is moved
31121          * @param {Roo.bootstrap.SplitBar} this
31122          * @param {Number} newSize the new width or height
31123          */
31124         "moved" : true,
31125         /**
31126          * @event beforeresize
31127          * Fires before the splitter is dragged
31128          * @param {Roo.bootstrap.SplitBar} this
31129          */
31130         "beforeresize" : true,
31131
31132         "beforeapply" : true
31133     });
31134
31135     Roo.util.Observable.call(this);
31136 };
31137
31138 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31139     onStartProxyDrag : function(x, y){
31140         this.fireEvent("beforeresize", this);
31141         if(!this.overlay){
31142             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31143             o.unselectable();
31144             o.enableDisplayMode("block");
31145             // all splitbars share the same overlay
31146             Roo.bootstrap.SplitBar.prototype.overlay = o;
31147         }
31148         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31149         this.overlay.show();
31150         Roo.get(this.proxy).setDisplayed("block");
31151         var size = this.adapter.getElementSize(this);
31152         this.activeMinSize = this.getMinimumSize();;
31153         this.activeMaxSize = this.getMaximumSize();;
31154         var c1 = size - this.activeMinSize;
31155         var c2 = Math.max(this.activeMaxSize - size, 0);
31156         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31157             this.dd.resetConstraints();
31158             this.dd.setXConstraint(
31159                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31160                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31161             );
31162             this.dd.setYConstraint(0, 0);
31163         }else{
31164             this.dd.resetConstraints();
31165             this.dd.setXConstraint(0, 0);
31166             this.dd.setYConstraint(
31167                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31168                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31169             );
31170          }
31171         this.dragSpecs.startSize = size;
31172         this.dragSpecs.startPoint = [x, y];
31173         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31174     },
31175     
31176     /** 
31177      * @private Called after the drag operation by the DDProxy
31178      */
31179     onEndProxyDrag : function(e){
31180         Roo.get(this.proxy).setDisplayed(false);
31181         var endPoint = Roo.lib.Event.getXY(e);
31182         if(this.overlay){
31183             this.overlay.hide();
31184         }
31185         var newSize;
31186         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31187             newSize = this.dragSpecs.startSize + 
31188                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31189                     endPoint[0] - this.dragSpecs.startPoint[0] :
31190                     this.dragSpecs.startPoint[0] - endPoint[0]
31191                 );
31192         }else{
31193             newSize = this.dragSpecs.startSize + 
31194                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31195                     endPoint[1] - this.dragSpecs.startPoint[1] :
31196                     this.dragSpecs.startPoint[1] - endPoint[1]
31197                 );
31198         }
31199         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31200         if(newSize != this.dragSpecs.startSize){
31201             if(this.fireEvent('beforeapply', this, newSize) !== false){
31202                 this.adapter.setElementSize(this, newSize);
31203                 this.fireEvent("moved", this, newSize);
31204                 this.fireEvent("resize", this, newSize);
31205             }
31206         }
31207     },
31208     
31209     /**
31210      * Get the adapter this SplitBar uses
31211      * @return The adapter object
31212      */
31213     getAdapter : function(){
31214         return this.adapter;
31215     },
31216     
31217     /**
31218      * Set the adapter this SplitBar uses
31219      * @param {Object} adapter A SplitBar adapter object
31220      */
31221     setAdapter : function(adapter){
31222         this.adapter = adapter;
31223         this.adapter.init(this);
31224     },
31225     
31226     /**
31227      * Gets the minimum size for the resizing element
31228      * @return {Number} The minimum size
31229      */
31230     getMinimumSize : function(){
31231         return this.minSize;
31232     },
31233     
31234     /**
31235      * Sets the minimum size for the resizing element
31236      * @param {Number} minSize The minimum size
31237      */
31238     setMinimumSize : function(minSize){
31239         this.minSize = minSize;
31240     },
31241     
31242     /**
31243      * Gets the maximum size for the resizing element
31244      * @return {Number} The maximum size
31245      */
31246     getMaximumSize : function(){
31247         return this.maxSize;
31248     },
31249     
31250     /**
31251      * Sets the maximum size for the resizing element
31252      * @param {Number} maxSize The maximum size
31253      */
31254     setMaximumSize : function(maxSize){
31255         this.maxSize = maxSize;
31256     },
31257     
31258     /**
31259      * Sets the initialize size for the resizing element
31260      * @param {Number} size The initial size
31261      */
31262     setCurrentSize : function(size){
31263         var oldAnimate = this.animate;
31264         this.animate = false;
31265         this.adapter.setElementSize(this, size);
31266         this.animate = oldAnimate;
31267     },
31268     
31269     /**
31270      * Destroy this splitbar. 
31271      * @param {Boolean} removeEl True to remove the element
31272      */
31273     destroy : function(removeEl){
31274         if(this.shim){
31275             this.shim.remove();
31276         }
31277         this.dd.unreg();
31278         this.proxy.parentNode.removeChild(this.proxy);
31279         if(removeEl){
31280             this.el.remove();
31281         }
31282     }
31283 });
31284
31285 /**
31286  * @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.
31287  */
31288 Roo.bootstrap.SplitBar.createProxy = function(dir){
31289     var proxy = new Roo.Element(document.createElement("div"));
31290     proxy.unselectable();
31291     var cls = 'roo-splitbar-proxy';
31292     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31293     document.body.appendChild(proxy.dom);
31294     return proxy.dom;
31295 };
31296
31297 /** 
31298  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31299  * Default Adapter. It assumes the splitter and resizing element are not positioned
31300  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31301  */
31302 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31303 };
31304
31305 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31306     // do nothing for now
31307     init : function(s){
31308     
31309     },
31310     /**
31311      * Called before drag operations to get the current size of the resizing element. 
31312      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31313      */
31314      getElementSize : function(s){
31315         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31316             return s.resizingEl.getWidth();
31317         }else{
31318             return s.resizingEl.getHeight();
31319         }
31320     },
31321     
31322     /**
31323      * Called after drag operations to set the size of the resizing element.
31324      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31325      * @param {Number} newSize The new size to set
31326      * @param {Function} onComplete A function to be invoked when resizing is complete
31327      */
31328     setElementSize : function(s, newSize, onComplete){
31329         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31330             if(!s.animate){
31331                 s.resizingEl.setWidth(newSize);
31332                 if(onComplete){
31333                     onComplete(s, newSize);
31334                 }
31335             }else{
31336                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31337             }
31338         }else{
31339             
31340             if(!s.animate){
31341                 s.resizingEl.setHeight(newSize);
31342                 if(onComplete){
31343                     onComplete(s, newSize);
31344                 }
31345             }else{
31346                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31347             }
31348         }
31349     }
31350 };
31351
31352 /** 
31353  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31354  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31355  * Adapter that  moves the splitter element to align with the resized sizing element. 
31356  * Used with an absolute positioned SplitBar.
31357  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31358  * document.body, make sure you assign an id to the body element.
31359  */
31360 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31361     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31362     this.container = Roo.get(container);
31363 };
31364
31365 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31366     init : function(s){
31367         this.basic.init(s);
31368     },
31369     
31370     getElementSize : function(s){
31371         return this.basic.getElementSize(s);
31372     },
31373     
31374     setElementSize : function(s, newSize, onComplete){
31375         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31376     },
31377     
31378     moveSplitter : function(s){
31379         var yes = Roo.bootstrap.SplitBar;
31380         switch(s.placement){
31381             case yes.LEFT:
31382                 s.el.setX(s.resizingEl.getRight());
31383                 break;
31384             case yes.RIGHT:
31385                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31386                 break;
31387             case yes.TOP:
31388                 s.el.setY(s.resizingEl.getBottom());
31389                 break;
31390             case yes.BOTTOM:
31391                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31392                 break;
31393         }
31394     }
31395 };
31396
31397 /**
31398  * Orientation constant - Create a vertical SplitBar
31399  * @static
31400  * @type Number
31401  */
31402 Roo.bootstrap.SplitBar.VERTICAL = 1;
31403
31404 /**
31405  * Orientation constant - Create a horizontal SplitBar
31406  * @static
31407  * @type Number
31408  */
31409 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31410
31411 /**
31412  * Placement constant - The resizing element is to the left of the splitter element
31413  * @static
31414  * @type Number
31415  */
31416 Roo.bootstrap.SplitBar.LEFT = 1;
31417
31418 /**
31419  * Placement constant - The resizing element is to the right of the splitter element
31420  * @static
31421  * @type Number
31422  */
31423 Roo.bootstrap.SplitBar.RIGHT = 2;
31424
31425 /**
31426  * Placement constant - The resizing element is positioned above the splitter element
31427  * @static
31428  * @type Number
31429  */
31430 Roo.bootstrap.SplitBar.TOP = 3;
31431
31432 /**
31433  * Placement constant - The resizing element is positioned under splitter element
31434  * @static
31435  * @type Number
31436  */
31437 Roo.bootstrap.SplitBar.BOTTOM = 4;
31438 Roo.namespace("Roo.bootstrap.layout");/*
31439  * Based on:
31440  * Ext JS Library 1.1.1
31441  * Copyright(c) 2006-2007, Ext JS, LLC.
31442  *
31443  * Originally Released Under LGPL - original licence link has changed is not relivant.
31444  *
31445  * Fork - LGPL
31446  * <script type="text/javascript">
31447  */
31448  
31449 /**
31450  * @class Roo.bootstrap.layout.Manager
31451  * @extends Roo.bootstrap.Component
31452  * Base class for layout managers.
31453  */
31454 Roo.bootstrap.layout.Manager = function(config)
31455 {
31456     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31457     
31458     
31459      
31460     
31461     
31462     /** false to disable window resize monitoring @type Boolean */
31463     this.monitorWindowResize = true;
31464     this.regions = {};
31465     this.addEvents({
31466         /**
31467          * @event layout
31468          * Fires when a layout is performed. 
31469          * @param {Roo.LayoutManager} this
31470          */
31471         "layout" : true,
31472         /**
31473          * @event regionresized
31474          * Fires when the user resizes a region. 
31475          * @param {Roo.LayoutRegion} region The resized region
31476          * @param {Number} newSize The new size (width for east/west, height for north/south)
31477          */
31478         "regionresized" : true,
31479         /**
31480          * @event regioncollapsed
31481          * Fires when a region is collapsed. 
31482          * @param {Roo.LayoutRegion} region The collapsed region
31483          */
31484         "regioncollapsed" : true,
31485         /**
31486          * @event regionexpanded
31487          * Fires when a region is expanded.  
31488          * @param {Roo.LayoutRegion} region The expanded region
31489          */
31490         "regionexpanded" : true
31491     });
31492     this.updating = false;
31493     
31494     if (config.el) {
31495         this.el = Roo.get(config.el);
31496         this.initEvents();
31497     }
31498     
31499 };
31500
31501 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31502     
31503     
31504     regions : null,
31505     
31506     monitorWindowResize : true,
31507     
31508     
31509     updating : false,
31510     
31511     
31512     onRender : function(ct, position)
31513     {
31514         if(!this.el){
31515             this.el = Roo.get(ct);
31516             this.initEvents();
31517         }
31518     },
31519     
31520     
31521     initEvents: function()
31522     {
31523         
31524         
31525         // ie scrollbar fix
31526         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31527             document.body.scroll = "no";
31528         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31529             this.el.position('relative');
31530         }
31531         this.id = this.el.id;
31532         this.el.addClass("roo-layout-container");
31533         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31534         if(this.el.dom != document.body ) {
31535             this.el.on('resize', this.layout,this);
31536             this.el.on('show', this.layout,this);
31537         }
31538
31539     },
31540     
31541     /**
31542      * Returns true if this layout is currently being updated
31543      * @return {Boolean}
31544      */
31545     isUpdating : function(){
31546         return this.updating; 
31547     },
31548     
31549     /**
31550      * Suspend the LayoutManager from doing auto-layouts while
31551      * making multiple add or remove calls
31552      */
31553     beginUpdate : function(){
31554         this.updating = true;    
31555     },
31556     
31557     /**
31558      * Restore auto-layouts and optionally disable the manager from performing a layout
31559      * @param {Boolean} noLayout true to disable a layout update 
31560      */
31561     endUpdate : function(noLayout){
31562         this.updating = false;
31563         if(!noLayout){
31564             this.layout();
31565         }    
31566     },
31567     
31568     layout: function(){
31569         // abstract...
31570     },
31571     
31572     onRegionResized : function(region, newSize){
31573         this.fireEvent("regionresized", region, newSize);
31574         this.layout();
31575     },
31576     
31577     onRegionCollapsed : function(region){
31578         this.fireEvent("regioncollapsed", region);
31579     },
31580     
31581     onRegionExpanded : function(region){
31582         this.fireEvent("regionexpanded", region);
31583     },
31584         
31585     /**
31586      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31587      * performs box-model adjustments.
31588      * @return {Object} The size as an object {width: (the width), height: (the height)}
31589      */
31590     getViewSize : function()
31591     {
31592         var size;
31593         if(this.el.dom != document.body){
31594             size = this.el.getSize();
31595         }else{
31596             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31597         }
31598         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31599         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31600         return size;
31601     },
31602     
31603     /**
31604      * Returns the Element this layout is bound to.
31605      * @return {Roo.Element}
31606      */
31607     getEl : function(){
31608         return this.el;
31609     },
31610     
31611     /**
31612      * Returns the specified region.
31613      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31614      * @return {Roo.LayoutRegion}
31615      */
31616     getRegion : function(target){
31617         return this.regions[target.toLowerCase()];
31618     },
31619     
31620     onWindowResize : function(){
31621         if(this.monitorWindowResize){
31622             this.layout();
31623         }
31624     }
31625 });/*
31626  * Based on:
31627  * Ext JS Library 1.1.1
31628  * Copyright(c) 2006-2007, Ext JS, LLC.
31629  *
31630  * Originally Released Under LGPL - original licence link has changed is not relivant.
31631  *
31632  * Fork - LGPL
31633  * <script type="text/javascript">
31634  */
31635 /**
31636  * @class Roo.bootstrap.layout.Border
31637  * @extends Roo.bootstrap.layout.Manager
31638  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31639  * please see: examples/bootstrap/nested.html<br><br>
31640  
31641 <b>The container the layout is rendered into can be either the body element or any other element.
31642 If it is not the body element, the container needs to either be an absolute positioned element,
31643 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31644 the container size if it is not the body element.</b>
31645
31646 * @constructor
31647 * Create a new Border
31648 * @param {Object} config Configuration options
31649  */
31650 Roo.bootstrap.layout.Border = function(config){
31651     config = config || {};
31652     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31653     
31654     
31655     
31656     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31657         if(config[region]){
31658             config[region].region = region;
31659             this.addRegion(config[region]);
31660         }
31661     },this);
31662     
31663 };
31664
31665 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31666
31667 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31668     /**
31669      * Creates and adds a new region if it doesn't already exist.
31670      * @param {String} target The target region key (north, south, east, west or center).
31671      * @param {Object} config The regions config object
31672      * @return {BorderLayoutRegion} The new region
31673      */
31674     addRegion : function(config)
31675     {
31676         if(!this.regions[config.region]){
31677             var r = this.factory(config);
31678             this.bindRegion(r);
31679         }
31680         return this.regions[config.region];
31681     },
31682
31683     // private (kinda)
31684     bindRegion : function(r){
31685         this.regions[r.config.region] = r;
31686         
31687         r.on("visibilitychange",    this.layout, this);
31688         r.on("paneladded",          this.layout, this);
31689         r.on("panelremoved",        this.layout, this);
31690         r.on("invalidated",         this.layout, this);
31691         r.on("resized",             this.onRegionResized, this);
31692         r.on("collapsed",           this.onRegionCollapsed, this);
31693         r.on("expanded",            this.onRegionExpanded, this);
31694     },
31695
31696     /**
31697      * Performs a layout update.
31698      */
31699     layout : function()
31700     {
31701         if(this.updating) {
31702             return;
31703         }
31704         var size = this.getViewSize();
31705         var w = size.width;
31706         var h = size.height;
31707         var centerW = w;
31708         var centerH = h;
31709         var centerY = 0;
31710         var centerX = 0;
31711         //var x = 0, y = 0;
31712
31713         var rs = this.regions;
31714         var north = rs["north"];
31715         var south = rs["south"]; 
31716         var west = rs["west"];
31717         var east = rs["east"];
31718         var center = rs["center"];
31719         //if(this.hideOnLayout){ // not supported anymore
31720             //c.el.setStyle("display", "none");
31721         //}
31722         if(north && north.isVisible()){
31723             var b = north.getBox();
31724             var m = north.getMargins();
31725             b.width = w - (m.left+m.right);
31726             b.x = m.left;
31727             b.y = m.top;
31728             centerY = b.height + b.y + m.bottom;
31729             centerH -= centerY;
31730             north.updateBox(this.safeBox(b));
31731         }
31732         if(south && south.isVisible()){
31733             var b = south.getBox();
31734             var m = south.getMargins();
31735             b.width = w - (m.left+m.right);
31736             b.x = m.left;
31737             var totalHeight = (b.height + m.top + m.bottom);
31738             b.y = h - totalHeight + m.top;
31739             centerH -= totalHeight;
31740             south.updateBox(this.safeBox(b));
31741         }
31742         if(west && west.isVisible()){
31743             var b = west.getBox();
31744             var m = west.getMargins();
31745             b.height = centerH - (m.top+m.bottom);
31746             b.x = m.left;
31747             b.y = centerY + m.top;
31748             var totalWidth = (b.width + m.left + m.right);
31749             centerX += totalWidth;
31750             centerW -= totalWidth;
31751             west.updateBox(this.safeBox(b));
31752         }
31753         if(east && east.isVisible()){
31754             var b = east.getBox();
31755             var m = east.getMargins();
31756             b.height = centerH - (m.top+m.bottom);
31757             var totalWidth = (b.width + m.left + m.right);
31758             b.x = w - totalWidth + m.left;
31759             b.y = centerY + m.top;
31760             centerW -= totalWidth;
31761             east.updateBox(this.safeBox(b));
31762         }
31763         if(center){
31764             var m = center.getMargins();
31765             var centerBox = {
31766                 x: centerX + m.left,
31767                 y: centerY + m.top,
31768                 width: centerW - (m.left+m.right),
31769                 height: centerH - (m.top+m.bottom)
31770             };
31771             //if(this.hideOnLayout){
31772                 //center.el.setStyle("display", "block");
31773             //}
31774             center.updateBox(this.safeBox(centerBox));
31775         }
31776         this.el.repaint();
31777         this.fireEvent("layout", this);
31778     },
31779
31780     // private
31781     safeBox : function(box){
31782         box.width = Math.max(0, box.width);
31783         box.height = Math.max(0, box.height);
31784         return box;
31785     },
31786
31787     /**
31788      * Adds a ContentPanel (or subclass) to this layout.
31789      * @param {String} target The target region key (north, south, east, west or center).
31790      * @param {Roo.ContentPanel} panel The panel to add
31791      * @return {Roo.ContentPanel} The added panel
31792      */
31793     add : function(target, panel){
31794          
31795         target = target.toLowerCase();
31796         return this.regions[target].add(panel);
31797     },
31798
31799     /**
31800      * Remove a ContentPanel (or subclass) to this layout.
31801      * @param {String} target The target region key (north, south, east, west or center).
31802      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31803      * @return {Roo.ContentPanel} The removed panel
31804      */
31805     remove : function(target, panel){
31806         target = target.toLowerCase();
31807         return this.regions[target].remove(panel);
31808     },
31809
31810     /**
31811      * Searches all regions for a panel with the specified id
31812      * @param {String} panelId
31813      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31814      */
31815     findPanel : function(panelId){
31816         var rs = this.regions;
31817         for(var target in rs){
31818             if(typeof rs[target] != "function"){
31819                 var p = rs[target].getPanel(panelId);
31820                 if(p){
31821                     return p;
31822                 }
31823             }
31824         }
31825         return null;
31826     },
31827
31828     /**
31829      * Searches all regions for a panel with the specified id and activates (shows) it.
31830      * @param {String/ContentPanel} panelId The panels id or the panel itself
31831      * @return {Roo.ContentPanel} The shown panel or null
31832      */
31833     showPanel : function(panelId) {
31834       var rs = this.regions;
31835       for(var target in rs){
31836          var r = rs[target];
31837          if(typeof r != "function"){
31838             if(r.hasPanel(panelId)){
31839                return r.showPanel(panelId);
31840             }
31841          }
31842       }
31843       return null;
31844    },
31845
31846    /**
31847      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31848      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31849      */
31850    /*
31851     restoreState : function(provider){
31852         if(!provider){
31853             provider = Roo.state.Manager;
31854         }
31855         var sm = new Roo.LayoutStateManager();
31856         sm.init(this, provider);
31857     },
31858 */
31859  
31860  
31861     /**
31862      * Adds a xtype elements to the layout.
31863      * <pre><code>
31864
31865 layout.addxtype({
31866        xtype : 'ContentPanel',
31867        region: 'west',
31868        items: [ .... ]
31869    }
31870 );
31871
31872 layout.addxtype({
31873         xtype : 'NestedLayoutPanel',
31874         region: 'west',
31875         layout: {
31876            center: { },
31877            west: { }   
31878         },
31879         items : [ ... list of content panels or nested layout panels.. ]
31880    }
31881 );
31882 </code></pre>
31883      * @param {Object} cfg Xtype definition of item to add.
31884      */
31885     addxtype : function(cfg)
31886     {
31887         // basically accepts a pannel...
31888         // can accept a layout region..!?!?
31889         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31890         
31891         
31892         // theory?  children can only be panels??
31893         
31894         //if (!cfg.xtype.match(/Panel$/)) {
31895         //    return false;
31896         //}
31897         var ret = false;
31898         
31899         if (typeof(cfg.region) == 'undefined') {
31900             Roo.log("Failed to add Panel, region was not set");
31901             Roo.log(cfg);
31902             return false;
31903         }
31904         var region = cfg.region;
31905         delete cfg.region;
31906         
31907           
31908         var xitems = [];
31909         if (cfg.items) {
31910             xitems = cfg.items;
31911             delete cfg.items;
31912         }
31913         var nb = false;
31914         
31915         switch(cfg.xtype) 
31916         {
31917             case 'Content':  // ContentPanel (el, cfg)
31918             case 'Scroll':  // ContentPanel (el, cfg)
31919             case 'View': 
31920                 cfg.autoCreate = true;
31921                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31922                 //} else {
31923                 //    var el = this.el.createChild();
31924                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31925                 //}
31926                 
31927                 this.add(region, ret);
31928                 break;
31929             
31930             /*
31931             case 'TreePanel': // our new panel!
31932                 cfg.el = this.el.createChild();
31933                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31934                 this.add(region, ret);
31935                 break;
31936             */
31937             
31938             case 'Nest': 
31939                 // create a new Layout (which is  a Border Layout...
31940                 
31941                 var clayout = cfg.layout;
31942                 clayout.el  = this.el.createChild();
31943                 clayout.items   = clayout.items  || [];
31944                 
31945                 delete cfg.layout;
31946                 
31947                 // replace this exitems with the clayout ones..
31948                 xitems = clayout.items;
31949                  
31950                 // force background off if it's in center...
31951                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31952                     cfg.background = false;
31953                 }
31954                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31955                 
31956                 
31957                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31958                 //console.log('adding nested layout panel '  + cfg.toSource());
31959                 this.add(region, ret);
31960                 nb = {}; /// find first...
31961                 break;
31962             
31963             case 'Grid':
31964                 
31965                 // needs grid and region
31966                 
31967                 //var el = this.getRegion(region).el.createChild();
31968                 /*
31969                  *var el = this.el.createChild();
31970                 // create the grid first...
31971                 cfg.grid.container = el;
31972                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31973                 */
31974                 
31975                 if (region == 'center' && this.active ) {
31976                     cfg.background = false;
31977                 }
31978                 
31979                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31980                 
31981                 this.add(region, ret);
31982                 /*
31983                 if (cfg.background) {
31984                     // render grid on panel activation (if panel background)
31985                     ret.on('activate', function(gp) {
31986                         if (!gp.grid.rendered) {
31987                     //        gp.grid.render(el);
31988                         }
31989                     });
31990                 } else {
31991                   //  cfg.grid.render(el);
31992                 }
31993                 */
31994                 break;
31995            
31996            
31997             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31998                 // it was the old xcomponent building that caused this before.
31999                 // espeically if border is the top element in the tree.
32000                 ret = this;
32001                 break; 
32002                 
32003                     
32004                 
32005                 
32006                 
32007             default:
32008                 /*
32009                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32010                     
32011                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32012                     this.add(region, ret);
32013                 } else {
32014                 */
32015                     Roo.log(cfg);
32016                     throw "Can not add '" + cfg.xtype + "' to Border";
32017                     return null;
32018              
32019                                 
32020              
32021         }
32022         this.beginUpdate();
32023         // add children..
32024         var region = '';
32025         var abn = {};
32026         Roo.each(xitems, function(i)  {
32027             region = nb && i.region ? i.region : false;
32028             
32029             var add = ret.addxtype(i);
32030            
32031             if (region) {
32032                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32033                 if (!i.background) {
32034                     abn[region] = nb[region] ;
32035                 }
32036             }
32037             
32038         });
32039         this.endUpdate();
32040
32041         // make the last non-background panel active..
32042         //if (nb) { Roo.log(abn); }
32043         if (nb) {
32044             
32045             for(var r in abn) {
32046                 region = this.getRegion(r);
32047                 if (region) {
32048                     // tried using nb[r], but it does not work..
32049                      
32050                     region.showPanel(abn[r]);
32051                    
32052                 }
32053             }
32054         }
32055         return ret;
32056         
32057     },
32058     
32059     
32060 // private
32061     factory : function(cfg)
32062     {
32063         
32064         var validRegions = Roo.bootstrap.layout.Border.regions;
32065
32066         var target = cfg.region;
32067         cfg.mgr = this;
32068         
32069         var r = Roo.bootstrap.layout;
32070         Roo.log(target);
32071         switch(target){
32072             case "north":
32073                 return new r.North(cfg);
32074             case "south":
32075                 return new r.South(cfg);
32076             case "east":
32077                 return new r.East(cfg);
32078             case "west":
32079                 return new r.West(cfg);
32080             case "center":
32081                 return new r.Center(cfg);
32082         }
32083         throw 'Layout region "'+target+'" not supported.';
32084     }
32085     
32086     
32087 });
32088  /*
32089  * Based on:
32090  * Ext JS Library 1.1.1
32091  * Copyright(c) 2006-2007, Ext JS, LLC.
32092  *
32093  * Originally Released Under LGPL - original licence link has changed is not relivant.
32094  *
32095  * Fork - LGPL
32096  * <script type="text/javascript">
32097  */
32098  
32099 /**
32100  * @class Roo.bootstrap.layout.Basic
32101  * @extends Roo.util.Observable
32102  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32103  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32104  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32105  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32106  * @cfg {string}   region  the region that it inhabits..
32107  * @cfg {bool}   skipConfig skip config?
32108  * 
32109
32110  */
32111 Roo.bootstrap.layout.Basic = function(config){
32112     
32113     this.mgr = config.mgr;
32114     
32115     this.position = config.region;
32116     
32117     var skipConfig = config.skipConfig;
32118     
32119     this.events = {
32120         /**
32121          * @scope Roo.BasicLayoutRegion
32122          */
32123         
32124         /**
32125          * @event beforeremove
32126          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32127          * @param {Roo.LayoutRegion} this
32128          * @param {Roo.ContentPanel} panel The panel
32129          * @param {Object} e The cancel event object
32130          */
32131         "beforeremove" : true,
32132         /**
32133          * @event invalidated
32134          * Fires when the layout for this region is changed.
32135          * @param {Roo.LayoutRegion} this
32136          */
32137         "invalidated" : true,
32138         /**
32139          * @event visibilitychange
32140          * Fires when this region is shown or hidden 
32141          * @param {Roo.LayoutRegion} this
32142          * @param {Boolean} visibility true or false
32143          */
32144         "visibilitychange" : true,
32145         /**
32146          * @event paneladded
32147          * Fires when a panel is added. 
32148          * @param {Roo.LayoutRegion} this
32149          * @param {Roo.ContentPanel} panel The panel
32150          */
32151         "paneladded" : true,
32152         /**
32153          * @event panelremoved
32154          * Fires when a panel is removed. 
32155          * @param {Roo.LayoutRegion} this
32156          * @param {Roo.ContentPanel} panel The panel
32157          */
32158         "panelremoved" : true,
32159         /**
32160          * @event beforecollapse
32161          * Fires when this region before collapse.
32162          * @param {Roo.LayoutRegion} this
32163          */
32164         "beforecollapse" : true,
32165         /**
32166          * @event collapsed
32167          * Fires when this region is collapsed.
32168          * @param {Roo.LayoutRegion} this
32169          */
32170         "collapsed" : true,
32171         /**
32172          * @event expanded
32173          * Fires when this region is expanded.
32174          * @param {Roo.LayoutRegion} this
32175          */
32176         "expanded" : true,
32177         /**
32178          * @event slideshow
32179          * Fires when this region is slid into view.
32180          * @param {Roo.LayoutRegion} this
32181          */
32182         "slideshow" : true,
32183         /**
32184          * @event slidehide
32185          * Fires when this region slides out of view. 
32186          * @param {Roo.LayoutRegion} this
32187          */
32188         "slidehide" : true,
32189         /**
32190          * @event panelactivated
32191          * Fires when a panel is activated. 
32192          * @param {Roo.LayoutRegion} this
32193          * @param {Roo.ContentPanel} panel The activated panel
32194          */
32195         "panelactivated" : true,
32196         /**
32197          * @event resized
32198          * Fires when the user resizes this region. 
32199          * @param {Roo.LayoutRegion} this
32200          * @param {Number} newSize The new size (width for east/west, height for north/south)
32201          */
32202         "resized" : true
32203     };
32204     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32205     this.panels = new Roo.util.MixedCollection();
32206     this.panels.getKey = this.getPanelId.createDelegate(this);
32207     this.box = null;
32208     this.activePanel = null;
32209     // ensure listeners are added...
32210     
32211     if (config.listeners || config.events) {
32212         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32213             listeners : config.listeners || {},
32214             events : config.events || {}
32215         });
32216     }
32217     
32218     if(skipConfig !== true){
32219         this.applyConfig(config);
32220     }
32221 };
32222
32223 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32224 {
32225     getPanelId : function(p){
32226         return p.getId();
32227     },
32228     
32229     applyConfig : function(config){
32230         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32231         this.config = config;
32232         
32233     },
32234     
32235     /**
32236      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32237      * the width, for horizontal (north, south) the height.
32238      * @param {Number} newSize The new width or height
32239      */
32240     resizeTo : function(newSize){
32241         var el = this.el ? this.el :
32242                  (this.activePanel ? this.activePanel.getEl() : null);
32243         if(el){
32244             switch(this.position){
32245                 case "east":
32246                 case "west":
32247                     el.setWidth(newSize);
32248                     this.fireEvent("resized", this, newSize);
32249                 break;
32250                 case "north":
32251                 case "south":
32252                     el.setHeight(newSize);
32253                     this.fireEvent("resized", this, newSize);
32254                 break;                
32255             }
32256         }
32257     },
32258     
32259     getBox : function(){
32260         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32261     },
32262     
32263     getMargins : function(){
32264         return this.margins;
32265     },
32266     
32267     updateBox : function(box){
32268         this.box = box;
32269         var el = this.activePanel.getEl();
32270         el.dom.style.left = box.x + "px";
32271         el.dom.style.top = box.y + "px";
32272         this.activePanel.setSize(box.width, box.height);
32273     },
32274     
32275     /**
32276      * Returns the container element for this region.
32277      * @return {Roo.Element}
32278      */
32279     getEl : function(){
32280         return this.activePanel;
32281     },
32282     
32283     /**
32284      * Returns true if this region is currently visible.
32285      * @return {Boolean}
32286      */
32287     isVisible : function(){
32288         return this.activePanel ? true : false;
32289     },
32290     
32291     setActivePanel : function(panel){
32292         panel = this.getPanel(panel);
32293         if(this.activePanel && this.activePanel != panel){
32294             this.activePanel.setActiveState(false);
32295             this.activePanel.getEl().setLeftTop(-10000,-10000);
32296         }
32297         this.activePanel = panel;
32298         panel.setActiveState(true);
32299         if(this.box){
32300             panel.setSize(this.box.width, this.box.height);
32301         }
32302         this.fireEvent("panelactivated", this, panel);
32303         this.fireEvent("invalidated");
32304     },
32305     
32306     /**
32307      * Show the specified panel.
32308      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32309      * @return {Roo.ContentPanel} The shown panel or null
32310      */
32311     showPanel : function(panel){
32312         panel = this.getPanel(panel);
32313         if(panel){
32314             this.setActivePanel(panel);
32315         }
32316         return panel;
32317     },
32318     
32319     /**
32320      * Get the active panel for this region.
32321      * @return {Roo.ContentPanel} The active panel or null
32322      */
32323     getActivePanel : function(){
32324         return this.activePanel;
32325     },
32326     
32327     /**
32328      * Add the passed ContentPanel(s)
32329      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32330      * @return {Roo.ContentPanel} The panel added (if only one was added)
32331      */
32332     add : function(panel){
32333         if(arguments.length > 1){
32334             for(var i = 0, len = arguments.length; i < len; i++) {
32335                 this.add(arguments[i]);
32336             }
32337             return null;
32338         }
32339         if(this.hasPanel(panel)){
32340             this.showPanel(panel);
32341             return panel;
32342         }
32343         var el = panel.getEl();
32344         if(el.dom.parentNode != this.mgr.el.dom){
32345             this.mgr.el.dom.appendChild(el.dom);
32346         }
32347         if(panel.setRegion){
32348             panel.setRegion(this);
32349         }
32350         this.panels.add(panel);
32351         el.setStyle("position", "absolute");
32352         if(!panel.background){
32353             this.setActivePanel(panel);
32354             if(this.config.initialSize && this.panels.getCount()==1){
32355                 this.resizeTo(this.config.initialSize);
32356             }
32357         }
32358         this.fireEvent("paneladded", this, panel);
32359         return panel;
32360     },
32361     
32362     /**
32363      * Returns true if the panel is in this region.
32364      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32365      * @return {Boolean}
32366      */
32367     hasPanel : function(panel){
32368         if(typeof panel == "object"){ // must be panel obj
32369             panel = panel.getId();
32370         }
32371         return this.getPanel(panel) ? true : false;
32372     },
32373     
32374     /**
32375      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32376      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32377      * @param {Boolean} preservePanel Overrides the config preservePanel option
32378      * @return {Roo.ContentPanel} The panel that was removed
32379      */
32380     remove : function(panel, preservePanel){
32381         panel = this.getPanel(panel);
32382         if(!panel){
32383             return null;
32384         }
32385         var e = {};
32386         this.fireEvent("beforeremove", this, panel, e);
32387         if(e.cancel === true){
32388             return null;
32389         }
32390         var panelId = panel.getId();
32391         this.panels.removeKey(panelId);
32392         return panel;
32393     },
32394     
32395     /**
32396      * Returns the panel specified or null if it's not in this region.
32397      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32398      * @return {Roo.ContentPanel}
32399      */
32400     getPanel : function(id){
32401         if(typeof id == "object"){ // must be panel obj
32402             return id;
32403         }
32404         return this.panels.get(id);
32405     },
32406     
32407     /**
32408      * Returns this regions position (north/south/east/west/center).
32409      * @return {String} 
32410      */
32411     getPosition: function(){
32412         return this.position;    
32413     }
32414 });/*
32415  * Based on:
32416  * Ext JS Library 1.1.1
32417  * Copyright(c) 2006-2007, Ext JS, LLC.
32418  *
32419  * Originally Released Under LGPL - original licence link has changed is not relivant.
32420  *
32421  * Fork - LGPL
32422  * <script type="text/javascript">
32423  */
32424  
32425 /**
32426  * @class Roo.bootstrap.layout.Region
32427  * @extends Roo.bootstrap.layout.Basic
32428  * This class represents a region in a layout manager.
32429  
32430  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32431  * @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})
32432  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32433  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32434  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32435  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32436  * @cfg {String}    title           The title for the region (overrides panel titles)
32437  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32438  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32439  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32440  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32441  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32442  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32443  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32444  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32445  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32446  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32447
32448  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32449  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32450  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32451  * @cfg {Number}    width           For East/West panels
32452  * @cfg {Number}    height          For North/South panels
32453  * @cfg {Boolean}   split           To show the splitter
32454  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32455  * 
32456  * @cfg {string}   cls             Extra CSS classes to add to region
32457  * 
32458  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32459  * @cfg {string}   region  the region that it inhabits..
32460  *
32461
32462  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32463  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32464
32465  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32466  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32467  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32468  */
32469 Roo.bootstrap.layout.Region = function(config)
32470 {
32471     this.applyConfig(config);
32472
32473     var mgr = config.mgr;
32474     var pos = config.region;
32475     config.skipConfig = true;
32476     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32477     
32478     if (mgr.el) {
32479         this.onRender(mgr.el);   
32480     }
32481      
32482     this.visible = true;
32483     this.collapsed = false;
32484 };
32485
32486 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32487
32488     position: '', // set by wrapper (eg. north/south etc..)
32489
32490     createBody : function(){
32491         /** This region's body element 
32492         * @type Roo.Element */
32493         this.bodyEl = this.el.createChild({
32494                 tag: "div",
32495                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32496         });
32497     },
32498
32499     onRender: function(ctr, pos)
32500     {
32501         var dh = Roo.DomHelper;
32502         /** This region's container element 
32503         * @type Roo.Element */
32504         this.el = dh.append(ctr.dom, {
32505                 tag: "div",
32506                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32507             }, true);
32508         /** This region's title element 
32509         * @type Roo.Element */
32510     
32511         this.titleEl = dh.append(this.el.dom,
32512             {
32513                     tag: "div",
32514                     unselectable: "on",
32515                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32516                     children:[
32517                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32518                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32519                     ]}, true);
32520         
32521         this.titleEl.enableDisplayMode();
32522         /** This region's title text element 
32523         * @type HTMLElement */
32524         this.titleTextEl = this.titleEl.dom.firstChild;
32525         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32526         /*
32527         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32528         this.closeBtn.enableDisplayMode();
32529         this.closeBtn.on("click", this.closeClicked, this);
32530         this.closeBtn.hide();
32531     */
32532         this.createBody(this.config);
32533         if(this.config.hideWhenEmpty){
32534             this.hide();
32535             this.on("paneladded", this.validateVisibility, this);
32536             this.on("panelremoved", this.validateVisibility, this);
32537         }
32538         if(this.autoScroll){
32539             this.bodyEl.setStyle("overflow", "auto");
32540         }else{
32541             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32542         }
32543         //if(c.titlebar !== false){
32544             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32545                 this.titleEl.hide();
32546             }else{
32547                 this.titleEl.show();
32548                 if(this.config.title){
32549                     this.titleTextEl.innerHTML = this.config.title;
32550                 }
32551             }
32552         //}
32553         if(this.config.collapsed){
32554             this.collapse(true);
32555         }
32556         if(this.config.hidden){
32557             this.hide();
32558         }
32559     },
32560     
32561     applyConfig : function(c)
32562     {
32563         /*
32564          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32565             var dh = Roo.DomHelper;
32566             if(c.titlebar !== false){
32567                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32568                 this.collapseBtn.on("click", this.collapse, this);
32569                 this.collapseBtn.enableDisplayMode();
32570                 /*
32571                 if(c.showPin === true || this.showPin){
32572                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32573                     this.stickBtn.enableDisplayMode();
32574                     this.stickBtn.on("click", this.expand, this);
32575                     this.stickBtn.hide();
32576                 }
32577                 
32578             }
32579             */
32580             /** This region's collapsed element
32581             * @type Roo.Element */
32582             /*
32583              *
32584             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32585                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32586             ]}, true);
32587             
32588             if(c.floatable !== false){
32589                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32590                this.collapsedEl.on("click", this.collapseClick, this);
32591             }
32592
32593             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32594                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32595                    id: "message", unselectable: "on", style:{"float":"left"}});
32596                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32597              }
32598             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32599             this.expandBtn.on("click", this.expand, this);
32600             
32601         }
32602         
32603         if(this.collapseBtn){
32604             this.collapseBtn.setVisible(c.collapsible == true);
32605         }
32606         
32607         this.cmargins = c.cmargins || this.cmargins ||
32608                          (this.position == "west" || this.position == "east" ?
32609                              {top: 0, left: 2, right:2, bottom: 0} :
32610                              {top: 2, left: 0, right:0, bottom: 2});
32611         */
32612         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32613         
32614         
32615         this.bottomTabs = c.tabPosition != "top";
32616         
32617         this.autoScroll = c.autoScroll || false;
32618         
32619         
32620        
32621         
32622         this.duration = c.duration || .30;
32623         this.slideDuration = c.slideDuration || .45;
32624         this.config = c;
32625        
32626     },
32627     /**
32628      * Returns true if this region is currently visible.
32629      * @return {Boolean}
32630      */
32631     isVisible : function(){
32632         return this.visible;
32633     },
32634
32635     /**
32636      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32637      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32638      */
32639     //setCollapsedTitle : function(title){
32640     //    title = title || "&#160;";
32641      //   if(this.collapsedTitleTextEl){
32642       //      this.collapsedTitleTextEl.innerHTML = title;
32643        // }
32644     //},
32645
32646     getBox : function(){
32647         var b;
32648       //  if(!this.collapsed){
32649             b = this.el.getBox(false, true);
32650        // }else{
32651           //  b = this.collapsedEl.getBox(false, true);
32652         //}
32653         return b;
32654     },
32655
32656     getMargins : function(){
32657         return this.margins;
32658         //return this.collapsed ? this.cmargins : this.margins;
32659     },
32660 /*
32661     highlight : function(){
32662         this.el.addClass("x-layout-panel-dragover");
32663     },
32664
32665     unhighlight : function(){
32666         this.el.removeClass("x-layout-panel-dragover");
32667     },
32668 */
32669     updateBox : function(box)
32670     {
32671         this.box = box;
32672         if(!this.collapsed){
32673             this.el.dom.style.left = box.x + "px";
32674             this.el.dom.style.top = box.y + "px";
32675             this.updateBody(box.width, box.height);
32676         }else{
32677             this.collapsedEl.dom.style.left = box.x + "px";
32678             this.collapsedEl.dom.style.top = box.y + "px";
32679             this.collapsedEl.setSize(box.width, box.height);
32680         }
32681         if(this.tabs){
32682             this.tabs.autoSizeTabs();
32683         }
32684     },
32685
32686     updateBody : function(w, h)
32687     {
32688         if(w !== null){
32689             this.el.setWidth(w);
32690             w -= this.el.getBorderWidth("rl");
32691             if(this.config.adjustments){
32692                 w += this.config.adjustments[0];
32693             }
32694         }
32695         if(h !== null){
32696             this.el.setHeight(h);
32697             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32698             h -= this.el.getBorderWidth("tb");
32699             if(this.config.adjustments){
32700                 h += this.config.adjustments[1];
32701             }
32702             this.bodyEl.setHeight(h);
32703             if(this.tabs){
32704                 h = this.tabs.syncHeight(h);
32705             }
32706         }
32707         if(this.panelSize){
32708             w = w !== null ? w : this.panelSize.width;
32709             h = h !== null ? h : this.panelSize.height;
32710         }
32711         if(this.activePanel){
32712             var el = this.activePanel.getEl();
32713             w = w !== null ? w : el.getWidth();
32714             h = h !== null ? h : el.getHeight();
32715             this.panelSize = {width: w, height: h};
32716             this.activePanel.setSize(w, h);
32717         }
32718         if(Roo.isIE && this.tabs){
32719             this.tabs.el.repaint();
32720         }
32721     },
32722
32723     /**
32724      * Returns the container element for this region.
32725      * @return {Roo.Element}
32726      */
32727     getEl : function(){
32728         return this.el;
32729     },
32730
32731     /**
32732      * Hides this region.
32733      */
32734     hide : function(){
32735         //if(!this.collapsed){
32736             this.el.dom.style.left = "-2000px";
32737             this.el.hide();
32738         //}else{
32739          //   this.collapsedEl.dom.style.left = "-2000px";
32740          //   this.collapsedEl.hide();
32741        // }
32742         this.visible = false;
32743         this.fireEvent("visibilitychange", this, false);
32744     },
32745
32746     /**
32747      * Shows this region if it was previously hidden.
32748      */
32749     show : function(){
32750         //if(!this.collapsed){
32751             this.el.show();
32752         //}else{
32753         //    this.collapsedEl.show();
32754        // }
32755         this.visible = true;
32756         this.fireEvent("visibilitychange", this, true);
32757     },
32758 /*
32759     closeClicked : function(){
32760         if(this.activePanel){
32761             this.remove(this.activePanel);
32762         }
32763     },
32764
32765     collapseClick : function(e){
32766         if(this.isSlid){
32767            e.stopPropagation();
32768            this.slideIn();
32769         }else{
32770            e.stopPropagation();
32771            this.slideOut();
32772         }
32773     },
32774 */
32775     /**
32776      * Collapses this region.
32777      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32778      */
32779     /*
32780     collapse : function(skipAnim, skipCheck = false){
32781         if(this.collapsed) {
32782             return;
32783         }
32784         
32785         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32786             
32787             this.collapsed = true;
32788             if(this.split){
32789                 this.split.el.hide();
32790             }
32791             if(this.config.animate && skipAnim !== true){
32792                 this.fireEvent("invalidated", this);
32793                 this.animateCollapse();
32794             }else{
32795                 this.el.setLocation(-20000,-20000);
32796                 this.el.hide();
32797                 this.collapsedEl.show();
32798                 this.fireEvent("collapsed", this);
32799                 this.fireEvent("invalidated", this);
32800             }
32801         }
32802         
32803     },
32804 */
32805     animateCollapse : function(){
32806         // overridden
32807     },
32808
32809     /**
32810      * Expands this region if it was previously collapsed.
32811      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32812      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32813      */
32814     /*
32815     expand : function(e, skipAnim){
32816         if(e) {
32817             e.stopPropagation();
32818         }
32819         if(!this.collapsed || this.el.hasActiveFx()) {
32820             return;
32821         }
32822         if(this.isSlid){
32823             this.afterSlideIn();
32824             skipAnim = true;
32825         }
32826         this.collapsed = false;
32827         if(this.config.animate && skipAnim !== true){
32828             this.animateExpand();
32829         }else{
32830             this.el.show();
32831             if(this.split){
32832                 this.split.el.show();
32833             }
32834             this.collapsedEl.setLocation(-2000,-2000);
32835             this.collapsedEl.hide();
32836             this.fireEvent("invalidated", this);
32837             this.fireEvent("expanded", this);
32838         }
32839     },
32840 */
32841     animateExpand : function(){
32842         // overridden
32843     },
32844
32845     initTabs : function()
32846     {
32847         this.bodyEl.setStyle("overflow", "hidden");
32848         var ts = new Roo.bootstrap.panel.Tabs({
32849                 el: this.bodyEl.dom,
32850                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32851                 disableTooltips: this.config.disableTabTips,
32852                 toolbar : this.config.toolbar
32853             });
32854         
32855         if(this.config.hideTabs){
32856             ts.stripWrap.setDisplayed(false);
32857         }
32858         this.tabs = ts;
32859         ts.resizeTabs = this.config.resizeTabs === true;
32860         ts.minTabWidth = this.config.minTabWidth || 40;
32861         ts.maxTabWidth = this.config.maxTabWidth || 250;
32862         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32863         ts.monitorResize = false;
32864         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32865         ts.bodyEl.addClass('roo-layout-tabs-body');
32866         this.panels.each(this.initPanelAsTab, this);
32867     },
32868
32869     initPanelAsTab : function(panel){
32870         var ti = this.tabs.addTab(
32871                     panel.getEl().id,
32872                     panel.getTitle(), null,
32873                     this.config.closeOnTab && panel.isClosable()
32874             );
32875         if(panel.tabTip !== undefined){
32876             ti.setTooltip(panel.tabTip);
32877         }
32878         ti.on("activate", function(){
32879               this.setActivePanel(panel);
32880         }, this);
32881         
32882         if(this.config.closeOnTab){
32883             ti.on("beforeclose", function(t, e){
32884                 e.cancel = true;
32885                 this.remove(panel);
32886             }, this);
32887         }
32888         return ti;
32889     },
32890
32891     updatePanelTitle : function(panel, title)
32892     {
32893         if(this.activePanel == panel){
32894             this.updateTitle(title);
32895         }
32896         if(this.tabs){
32897             var ti = this.tabs.getTab(panel.getEl().id);
32898             ti.setText(title);
32899             if(panel.tabTip !== undefined){
32900                 ti.setTooltip(panel.tabTip);
32901             }
32902         }
32903     },
32904
32905     updateTitle : function(title){
32906         if(this.titleTextEl && !this.config.title){
32907             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32908         }
32909     },
32910
32911     setActivePanel : function(panel)
32912     {
32913         panel = this.getPanel(panel);
32914         if(this.activePanel && this.activePanel != panel){
32915             this.activePanel.setActiveState(false);
32916         }
32917         this.activePanel = panel;
32918         panel.setActiveState(true);
32919         if(this.panelSize){
32920             panel.setSize(this.panelSize.width, this.panelSize.height);
32921         }
32922         if(this.closeBtn){
32923             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32924         }
32925         this.updateTitle(panel.getTitle());
32926         if(this.tabs){
32927             this.fireEvent("invalidated", this);
32928         }
32929         this.fireEvent("panelactivated", this, panel);
32930     },
32931
32932     /**
32933      * Shows the specified panel.
32934      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32935      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32936      */
32937     showPanel : function(panel)
32938     {
32939         panel = this.getPanel(panel);
32940         if(panel){
32941             if(this.tabs){
32942                 var tab = this.tabs.getTab(panel.getEl().id);
32943                 if(tab.isHidden()){
32944                     this.tabs.unhideTab(tab.id);
32945                 }
32946                 tab.activate();
32947             }else{
32948                 this.setActivePanel(panel);
32949             }
32950         }
32951         return panel;
32952     },
32953
32954     /**
32955      * Get the active panel for this region.
32956      * @return {Roo.ContentPanel} The active panel or null
32957      */
32958     getActivePanel : function(){
32959         return this.activePanel;
32960     },
32961
32962     validateVisibility : function(){
32963         if(this.panels.getCount() < 1){
32964             this.updateTitle("&#160;");
32965             this.closeBtn.hide();
32966             this.hide();
32967         }else{
32968             if(!this.isVisible()){
32969                 this.show();
32970             }
32971         }
32972     },
32973
32974     /**
32975      * Adds the passed ContentPanel(s) to this region.
32976      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32977      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32978      */
32979     add : function(panel){
32980         if(arguments.length > 1){
32981             for(var i = 0, len = arguments.length; i < len; i++) {
32982                 this.add(arguments[i]);
32983             }
32984             return null;
32985         }
32986         if(this.hasPanel(panel)){
32987             this.showPanel(panel);
32988             return panel;
32989         }
32990         panel.setRegion(this);
32991         this.panels.add(panel);
32992         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32993             this.bodyEl.dom.appendChild(panel.getEl().dom);
32994             if(panel.background !== true){
32995                 this.setActivePanel(panel);
32996             }
32997             this.fireEvent("paneladded", this, panel);
32998             return panel;
32999         }
33000         if(!this.tabs){
33001             this.initTabs();
33002         }else{
33003             this.initPanelAsTab(panel);
33004         }
33005         
33006         
33007         if(panel.background !== true){
33008             this.tabs.activate(panel.getEl().id);
33009         }
33010         this.fireEvent("paneladded", this, panel);
33011         return panel;
33012     },
33013
33014     /**
33015      * Hides the tab for the specified panel.
33016      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33017      */
33018     hidePanel : function(panel){
33019         if(this.tabs && (panel = this.getPanel(panel))){
33020             this.tabs.hideTab(panel.getEl().id);
33021         }
33022     },
33023
33024     /**
33025      * Unhides the tab for a previously hidden panel.
33026      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33027      */
33028     unhidePanel : function(panel){
33029         if(this.tabs && (panel = this.getPanel(panel))){
33030             this.tabs.unhideTab(panel.getEl().id);
33031         }
33032     },
33033
33034     clearPanels : function(){
33035         while(this.panels.getCount() > 0){
33036              this.remove(this.panels.first());
33037         }
33038     },
33039
33040     /**
33041      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33042      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33043      * @param {Boolean} preservePanel Overrides the config preservePanel option
33044      * @return {Roo.ContentPanel} The panel that was removed
33045      */
33046     remove : function(panel, preservePanel)
33047     {
33048         panel = this.getPanel(panel);
33049         if(!panel){
33050             return null;
33051         }
33052         var e = {};
33053         this.fireEvent("beforeremove", this, panel, e);
33054         if(e.cancel === true){
33055             return null;
33056         }
33057         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33058         var panelId = panel.getId();
33059         this.panels.removeKey(panelId);
33060         if(preservePanel){
33061             document.body.appendChild(panel.getEl().dom);
33062         }
33063         if(this.tabs){
33064             this.tabs.removeTab(panel.getEl().id);
33065         }else if (!preservePanel){
33066             this.bodyEl.dom.removeChild(panel.getEl().dom);
33067         }
33068         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33069             var p = this.panels.first();
33070             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33071             tempEl.appendChild(p.getEl().dom);
33072             this.bodyEl.update("");
33073             this.bodyEl.dom.appendChild(p.getEl().dom);
33074             tempEl = null;
33075             this.updateTitle(p.getTitle());
33076             this.tabs = null;
33077             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33078             this.setActivePanel(p);
33079         }
33080         panel.setRegion(null);
33081         if(this.activePanel == panel){
33082             this.activePanel = null;
33083         }
33084         if(this.config.autoDestroy !== false && preservePanel !== true){
33085             try{panel.destroy();}catch(e){}
33086         }
33087         this.fireEvent("panelremoved", this, panel);
33088         return panel;
33089     },
33090
33091     /**
33092      * Returns the TabPanel component used by this region
33093      * @return {Roo.TabPanel}
33094      */
33095     getTabs : function(){
33096         return this.tabs;
33097     },
33098
33099     createTool : function(parentEl, className){
33100         var btn = Roo.DomHelper.append(parentEl, {
33101             tag: "div",
33102             cls: "x-layout-tools-button",
33103             children: [ {
33104                 tag: "div",
33105                 cls: "roo-layout-tools-button-inner " + className,
33106                 html: "&#160;"
33107             }]
33108         }, true);
33109         btn.addClassOnOver("roo-layout-tools-button-over");
33110         return btn;
33111     }
33112 });/*
33113  * Based on:
33114  * Ext JS Library 1.1.1
33115  * Copyright(c) 2006-2007, Ext JS, LLC.
33116  *
33117  * Originally Released Under LGPL - original licence link has changed is not relivant.
33118  *
33119  * Fork - LGPL
33120  * <script type="text/javascript">
33121  */
33122  
33123
33124
33125 /**
33126  * @class Roo.SplitLayoutRegion
33127  * @extends Roo.LayoutRegion
33128  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33129  */
33130 Roo.bootstrap.layout.Split = function(config){
33131     this.cursor = config.cursor;
33132     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33133 };
33134
33135 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33136 {
33137     splitTip : "Drag to resize.",
33138     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33139     useSplitTips : false,
33140
33141     applyConfig : function(config){
33142         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33143     },
33144     
33145     onRender : function(ctr,pos) {
33146         
33147         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33148         if(!this.config.split){
33149             return;
33150         }
33151         if(!this.split){
33152             
33153             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33154                             tag: "div",
33155                             id: this.el.id + "-split",
33156                             cls: "roo-layout-split roo-layout-split-"+this.position,
33157                             html: "&#160;"
33158             });
33159             /** The SplitBar for this region 
33160             * @type Roo.SplitBar */
33161             // does not exist yet...
33162             Roo.log([this.position, this.orientation]);
33163             
33164             this.split = new Roo.bootstrap.SplitBar({
33165                 dragElement : splitEl,
33166                 resizingElement: this.el,
33167                 orientation : this.orientation
33168             });
33169             
33170             this.split.on("moved", this.onSplitMove, this);
33171             this.split.useShim = this.config.useShim === true;
33172             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33173             if(this.useSplitTips){
33174                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33175             }
33176             //if(config.collapsible){
33177             //    this.split.el.on("dblclick", this.collapse,  this);
33178             //}
33179         }
33180         if(typeof this.config.minSize != "undefined"){
33181             this.split.minSize = this.config.minSize;
33182         }
33183         if(typeof this.config.maxSize != "undefined"){
33184             this.split.maxSize = this.config.maxSize;
33185         }
33186         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33187             this.hideSplitter();
33188         }
33189         
33190     },
33191
33192     getHMaxSize : function(){
33193          var cmax = this.config.maxSize || 10000;
33194          var center = this.mgr.getRegion("center");
33195          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33196     },
33197
33198     getVMaxSize : function(){
33199          var cmax = this.config.maxSize || 10000;
33200          var center = this.mgr.getRegion("center");
33201          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33202     },
33203
33204     onSplitMove : function(split, newSize){
33205         this.fireEvent("resized", this, newSize);
33206     },
33207     
33208     /** 
33209      * Returns the {@link Roo.SplitBar} for this region.
33210      * @return {Roo.SplitBar}
33211      */
33212     getSplitBar : function(){
33213         return this.split;
33214     },
33215     
33216     hide : function(){
33217         this.hideSplitter();
33218         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33219     },
33220
33221     hideSplitter : function(){
33222         if(this.split){
33223             this.split.el.setLocation(-2000,-2000);
33224             this.split.el.hide();
33225         }
33226     },
33227
33228     show : function(){
33229         if(this.split){
33230             this.split.el.show();
33231         }
33232         Roo.bootstrap.layout.Split.superclass.show.call(this);
33233     },
33234     
33235     beforeSlide: function(){
33236         if(Roo.isGecko){// firefox overflow auto bug workaround
33237             this.bodyEl.clip();
33238             if(this.tabs) {
33239                 this.tabs.bodyEl.clip();
33240             }
33241             if(this.activePanel){
33242                 this.activePanel.getEl().clip();
33243                 
33244                 if(this.activePanel.beforeSlide){
33245                     this.activePanel.beforeSlide();
33246                 }
33247             }
33248         }
33249     },
33250     
33251     afterSlide : function(){
33252         if(Roo.isGecko){// firefox overflow auto bug workaround
33253             this.bodyEl.unclip();
33254             if(this.tabs) {
33255                 this.tabs.bodyEl.unclip();
33256             }
33257             if(this.activePanel){
33258                 this.activePanel.getEl().unclip();
33259                 if(this.activePanel.afterSlide){
33260                     this.activePanel.afterSlide();
33261                 }
33262             }
33263         }
33264     },
33265
33266     initAutoHide : function(){
33267         if(this.autoHide !== false){
33268             if(!this.autoHideHd){
33269                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33270                 this.autoHideHd = {
33271                     "mouseout": function(e){
33272                         if(!e.within(this.el, true)){
33273                             st.delay(500);
33274                         }
33275                     },
33276                     "mouseover" : function(e){
33277                         st.cancel();
33278                     },
33279                     scope : this
33280                 };
33281             }
33282             this.el.on(this.autoHideHd);
33283         }
33284     },
33285
33286     clearAutoHide : function(){
33287         if(this.autoHide !== false){
33288             this.el.un("mouseout", this.autoHideHd.mouseout);
33289             this.el.un("mouseover", this.autoHideHd.mouseover);
33290         }
33291     },
33292
33293     clearMonitor : function(){
33294         Roo.get(document).un("click", this.slideInIf, this);
33295     },
33296
33297     // these names are backwards but not changed for compat
33298     slideOut : function(){
33299         if(this.isSlid || this.el.hasActiveFx()){
33300             return;
33301         }
33302         this.isSlid = true;
33303         if(this.collapseBtn){
33304             this.collapseBtn.hide();
33305         }
33306         this.closeBtnState = this.closeBtn.getStyle('display');
33307         this.closeBtn.hide();
33308         if(this.stickBtn){
33309             this.stickBtn.show();
33310         }
33311         this.el.show();
33312         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33313         this.beforeSlide();
33314         this.el.setStyle("z-index", 10001);
33315         this.el.slideIn(this.getSlideAnchor(), {
33316             callback: function(){
33317                 this.afterSlide();
33318                 this.initAutoHide();
33319                 Roo.get(document).on("click", this.slideInIf, this);
33320                 this.fireEvent("slideshow", this);
33321             },
33322             scope: this,
33323             block: true
33324         });
33325     },
33326
33327     afterSlideIn : function(){
33328         this.clearAutoHide();
33329         this.isSlid = false;
33330         this.clearMonitor();
33331         this.el.setStyle("z-index", "");
33332         if(this.collapseBtn){
33333             this.collapseBtn.show();
33334         }
33335         this.closeBtn.setStyle('display', this.closeBtnState);
33336         if(this.stickBtn){
33337             this.stickBtn.hide();
33338         }
33339         this.fireEvent("slidehide", this);
33340     },
33341
33342     slideIn : function(cb){
33343         if(!this.isSlid || this.el.hasActiveFx()){
33344             Roo.callback(cb);
33345             return;
33346         }
33347         this.isSlid = false;
33348         this.beforeSlide();
33349         this.el.slideOut(this.getSlideAnchor(), {
33350             callback: function(){
33351                 this.el.setLeftTop(-10000, -10000);
33352                 this.afterSlide();
33353                 this.afterSlideIn();
33354                 Roo.callback(cb);
33355             },
33356             scope: this,
33357             block: true
33358         });
33359     },
33360     
33361     slideInIf : function(e){
33362         if(!e.within(this.el)){
33363             this.slideIn();
33364         }
33365     },
33366
33367     animateCollapse : function(){
33368         this.beforeSlide();
33369         this.el.setStyle("z-index", 20000);
33370         var anchor = this.getSlideAnchor();
33371         this.el.slideOut(anchor, {
33372             callback : function(){
33373                 this.el.setStyle("z-index", "");
33374                 this.collapsedEl.slideIn(anchor, {duration:.3});
33375                 this.afterSlide();
33376                 this.el.setLocation(-10000,-10000);
33377                 this.el.hide();
33378                 this.fireEvent("collapsed", this);
33379             },
33380             scope: this,
33381             block: true
33382         });
33383     },
33384
33385     animateExpand : function(){
33386         this.beforeSlide();
33387         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33388         this.el.setStyle("z-index", 20000);
33389         this.collapsedEl.hide({
33390             duration:.1
33391         });
33392         this.el.slideIn(this.getSlideAnchor(), {
33393             callback : function(){
33394                 this.el.setStyle("z-index", "");
33395                 this.afterSlide();
33396                 if(this.split){
33397                     this.split.el.show();
33398                 }
33399                 this.fireEvent("invalidated", this);
33400                 this.fireEvent("expanded", this);
33401             },
33402             scope: this,
33403             block: true
33404         });
33405     },
33406
33407     anchors : {
33408         "west" : "left",
33409         "east" : "right",
33410         "north" : "top",
33411         "south" : "bottom"
33412     },
33413
33414     sanchors : {
33415         "west" : "l",
33416         "east" : "r",
33417         "north" : "t",
33418         "south" : "b"
33419     },
33420
33421     canchors : {
33422         "west" : "tl-tr",
33423         "east" : "tr-tl",
33424         "north" : "tl-bl",
33425         "south" : "bl-tl"
33426     },
33427
33428     getAnchor : function(){
33429         return this.anchors[this.position];
33430     },
33431
33432     getCollapseAnchor : function(){
33433         return this.canchors[this.position];
33434     },
33435
33436     getSlideAnchor : function(){
33437         return this.sanchors[this.position];
33438     },
33439
33440     getAlignAdj : function(){
33441         var cm = this.cmargins;
33442         switch(this.position){
33443             case "west":
33444                 return [0, 0];
33445             break;
33446             case "east":
33447                 return [0, 0];
33448             break;
33449             case "north":
33450                 return [0, 0];
33451             break;
33452             case "south":
33453                 return [0, 0];
33454             break;
33455         }
33456     },
33457
33458     getExpandAdj : function(){
33459         var c = this.collapsedEl, cm = this.cmargins;
33460         switch(this.position){
33461             case "west":
33462                 return [-(cm.right+c.getWidth()+cm.left), 0];
33463             break;
33464             case "east":
33465                 return [cm.right+c.getWidth()+cm.left, 0];
33466             break;
33467             case "north":
33468                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33469             break;
33470             case "south":
33471                 return [0, cm.top+cm.bottom+c.getHeight()];
33472             break;
33473         }
33474     }
33475 });/*
33476  * Based on:
33477  * Ext JS Library 1.1.1
33478  * Copyright(c) 2006-2007, Ext JS, LLC.
33479  *
33480  * Originally Released Under LGPL - original licence link has changed is not relivant.
33481  *
33482  * Fork - LGPL
33483  * <script type="text/javascript">
33484  */
33485 /*
33486  * These classes are private internal classes
33487  */
33488 Roo.bootstrap.layout.Center = function(config){
33489     config.region = "center";
33490     Roo.bootstrap.layout.Region.call(this, config);
33491     this.visible = true;
33492     this.minWidth = config.minWidth || 20;
33493     this.minHeight = config.minHeight || 20;
33494 };
33495
33496 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33497     hide : function(){
33498         // center panel can't be hidden
33499     },
33500     
33501     show : function(){
33502         // center panel can't be hidden
33503     },
33504     
33505     getMinWidth: function(){
33506         return this.minWidth;
33507     },
33508     
33509     getMinHeight: function(){
33510         return this.minHeight;
33511     }
33512 });
33513
33514
33515
33516
33517  
33518
33519
33520
33521
33522
33523 Roo.bootstrap.layout.North = function(config)
33524 {
33525     config.region = 'north';
33526     config.cursor = 'n-resize';
33527     
33528     Roo.bootstrap.layout.Split.call(this, config);
33529     
33530     
33531     if(this.split){
33532         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33533         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33534         this.split.el.addClass("roo-layout-split-v");
33535     }
33536     var size = config.initialSize || config.height;
33537     if(typeof size != "undefined"){
33538         this.el.setHeight(size);
33539     }
33540 };
33541 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33542 {
33543     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33544     
33545     
33546     
33547     getBox : function(){
33548         if(this.collapsed){
33549             return this.collapsedEl.getBox();
33550         }
33551         var box = this.el.getBox();
33552         if(this.split){
33553             box.height += this.split.el.getHeight();
33554         }
33555         return box;
33556     },
33557     
33558     updateBox : function(box){
33559         if(this.split && !this.collapsed){
33560             box.height -= this.split.el.getHeight();
33561             this.split.el.setLeft(box.x);
33562             this.split.el.setTop(box.y+box.height);
33563             this.split.el.setWidth(box.width);
33564         }
33565         if(this.collapsed){
33566             this.updateBody(box.width, null);
33567         }
33568         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33569     }
33570 });
33571
33572
33573
33574
33575
33576 Roo.bootstrap.layout.South = function(config){
33577     config.region = 'south';
33578     config.cursor = 's-resize';
33579     Roo.bootstrap.layout.Split.call(this, config);
33580     if(this.split){
33581         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33582         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33583         this.split.el.addClass("roo-layout-split-v");
33584     }
33585     var size = config.initialSize || config.height;
33586     if(typeof size != "undefined"){
33587         this.el.setHeight(size);
33588     }
33589 };
33590
33591 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33592     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33593     getBox : function(){
33594         if(this.collapsed){
33595             return this.collapsedEl.getBox();
33596         }
33597         var box = this.el.getBox();
33598         if(this.split){
33599             var sh = this.split.el.getHeight();
33600             box.height += sh;
33601             box.y -= sh;
33602         }
33603         return box;
33604     },
33605     
33606     updateBox : function(box){
33607         if(this.split && !this.collapsed){
33608             var sh = this.split.el.getHeight();
33609             box.height -= sh;
33610             box.y += sh;
33611             this.split.el.setLeft(box.x);
33612             this.split.el.setTop(box.y-sh);
33613             this.split.el.setWidth(box.width);
33614         }
33615         if(this.collapsed){
33616             this.updateBody(box.width, null);
33617         }
33618         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33619     }
33620 });
33621
33622 Roo.bootstrap.layout.East = function(config){
33623     config.region = "east";
33624     config.cursor = "e-resize";
33625     Roo.bootstrap.layout.Split.call(this, config);
33626     if(this.split){
33627         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33628         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33629         this.split.el.addClass("roo-layout-split-h");
33630     }
33631     var size = config.initialSize || config.width;
33632     if(typeof size != "undefined"){
33633         this.el.setWidth(size);
33634     }
33635 };
33636 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33637     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33638     getBox : function(){
33639         if(this.collapsed){
33640             return this.collapsedEl.getBox();
33641         }
33642         var box = this.el.getBox();
33643         if(this.split){
33644             var sw = this.split.el.getWidth();
33645             box.width += sw;
33646             box.x -= sw;
33647         }
33648         return box;
33649     },
33650
33651     updateBox : function(box){
33652         if(this.split && !this.collapsed){
33653             var sw = this.split.el.getWidth();
33654             box.width -= sw;
33655             this.split.el.setLeft(box.x);
33656             this.split.el.setTop(box.y);
33657             this.split.el.setHeight(box.height);
33658             box.x += sw;
33659         }
33660         if(this.collapsed){
33661             this.updateBody(null, box.height);
33662         }
33663         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33664     }
33665 });
33666
33667 Roo.bootstrap.layout.West = function(config){
33668     config.region = "west";
33669     config.cursor = "w-resize";
33670     
33671     Roo.bootstrap.layout.Split.call(this, config);
33672     if(this.split){
33673         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33674         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33675         this.split.el.addClass("roo-layout-split-h");
33676     }
33677     
33678 };
33679 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33680     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33681     
33682     onRender: function(ctr, pos)
33683     {
33684         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33685         var size = this.config.initialSize || this.config.width;
33686         if(typeof size != "undefined"){
33687             this.el.setWidth(size);
33688         }
33689     },
33690     
33691     getBox : function(){
33692         if(this.collapsed){
33693             return this.collapsedEl.getBox();
33694         }
33695         var box = this.el.getBox();
33696         if(this.split){
33697             box.width += this.split.el.getWidth();
33698         }
33699         return box;
33700     },
33701     
33702     updateBox : function(box){
33703         if(this.split && !this.collapsed){
33704             var sw = this.split.el.getWidth();
33705             box.width -= sw;
33706             this.split.el.setLeft(box.x+box.width);
33707             this.split.el.setTop(box.y);
33708             this.split.el.setHeight(box.height);
33709         }
33710         if(this.collapsed){
33711             this.updateBody(null, box.height);
33712         }
33713         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33714     }
33715 });
33716 Roo.namespace("Roo.bootstrap.panel");/*
33717  * Based on:
33718  * Ext JS Library 1.1.1
33719  * Copyright(c) 2006-2007, Ext JS, LLC.
33720  *
33721  * Originally Released Under LGPL - original licence link has changed is not relivant.
33722  *
33723  * Fork - LGPL
33724  * <script type="text/javascript">
33725  */
33726 /**
33727  * @class Roo.ContentPanel
33728  * @extends Roo.util.Observable
33729  * A basic ContentPanel element.
33730  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33731  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33732  * @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
33733  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33734  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33735  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33736  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33737  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33738  * @cfg {String} title          The title for this panel
33739  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33740  * @cfg {String} url            Calls {@link #setUrl} with this value
33741  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33742  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33743  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33744  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33745
33746  * @constructor
33747  * Create a new ContentPanel.
33748  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33749  * @param {String/Object} config A string to set only the title or a config object
33750  * @param {String} content (optional) Set the HTML content for this panel
33751  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33752  */
33753 Roo.bootstrap.panel.Content = function( config){
33754     
33755     var el = config.el;
33756     var content = config.content;
33757
33758     if(config.autoCreate){ // xtype is available if this is called from factory
33759         el = Roo.id();
33760     }
33761     this.el = Roo.get(el);
33762     if(!this.el && config && config.autoCreate){
33763         if(typeof config.autoCreate == "object"){
33764             if(!config.autoCreate.id){
33765                 config.autoCreate.id = config.id||el;
33766             }
33767             this.el = Roo.DomHelper.append(document.body,
33768                         config.autoCreate, true);
33769         }else{
33770             var elcfg =  {   tag: "div",
33771                             cls: "roo-layout-inactive-content",
33772                             id: config.id||el
33773                             };
33774             if (config.html) {
33775                 elcfg.html = config.html;
33776                 
33777             }
33778                         
33779             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33780         }
33781     } 
33782     this.closable = false;
33783     this.loaded = false;
33784     this.active = false;
33785    
33786       
33787     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33788         
33789         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33790         
33791         this.wrapEl = this.el.wrap();
33792         var ti = [];
33793         if (config.toolbar.items) {
33794             ti = config.toolbar.items ;
33795             delete config.toolbar.items ;
33796         }
33797         
33798         var nitems = [];
33799         this.toolbar.render(this.wrapEl, 'before');
33800         for(var i =0;i < ti.length;i++) {
33801           //  Roo.log(['add child', items[i]]);
33802             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33803         }
33804         this.toolbar.items = nitems;
33805         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33806         delete config.toolbar;
33807         
33808     }
33809     /*
33810     // xtype created footer. - not sure if will work as we normally have to render first..
33811     if (this.footer && !this.footer.el && this.footer.xtype) {
33812         if (!this.wrapEl) {
33813             this.wrapEl = this.el.wrap();
33814         }
33815     
33816         this.footer.container = this.wrapEl.createChild();
33817          
33818         this.footer = Roo.factory(this.footer, Roo);
33819         
33820     }
33821     */
33822     
33823      if(typeof config == "string"){
33824         this.title = config;
33825     }else{
33826         Roo.apply(this, config);
33827     }
33828     
33829     if(this.resizeEl){
33830         this.resizeEl = Roo.get(this.resizeEl, true);
33831     }else{
33832         this.resizeEl = this.el;
33833     }
33834     // handle view.xtype
33835     
33836  
33837     
33838     
33839     this.addEvents({
33840         /**
33841          * @event activate
33842          * Fires when this panel is activated. 
33843          * @param {Roo.ContentPanel} this
33844          */
33845         "activate" : true,
33846         /**
33847          * @event deactivate
33848          * Fires when this panel is activated. 
33849          * @param {Roo.ContentPanel} this
33850          */
33851         "deactivate" : true,
33852
33853         /**
33854          * @event resize
33855          * Fires when this panel is resized if fitToFrame is true.
33856          * @param {Roo.ContentPanel} this
33857          * @param {Number} width The width after any component adjustments
33858          * @param {Number} height The height after any component adjustments
33859          */
33860         "resize" : true,
33861         
33862          /**
33863          * @event render
33864          * Fires when this tab is created
33865          * @param {Roo.ContentPanel} this
33866          */
33867         "render" : true
33868         
33869         
33870         
33871     });
33872     
33873
33874     
33875     
33876     if(this.autoScroll){
33877         this.resizeEl.setStyle("overflow", "auto");
33878     } else {
33879         // fix randome scrolling
33880         //this.el.on('scroll', function() {
33881         //    Roo.log('fix random scolling');
33882         //    this.scrollTo('top',0); 
33883         //});
33884     }
33885     content = content || this.content;
33886     if(content){
33887         this.setContent(content);
33888     }
33889     if(config && config.url){
33890         this.setUrl(this.url, this.params, this.loadOnce);
33891     }
33892     
33893     
33894     
33895     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33896     
33897     if (this.view && typeof(this.view.xtype) != 'undefined') {
33898         this.view.el = this.el.appendChild(document.createElement("div"));
33899         this.view = Roo.factory(this.view); 
33900         this.view.render  &&  this.view.render(false, '');  
33901     }
33902     
33903     
33904     this.fireEvent('render', this);
33905 };
33906
33907 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33908     tabTip:'',
33909     setRegion : function(region){
33910         this.region = region;
33911         if(region){
33912            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33913         }else{
33914            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33915         } 
33916     },
33917     
33918     /**
33919      * Returns the toolbar for this Panel if one was configured. 
33920      * @return {Roo.Toolbar} 
33921      */
33922     getToolbar : function(){
33923         return this.toolbar;
33924     },
33925     
33926     setActiveState : function(active){
33927         this.active = active;
33928         if(!active){
33929             this.fireEvent("deactivate", this);
33930         }else{
33931             this.fireEvent("activate", this);
33932         }
33933     },
33934     /**
33935      * Updates this panel's element
33936      * @param {String} content The new content
33937      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33938     */
33939     setContent : function(content, loadScripts){
33940         this.el.update(content, loadScripts);
33941     },
33942
33943     ignoreResize : function(w, h){
33944         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33945             return true;
33946         }else{
33947             this.lastSize = {width: w, height: h};
33948             return false;
33949         }
33950     },
33951     /**
33952      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33953      * @return {Roo.UpdateManager} The UpdateManager
33954      */
33955     getUpdateManager : function(){
33956         return this.el.getUpdateManager();
33957     },
33958      /**
33959      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33960      * @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:
33961 <pre><code>
33962 panel.load({
33963     url: "your-url.php",
33964     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33965     callback: yourFunction,
33966     scope: yourObject, //(optional scope)
33967     discardUrl: false,
33968     nocache: false,
33969     text: "Loading...",
33970     timeout: 30,
33971     scripts: false
33972 });
33973 </code></pre>
33974      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33975      * 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.
33976      * @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}
33977      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33978      * @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.
33979      * @return {Roo.ContentPanel} this
33980      */
33981     load : function(){
33982         var um = this.el.getUpdateManager();
33983         um.update.apply(um, arguments);
33984         return this;
33985     },
33986
33987
33988     /**
33989      * 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.
33990      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33991      * @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)
33992      * @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)
33993      * @return {Roo.UpdateManager} The UpdateManager
33994      */
33995     setUrl : function(url, params, loadOnce){
33996         if(this.refreshDelegate){
33997             this.removeListener("activate", this.refreshDelegate);
33998         }
33999         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34000         this.on("activate", this.refreshDelegate);
34001         return this.el.getUpdateManager();
34002     },
34003     
34004     _handleRefresh : function(url, params, loadOnce){
34005         if(!loadOnce || !this.loaded){
34006             var updater = this.el.getUpdateManager();
34007             updater.update(url, params, this._setLoaded.createDelegate(this));
34008         }
34009     },
34010     
34011     _setLoaded : function(){
34012         this.loaded = true;
34013     }, 
34014     
34015     /**
34016      * Returns this panel's id
34017      * @return {String} 
34018      */
34019     getId : function(){
34020         return this.el.id;
34021     },
34022     
34023     /** 
34024      * Returns this panel's element - used by regiosn to add.
34025      * @return {Roo.Element} 
34026      */
34027     getEl : function(){
34028         return this.wrapEl || this.el;
34029     },
34030     
34031    
34032     
34033     adjustForComponents : function(width, height)
34034     {
34035         //Roo.log('adjustForComponents ');
34036         if(this.resizeEl != this.el){
34037             width -= this.el.getFrameWidth('lr');
34038             height -= this.el.getFrameWidth('tb');
34039         }
34040         if(this.toolbar){
34041             var te = this.toolbar.getEl();
34042             height -= te.getHeight();
34043             te.setWidth(width);
34044         }
34045         if(this.footer){
34046             var te = this.footer.getEl();
34047             Roo.log("footer:" + te.getHeight());
34048             
34049             height -= te.getHeight();
34050             te.setWidth(width);
34051         }
34052         
34053         
34054         if(this.adjustments){
34055             width += this.adjustments[0];
34056             height += this.adjustments[1];
34057         }
34058         return {"width": width, "height": height};
34059     },
34060     
34061     setSize : function(width, height){
34062         if(this.fitToFrame && !this.ignoreResize(width, height)){
34063             if(this.fitContainer && this.resizeEl != this.el){
34064                 this.el.setSize(width, height);
34065             }
34066             var size = this.adjustForComponents(width, height);
34067             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34068             this.fireEvent('resize', this, size.width, size.height);
34069         }
34070     },
34071     
34072     /**
34073      * Returns this panel's title
34074      * @return {String} 
34075      */
34076     getTitle : function(){
34077         return this.title;
34078     },
34079     
34080     /**
34081      * Set this panel's title
34082      * @param {String} title
34083      */
34084     setTitle : function(title){
34085         this.title = title;
34086         if(this.region){
34087             this.region.updatePanelTitle(this, title);
34088         }
34089     },
34090     
34091     /**
34092      * Returns true is this panel was configured to be closable
34093      * @return {Boolean} 
34094      */
34095     isClosable : function(){
34096         return this.closable;
34097     },
34098     
34099     beforeSlide : function(){
34100         this.el.clip();
34101         this.resizeEl.clip();
34102     },
34103     
34104     afterSlide : function(){
34105         this.el.unclip();
34106         this.resizeEl.unclip();
34107     },
34108     
34109     /**
34110      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34111      *   Will fail silently if the {@link #setUrl} method has not been called.
34112      *   This does not activate the panel, just updates its content.
34113      */
34114     refresh : function(){
34115         if(this.refreshDelegate){
34116            this.loaded = false;
34117            this.refreshDelegate();
34118         }
34119     },
34120     
34121     /**
34122      * Destroys this panel
34123      */
34124     destroy : function(){
34125         this.el.removeAllListeners();
34126         var tempEl = document.createElement("span");
34127         tempEl.appendChild(this.el.dom);
34128         tempEl.innerHTML = "";
34129         this.el.remove();
34130         this.el = null;
34131     },
34132     
34133     /**
34134      * form - if the content panel contains a form - this is a reference to it.
34135      * @type {Roo.form.Form}
34136      */
34137     form : false,
34138     /**
34139      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34140      *    This contains a reference to it.
34141      * @type {Roo.View}
34142      */
34143     view : false,
34144     
34145       /**
34146      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34147      * <pre><code>
34148
34149 layout.addxtype({
34150        xtype : 'Form',
34151        items: [ .... ]
34152    }
34153 );
34154
34155 </code></pre>
34156      * @param {Object} cfg Xtype definition of item to add.
34157      */
34158     
34159     
34160     getChildContainer: function () {
34161         return this.getEl();
34162     }
34163     
34164     
34165     /*
34166         var  ret = new Roo.factory(cfg);
34167         return ret;
34168         
34169         
34170         // add form..
34171         if (cfg.xtype.match(/^Form$/)) {
34172             
34173             var el;
34174             //if (this.footer) {
34175             //    el = this.footer.container.insertSibling(false, 'before');
34176             //} else {
34177                 el = this.el.createChild();
34178             //}
34179
34180             this.form = new  Roo.form.Form(cfg);
34181             
34182             
34183             if ( this.form.allItems.length) {
34184                 this.form.render(el.dom);
34185             }
34186             return this.form;
34187         }
34188         // should only have one of theses..
34189         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34190             // views.. should not be just added - used named prop 'view''
34191             
34192             cfg.el = this.el.appendChild(document.createElement("div"));
34193             // factory?
34194             
34195             var ret = new Roo.factory(cfg);
34196              
34197              ret.render && ret.render(false, ''); // render blank..
34198             this.view = ret;
34199             return ret;
34200         }
34201         return false;
34202     }
34203     \*/
34204 });
34205  
34206 /**
34207  * @class Roo.bootstrap.panel.Grid
34208  * @extends Roo.bootstrap.panel.Content
34209  * @constructor
34210  * Create a new GridPanel.
34211  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34212  * @param {Object} config A the config object
34213   
34214  */
34215
34216
34217
34218 Roo.bootstrap.panel.Grid = function(config)
34219 {
34220     
34221       
34222     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34223         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34224
34225     config.el = this.wrapper;
34226     //this.el = this.wrapper;
34227     
34228       if (config.container) {
34229         // ctor'ed from a Border/panel.grid
34230         
34231         
34232         this.wrapper.setStyle("overflow", "hidden");
34233         this.wrapper.addClass('roo-grid-container');
34234
34235     }
34236     
34237     
34238     if(config.toolbar){
34239         var tool_el = this.wrapper.createChild();    
34240         this.toolbar = Roo.factory(config.toolbar);
34241         var ti = [];
34242         if (config.toolbar.items) {
34243             ti = config.toolbar.items ;
34244             delete config.toolbar.items ;
34245         }
34246         
34247         var nitems = [];
34248         this.toolbar.render(tool_el);
34249         for(var i =0;i < ti.length;i++) {
34250           //  Roo.log(['add child', items[i]]);
34251             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34252         }
34253         this.toolbar.items = nitems;
34254         
34255         delete config.toolbar;
34256     }
34257     
34258     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34259     config.grid.scrollBody = true;;
34260     config.grid.monitorWindowResize = false; // turn off autosizing
34261     config.grid.autoHeight = false;
34262     config.grid.autoWidth = false;
34263     
34264     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34265     
34266     if (config.background) {
34267         // render grid on panel activation (if panel background)
34268         this.on('activate', function(gp) {
34269             if (!gp.grid.rendered) {
34270                 gp.grid.render(el);
34271                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34272
34273             }
34274         });
34275             
34276     } else {
34277         this.grid.render(this.wrapper);
34278         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34279
34280     }
34281     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34282     // ??? needed ??? config.el = this.wrapper;
34283     
34284     
34285     
34286   
34287     // xtype created footer. - not sure if will work as we normally have to render first..
34288     if (this.footer && !this.footer.el && this.footer.xtype) {
34289         
34290         var ctr = this.grid.getView().getFooterPanel(true);
34291         this.footer.dataSource = this.grid.dataSource;
34292         this.footer = Roo.factory(this.footer, Roo);
34293         this.footer.render(ctr);
34294         
34295     }
34296     
34297     
34298     
34299     
34300      
34301 };
34302
34303 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34304     getId : function(){
34305         return this.grid.id;
34306     },
34307     
34308     /**
34309      * Returns the grid for this panel
34310      * @return {Roo.bootstrap.Table} 
34311      */
34312     getGrid : function(){
34313         return this.grid;    
34314     },
34315     
34316     setSize : function(width, height){
34317         if(!this.ignoreResize(width, height)){
34318             var grid = this.grid;
34319             var size = this.adjustForComponents(width, height);
34320             var gridel = grid.getGridEl();
34321             gridel.setSize(size.width, size.height);
34322             /*
34323             var thd = grid.getGridEl().select('thead',true).first();
34324             var tbd = grid.getGridEl().select('tbody', true).first();
34325             if (tbd) {
34326                 tbd.setSize(width, height - thd.getHeight());
34327             }
34328             */
34329             grid.autoSize();
34330         }
34331     },
34332      
34333     
34334     
34335     beforeSlide : function(){
34336         this.grid.getView().scroller.clip();
34337     },
34338     
34339     afterSlide : function(){
34340         this.grid.getView().scroller.unclip();
34341     },
34342     
34343     destroy : function(){
34344         this.grid.destroy();
34345         delete this.grid;
34346         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34347     }
34348 });
34349
34350 /**
34351  * @class Roo.bootstrap.panel.Nest
34352  * @extends Roo.bootstrap.panel.Content
34353  * @constructor
34354  * Create a new Panel, that can contain a layout.Border.
34355  * 
34356  * 
34357  * @param {Roo.BorderLayout} layout The layout for this panel
34358  * @param {String/Object} config A string to set only the title or a config object
34359  */
34360 Roo.bootstrap.panel.Nest = function(config)
34361 {
34362     // construct with only one argument..
34363     /* FIXME - implement nicer consturctors
34364     if (layout.layout) {
34365         config = layout;
34366         layout = config.layout;
34367         delete config.layout;
34368     }
34369     if (layout.xtype && !layout.getEl) {
34370         // then layout needs constructing..
34371         layout = Roo.factory(layout, Roo);
34372     }
34373     */
34374     
34375     config.el =  config.layout.getEl();
34376     
34377     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34378     
34379     config.layout.monitorWindowResize = false; // turn off autosizing
34380     this.layout = config.layout;
34381     this.layout.getEl().addClass("roo-layout-nested-layout");
34382     
34383     
34384     
34385     
34386 };
34387
34388 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34389
34390     setSize : function(width, height){
34391         if(!this.ignoreResize(width, height)){
34392             var size = this.adjustForComponents(width, height);
34393             var el = this.layout.getEl();
34394             el.setSize(size.width, size.height);
34395             var touch = el.dom.offsetWidth;
34396             this.layout.layout();
34397             // ie requires a double layout on the first pass
34398             if(Roo.isIE && !this.initialized){
34399                 this.initialized = true;
34400                 this.layout.layout();
34401             }
34402         }
34403     },
34404     
34405     // activate all subpanels if not currently active..
34406     
34407     setActiveState : function(active){
34408         this.active = active;
34409         if(!active){
34410             this.fireEvent("deactivate", this);
34411             return;
34412         }
34413         
34414         this.fireEvent("activate", this);
34415         // not sure if this should happen before or after..
34416         if (!this.layout) {
34417             return; // should not happen..
34418         }
34419         var reg = false;
34420         for (var r in this.layout.regions) {
34421             reg = this.layout.getRegion(r);
34422             if (reg.getActivePanel()) {
34423                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34424                 reg.setActivePanel(reg.getActivePanel());
34425                 continue;
34426             }
34427             if (!reg.panels.length) {
34428                 continue;
34429             }
34430             reg.showPanel(reg.getPanel(0));
34431         }
34432         
34433         
34434         
34435         
34436     },
34437     
34438     /**
34439      * Returns the nested BorderLayout for this panel
34440      * @return {Roo.BorderLayout} 
34441      */
34442     getLayout : function(){
34443         return this.layout;
34444     },
34445     
34446      /**
34447      * Adds a xtype elements to the layout of the nested panel
34448      * <pre><code>
34449
34450 panel.addxtype({
34451        xtype : 'ContentPanel',
34452        region: 'west',
34453        items: [ .... ]
34454    }
34455 );
34456
34457 panel.addxtype({
34458         xtype : 'NestedLayoutPanel',
34459         region: 'west',
34460         layout: {
34461            center: { },
34462            west: { }   
34463         },
34464         items : [ ... list of content panels or nested layout panels.. ]
34465    }
34466 );
34467 </code></pre>
34468      * @param {Object} cfg Xtype definition of item to add.
34469      */
34470     addxtype : function(cfg) {
34471         return this.layout.addxtype(cfg);
34472     
34473     }
34474 });        /*
34475  * Based on:
34476  * Ext JS Library 1.1.1
34477  * Copyright(c) 2006-2007, Ext JS, LLC.
34478  *
34479  * Originally Released Under LGPL - original licence link has changed is not relivant.
34480  *
34481  * Fork - LGPL
34482  * <script type="text/javascript">
34483  */
34484 /**
34485  * @class Roo.TabPanel
34486  * @extends Roo.util.Observable
34487  * A lightweight tab container.
34488  * <br><br>
34489  * Usage:
34490  * <pre><code>
34491 // basic tabs 1, built from existing content
34492 var tabs = new Roo.TabPanel("tabs1");
34493 tabs.addTab("script", "View Script");
34494 tabs.addTab("markup", "View Markup");
34495 tabs.activate("script");
34496
34497 // more advanced tabs, built from javascript
34498 var jtabs = new Roo.TabPanel("jtabs");
34499 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34500
34501 // set up the UpdateManager
34502 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34503 var updater = tab2.getUpdateManager();
34504 updater.setDefaultUrl("ajax1.htm");
34505 tab2.on('activate', updater.refresh, updater, true);
34506
34507 // Use setUrl for Ajax loading
34508 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34509 tab3.setUrl("ajax2.htm", null, true);
34510
34511 // Disabled tab
34512 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34513 tab4.disable();
34514
34515 jtabs.activate("jtabs-1");
34516  * </code></pre>
34517  * @constructor
34518  * Create a new TabPanel.
34519  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34520  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34521  */
34522 Roo.bootstrap.panel.Tabs = function(config){
34523     /**
34524     * The container element for this TabPanel.
34525     * @type Roo.Element
34526     */
34527     this.el = Roo.get(config.el);
34528     delete config.el;
34529     if(config){
34530         if(typeof config == "boolean"){
34531             this.tabPosition = config ? "bottom" : "top";
34532         }else{
34533             Roo.apply(this, config);
34534         }
34535     }
34536     
34537     if(this.tabPosition == "bottom"){
34538         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34539         this.el.addClass("roo-tabs-bottom");
34540     }
34541     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34542     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34543     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34544     if(Roo.isIE){
34545         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34546     }
34547     if(this.tabPosition != "bottom"){
34548         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34549          * @type Roo.Element
34550          */
34551         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34552         this.el.addClass("roo-tabs-top");
34553     }
34554     this.items = [];
34555
34556     this.bodyEl.setStyle("position", "relative");
34557
34558     this.active = null;
34559     this.activateDelegate = this.activate.createDelegate(this);
34560
34561     this.addEvents({
34562         /**
34563          * @event tabchange
34564          * Fires when the active tab changes
34565          * @param {Roo.TabPanel} this
34566          * @param {Roo.TabPanelItem} activePanel The new active tab
34567          */
34568         "tabchange": true,
34569         /**
34570          * @event beforetabchange
34571          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34572          * @param {Roo.TabPanel} this
34573          * @param {Object} e Set cancel to true on this object to cancel the tab change
34574          * @param {Roo.TabPanelItem} tab The tab being changed to
34575          */
34576         "beforetabchange" : true
34577     });
34578
34579     Roo.EventManager.onWindowResize(this.onResize, this);
34580     this.cpad = this.el.getPadding("lr");
34581     this.hiddenCount = 0;
34582
34583
34584     // toolbar on the tabbar support...
34585     if (this.toolbar) {
34586         alert("no toolbar support yet");
34587         this.toolbar  = false;
34588         /*
34589         var tcfg = this.toolbar;
34590         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34591         this.toolbar = new Roo.Toolbar(tcfg);
34592         if (Roo.isSafari) {
34593             var tbl = tcfg.container.child('table', true);
34594             tbl.setAttribute('width', '100%');
34595         }
34596         */
34597         
34598     }
34599    
34600
34601
34602     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34603 };
34604
34605 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34606     /*
34607      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34608      */
34609     tabPosition : "top",
34610     /*
34611      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34612      */
34613     currentTabWidth : 0,
34614     /*
34615      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34616      */
34617     minTabWidth : 40,
34618     /*
34619      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34620      */
34621     maxTabWidth : 250,
34622     /*
34623      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34624      */
34625     preferredTabWidth : 175,
34626     /*
34627      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34628      */
34629     resizeTabs : false,
34630     /*
34631      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34632      */
34633     monitorResize : true,
34634     /*
34635      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34636      */
34637     toolbar : false,
34638
34639     /**
34640      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34641      * @param {String} id The id of the div to use <b>or create</b>
34642      * @param {String} text The text for the tab
34643      * @param {String} content (optional) Content to put in the TabPanelItem body
34644      * @param {Boolean} closable (optional) True to create a close icon on the tab
34645      * @return {Roo.TabPanelItem} The created TabPanelItem
34646      */
34647     addTab : function(id, text, content, closable)
34648     {
34649         var item = new Roo.bootstrap.panel.TabItem({
34650             panel: this,
34651             id : id,
34652             text : text,
34653             closable : closable
34654         });
34655         this.addTabItem(item);
34656         if(content){
34657             item.setContent(content);
34658         }
34659         return item;
34660     },
34661
34662     /**
34663      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34664      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34665      * @return {Roo.TabPanelItem}
34666      */
34667     getTab : function(id){
34668         return this.items[id];
34669     },
34670
34671     /**
34672      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34673      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34674      */
34675     hideTab : function(id){
34676         var t = this.items[id];
34677         if(!t.isHidden()){
34678            t.setHidden(true);
34679            this.hiddenCount++;
34680            this.autoSizeTabs();
34681         }
34682     },
34683
34684     /**
34685      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34686      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34687      */
34688     unhideTab : function(id){
34689         var t = this.items[id];
34690         if(t.isHidden()){
34691            t.setHidden(false);
34692            this.hiddenCount--;
34693            this.autoSizeTabs();
34694         }
34695     },
34696
34697     /**
34698      * Adds an existing {@link Roo.TabPanelItem}.
34699      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34700      */
34701     addTabItem : function(item){
34702         this.items[item.id] = item;
34703         this.items.push(item);
34704       //  if(this.resizeTabs){
34705     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34706   //         this.autoSizeTabs();
34707 //        }else{
34708 //            item.autoSize();
34709        // }
34710     },
34711
34712     /**
34713      * Removes a {@link Roo.TabPanelItem}.
34714      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34715      */
34716     removeTab : function(id){
34717         var items = this.items;
34718         var tab = items[id];
34719         if(!tab) { return; }
34720         var index = items.indexOf(tab);
34721         if(this.active == tab && items.length > 1){
34722             var newTab = this.getNextAvailable(index);
34723             if(newTab) {
34724                 newTab.activate();
34725             }
34726         }
34727         this.stripEl.dom.removeChild(tab.pnode.dom);
34728         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34729             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34730         }
34731         items.splice(index, 1);
34732         delete this.items[tab.id];
34733         tab.fireEvent("close", tab);
34734         tab.purgeListeners();
34735         this.autoSizeTabs();
34736     },
34737
34738     getNextAvailable : function(start){
34739         var items = this.items;
34740         var index = start;
34741         // look for a next tab that will slide over to
34742         // replace the one being removed
34743         while(index < items.length){
34744             var item = items[++index];
34745             if(item && !item.isHidden()){
34746                 return item;
34747             }
34748         }
34749         // if one isn't found select the previous tab (on the left)
34750         index = start;
34751         while(index >= 0){
34752             var item = items[--index];
34753             if(item && !item.isHidden()){
34754                 return item;
34755             }
34756         }
34757         return null;
34758     },
34759
34760     /**
34761      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34762      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34763      */
34764     disableTab : function(id){
34765         var tab = this.items[id];
34766         if(tab && this.active != tab){
34767             tab.disable();
34768         }
34769     },
34770
34771     /**
34772      * Enables a {@link Roo.TabPanelItem} that is disabled.
34773      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34774      */
34775     enableTab : function(id){
34776         var tab = this.items[id];
34777         tab.enable();
34778     },
34779
34780     /**
34781      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34782      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34783      * @return {Roo.TabPanelItem} The TabPanelItem.
34784      */
34785     activate : function(id){
34786         var tab = this.items[id];
34787         if(!tab){
34788             return null;
34789         }
34790         if(tab == this.active || tab.disabled){
34791             return tab;
34792         }
34793         var e = {};
34794         this.fireEvent("beforetabchange", this, e, tab);
34795         if(e.cancel !== true && !tab.disabled){
34796             if(this.active){
34797                 this.active.hide();
34798             }
34799             this.active = this.items[id];
34800             this.active.show();
34801             this.fireEvent("tabchange", this, this.active);
34802         }
34803         return tab;
34804     },
34805
34806     /**
34807      * Gets the active {@link Roo.TabPanelItem}.
34808      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34809      */
34810     getActiveTab : function(){
34811         return this.active;
34812     },
34813
34814     /**
34815      * Updates the tab body element to fit the height of the container element
34816      * for overflow scrolling
34817      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34818      */
34819     syncHeight : function(targetHeight){
34820         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34821         var bm = this.bodyEl.getMargins();
34822         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34823         this.bodyEl.setHeight(newHeight);
34824         return newHeight;
34825     },
34826
34827     onResize : function(){
34828         if(this.monitorResize){
34829             this.autoSizeTabs();
34830         }
34831     },
34832
34833     /**
34834      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34835      */
34836     beginUpdate : function(){
34837         this.updating = true;
34838     },
34839
34840     /**
34841      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34842      */
34843     endUpdate : function(){
34844         this.updating = false;
34845         this.autoSizeTabs();
34846     },
34847
34848     /**
34849      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34850      */
34851     autoSizeTabs : function(){
34852         var count = this.items.length;
34853         var vcount = count - this.hiddenCount;
34854         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34855             return;
34856         }
34857         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34858         var availWidth = Math.floor(w / vcount);
34859         var b = this.stripBody;
34860         if(b.getWidth() > w){
34861             var tabs = this.items;
34862             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34863             if(availWidth < this.minTabWidth){
34864                 /*if(!this.sleft){    // incomplete scrolling code
34865                     this.createScrollButtons();
34866                 }
34867                 this.showScroll();
34868                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34869             }
34870         }else{
34871             if(this.currentTabWidth < this.preferredTabWidth){
34872                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34873             }
34874         }
34875     },
34876
34877     /**
34878      * Returns the number of tabs in this TabPanel.
34879      * @return {Number}
34880      */
34881      getCount : function(){
34882          return this.items.length;
34883      },
34884
34885     /**
34886      * Resizes all the tabs to the passed width
34887      * @param {Number} The new width
34888      */
34889     setTabWidth : function(width){
34890         this.currentTabWidth = width;
34891         for(var i = 0, len = this.items.length; i < len; i++) {
34892                 if(!this.items[i].isHidden()) {
34893                 this.items[i].setWidth(width);
34894             }
34895         }
34896     },
34897
34898     /**
34899      * Destroys this TabPanel
34900      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34901      */
34902     destroy : function(removeEl){
34903         Roo.EventManager.removeResizeListener(this.onResize, this);
34904         for(var i = 0, len = this.items.length; i < len; i++){
34905             this.items[i].purgeListeners();
34906         }
34907         if(removeEl === true){
34908             this.el.update("");
34909             this.el.remove();
34910         }
34911     },
34912     
34913     createStrip : function(container)
34914     {
34915         var strip = document.createElement("nav");
34916         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34917         container.appendChild(strip);
34918         return strip;
34919     },
34920     
34921     createStripList : function(strip)
34922     {
34923         // div wrapper for retard IE
34924         // returns the "tr" element.
34925         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34926         //'<div class="x-tabs-strip-wrap">'+
34927           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34928           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34929         return strip.firstChild; //.firstChild.firstChild.firstChild;
34930     },
34931     createBody : function(container)
34932     {
34933         var body = document.createElement("div");
34934         Roo.id(body, "tab-body");
34935         //Roo.fly(body).addClass("x-tabs-body");
34936         Roo.fly(body).addClass("tab-content");
34937         container.appendChild(body);
34938         return body;
34939     },
34940     createItemBody :function(bodyEl, id){
34941         var body = Roo.getDom(id);
34942         if(!body){
34943             body = document.createElement("div");
34944             body.id = id;
34945         }
34946         //Roo.fly(body).addClass("x-tabs-item-body");
34947         Roo.fly(body).addClass("tab-pane");
34948          bodyEl.insertBefore(body, bodyEl.firstChild);
34949         return body;
34950     },
34951     /** @private */
34952     createStripElements :  function(stripEl, text, closable)
34953     {
34954         var td = document.createElement("li"); // was td..
34955         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34956         //stripEl.appendChild(td);
34957         /*if(closable){
34958             td.className = "x-tabs-closable";
34959             if(!this.closeTpl){
34960                 this.closeTpl = new Roo.Template(
34961                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34962                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34963                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34964                 );
34965             }
34966             var el = this.closeTpl.overwrite(td, {"text": text});
34967             var close = el.getElementsByTagName("div")[0];
34968             var inner = el.getElementsByTagName("em")[0];
34969             return {"el": el, "close": close, "inner": inner};
34970         } else {
34971         */
34972         // not sure what this is..
34973             if(!this.tabTpl){
34974                 //this.tabTpl = new Roo.Template(
34975                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34976                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34977                 //);
34978                 this.tabTpl = new Roo.Template(
34979                    '<a href="#">' +
34980                    '<span unselectable="on"' +
34981                             (this.disableTooltips ? '' : ' title="{text}"') +
34982                             ' >{text}</span></span></a>'
34983                 );
34984                 
34985             }
34986             var el = this.tabTpl.overwrite(td, {"text": text});
34987             var inner = el.getElementsByTagName("span")[0];
34988             return {"el": el, "inner": inner};
34989         //}
34990     }
34991         
34992     
34993 });
34994
34995 /**
34996  * @class Roo.TabPanelItem
34997  * @extends Roo.util.Observable
34998  * Represents an individual item (tab plus body) in a TabPanel.
34999  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35000  * @param {String} id The id of this TabPanelItem
35001  * @param {String} text The text for the tab of this TabPanelItem
35002  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35003  */
35004 Roo.bootstrap.panel.TabItem = function(config){
35005     /**
35006      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35007      * @type Roo.TabPanel
35008      */
35009     this.tabPanel = config.panel;
35010     /**
35011      * The id for this TabPanelItem
35012      * @type String
35013      */
35014     this.id = config.id;
35015     /** @private */
35016     this.disabled = false;
35017     /** @private */
35018     this.text = config.text;
35019     /** @private */
35020     this.loaded = false;
35021     this.closable = config.closable;
35022
35023     /**
35024      * The body element for this TabPanelItem.
35025      * @type Roo.Element
35026      */
35027     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35028     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35029     this.bodyEl.setStyle("display", "block");
35030     this.bodyEl.setStyle("zoom", "1");
35031     //this.hideAction();
35032
35033     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35034     /** @private */
35035     this.el = Roo.get(els.el);
35036     this.inner = Roo.get(els.inner, true);
35037     this.textEl = Roo.get(this.el.dom.firstChild, true);
35038     this.pnode = Roo.get(els.el.parentNode, true);
35039     this.el.on("mousedown", this.onTabMouseDown, this);
35040     this.el.on("click", this.onTabClick, this);
35041     /** @private */
35042     if(config.closable){
35043         var c = Roo.get(els.close, true);
35044         c.dom.title = this.closeText;
35045         c.addClassOnOver("close-over");
35046         c.on("click", this.closeClick, this);
35047      }
35048
35049     this.addEvents({
35050          /**
35051          * @event activate
35052          * Fires when this tab becomes the active tab.
35053          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35054          * @param {Roo.TabPanelItem} this
35055          */
35056         "activate": true,
35057         /**
35058          * @event beforeclose
35059          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35060          * @param {Roo.TabPanelItem} this
35061          * @param {Object} e Set cancel to true on this object to cancel the close.
35062          */
35063         "beforeclose": true,
35064         /**
35065          * @event close
35066          * Fires when this tab is closed.
35067          * @param {Roo.TabPanelItem} this
35068          */
35069          "close": true,
35070         /**
35071          * @event deactivate
35072          * Fires when this tab is no longer the active tab.
35073          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35074          * @param {Roo.TabPanelItem} this
35075          */
35076          "deactivate" : true
35077     });
35078     this.hidden = false;
35079
35080     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35081 };
35082
35083 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35084            {
35085     purgeListeners : function(){
35086        Roo.util.Observable.prototype.purgeListeners.call(this);
35087        this.el.removeAllListeners();
35088     },
35089     /**
35090      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35091      */
35092     show : function(){
35093         this.pnode.addClass("active");
35094         this.showAction();
35095         if(Roo.isOpera){
35096             this.tabPanel.stripWrap.repaint();
35097         }
35098         this.fireEvent("activate", this.tabPanel, this);
35099     },
35100
35101     /**
35102      * Returns true if this tab is the active tab.
35103      * @return {Boolean}
35104      */
35105     isActive : function(){
35106         return this.tabPanel.getActiveTab() == this;
35107     },
35108
35109     /**
35110      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35111      */
35112     hide : function(){
35113         this.pnode.removeClass("active");
35114         this.hideAction();
35115         this.fireEvent("deactivate", this.tabPanel, this);
35116     },
35117
35118     hideAction : function(){
35119         this.bodyEl.hide();
35120         this.bodyEl.setStyle("position", "absolute");
35121         this.bodyEl.setLeft("-20000px");
35122         this.bodyEl.setTop("-20000px");
35123     },
35124
35125     showAction : function(){
35126         this.bodyEl.setStyle("position", "relative");
35127         this.bodyEl.setTop("");
35128         this.bodyEl.setLeft("");
35129         this.bodyEl.show();
35130     },
35131
35132     /**
35133      * Set the tooltip for the tab.
35134      * @param {String} tooltip The tab's tooltip
35135      */
35136     setTooltip : function(text){
35137         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35138             this.textEl.dom.qtip = text;
35139             this.textEl.dom.removeAttribute('title');
35140         }else{
35141             this.textEl.dom.title = text;
35142         }
35143     },
35144
35145     onTabClick : function(e){
35146         e.preventDefault();
35147         this.tabPanel.activate(this.id);
35148     },
35149
35150     onTabMouseDown : function(e){
35151         e.preventDefault();
35152         this.tabPanel.activate(this.id);
35153     },
35154 /*
35155     getWidth : function(){
35156         return this.inner.getWidth();
35157     },
35158
35159     setWidth : function(width){
35160         var iwidth = width - this.pnode.getPadding("lr");
35161         this.inner.setWidth(iwidth);
35162         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35163         this.pnode.setWidth(width);
35164     },
35165 */
35166     /**
35167      * Show or hide the tab
35168      * @param {Boolean} hidden True to hide or false to show.
35169      */
35170     setHidden : function(hidden){
35171         this.hidden = hidden;
35172         this.pnode.setStyle("display", hidden ? "none" : "");
35173     },
35174
35175     /**
35176      * Returns true if this tab is "hidden"
35177      * @return {Boolean}
35178      */
35179     isHidden : function(){
35180         return this.hidden;
35181     },
35182
35183     /**
35184      * Returns the text for this tab
35185      * @return {String}
35186      */
35187     getText : function(){
35188         return this.text;
35189     },
35190     /*
35191     autoSize : function(){
35192         //this.el.beginMeasure();
35193         this.textEl.setWidth(1);
35194         /*
35195          *  #2804 [new] Tabs in Roojs
35196          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35197          */
35198         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35199         //this.el.endMeasure();
35200     //},
35201
35202     /**
35203      * Sets the text for the tab (Note: this also sets the tooltip text)
35204      * @param {String} text The tab's text and tooltip
35205      */
35206     setText : function(text){
35207         this.text = text;
35208         this.textEl.update(text);
35209         this.setTooltip(text);
35210         //if(!this.tabPanel.resizeTabs){
35211         //    this.autoSize();
35212         //}
35213     },
35214     /**
35215      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35216      */
35217     activate : function(){
35218         this.tabPanel.activate(this.id);
35219     },
35220
35221     /**
35222      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35223      */
35224     disable : function(){
35225         if(this.tabPanel.active != this){
35226             this.disabled = true;
35227             this.pnode.addClass("disabled");
35228         }
35229     },
35230
35231     /**
35232      * Enables this TabPanelItem if it was previously disabled.
35233      */
35234     enable : function(){
35235         this.disabled = false;
35236         this.pnode.removeClass("disabled");
35237     },
35238
35239     /**
35240      * Sets the content for this TabPanelItem.
35241      * @param {String} content The content
35242      * @param {Boolean} loadScripts true to look for and load scripts
35243      */
35244     setContent : function(content, loadScripts){
35245         this.bodyEl.update(content, loadScripts);
35246     },
35247
35248     /**
35249      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35250      * @return {Roo.UpdateManager} The UpdateManager
35251      */
35252     getUpdateManager : function(){
35253         return this.bodyEl.getUpdateManager();
35254     },
35255
35256     /**
35257      * Set a URL to be used to load the content for this TabPanelItem.
35258      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35259      * @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)
35260      * @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)
35261      * @return {Roo.UpdateManager} The UpdateManager
35262      */
35263     setUrl : function(url, params, loadOnce){
35264         if(this.refreshDelegate){
35265             this.un('activate', this.refreshDelegate);
35266         }
35267         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35268         this.on("activate", this.refreshDelegate);
35269         return this.bodyEl.getUpdateManager();
35270     },
35271
35272     /** @private */
35273     _handleRefresh : function(url, params, loadOnce){
35274         if(!loadOnce || !this.loaded){
35275             var updater = this.bodyEl.getUpdateManager();
35276             updater.update(url, params, this._setLoaded.createDelegate(this));
35277         }
35278     },
35279
35280     /**
35281      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35282      *   Will fail silently if the setUrl method has not been called.
35283      *   This does not activate the panel, just updates its content.
35284      */
35285     refresh : function(){
35286         if(this.refreshDelegate){
35287            this.loaded = false;
35288            this.refreshDelegate();
35289         }
35290     },
35291
35292     /** @private */
35293     _setLoaded : function(){
35294         this.loaded = true;
35295     },
35296
35297     /** @private */
35298     closeClick : function(e){
35299         var o = {};
35300         e.stopEvent();
35301         this.fireEvent("beforeclose", this, o);
35302         if(o.cancel !== true){
35303             this.tabPanel.removeTab(this.id);
35304         }
35305     },
35306     /**
35307      * The text displayed in the tooltip for the close icon.
35308      * @type String
35309      */
35310     closeText : "Close this tab"
35311 });