Roo/bootstrap/Table.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         //Roo.log('initEvents with ds!!!!');
5977         
5978         this.mainBody = this.el.select('tbody', true).first();
5979         this.mainHead = this.el.select('thead', true).first();
5980         
5981         
5982         
5983         var _this = this;
5984         
5985         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5986             e.on('click', _this.sort, _this);
5987         });
5988         
5989         this.el.on("click", this.onClick, this);
5990         this.el.on("dblclick", this.onDblClick, this);
5991         
5992         // why is this done????? = it breaks dialogs??
5993         //this.parent().el.setStyle('position', 'relative');
5994         
5995         
5996         if (this.footer) {
5997             this.footer.parentId = this.id;
5998             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5999         }
6000         
6001         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6002         
6003         this.store.on('load', this.onLoad, this);
6004         this.store.on('beforeload', this.onBeforeLoad, this);
6005         this.store.on('update', this.onUpdate, this);
6006         this.store.on('add', this.onAdd, this);
6007         this.store.on("clear", this.clear, this);
6008         
6009         this.el.on("contextmenu", this.onContextMenu, this);
6010         
6011         this.mainBody.on('scroll', this.onBodyScroll, this);
6012         
6013         
6014     },
6015     
6016     onContextMenu : function(e, t)
6017     {
6018         this.processEvent("contextmenu", e);
6019     },
6020     
6021     processEvent : function(name, e)
6022     {
6023         if (name != 'touchstart' ) {
6024             this.fireEvent(name, e);    
6025         }
6026         
6027         var t = e.getTarget();
6028         
6029         var cell = Roo.get(t);
6030         
6031         if(!cell){
6032             return;
6033         }
6034         
6035         if(cell.findParent('tfoot', false, true)){
6036             return;
6037         }
6038         
6039         if(cell.findParent('thead', false, true)){
6040             
6041             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6042                 cell = Roo.get(t).findParent('th', false, true);
6043                 if (!cell) {
6044                     Roo.log("failed to find th in thead?");
6045                     Roo.log(e.getTarget());
6046                     return;
6047                 }
6048             }
6049             
6050             var cellIndex = cell.dom.cellIndex;
6051             
6052             var ename = name == 'touchstart' ? 'click' : name;
6053             this.fireEvent("header" + ename, this, cellIndex, e);
6054             
6055             return;
6056         }
6057         
6058         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6059             cell = Roo.get(t).findParent('td', false, true);
6060             if (!cell) {
6061                 Roo.log("failed to find th in tbody?");
6062                 Roo.log(e.getTarget());
6063                 return;
6064             }
6065         }
6066         
6067         var row = cell.findParent('tr', false, true);
6068         var cellIndex = cell.dom.cellIndex;
6069         var rowIndex = row.dom.rowIndex - 1;
6070         
6071         if(row !== false){
6072             
6073             this.fireEvent("row" + name, this, rowIndex, e);
6074             
6075             if(cell !== false){
6076             
6077                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6078             }
6079         }
6080         
6081     },
6082     
6083     onMouseover : function(e, el)
6084     {
6085         var cell = Roo.get(el);
6086         
6087         if(!cell){
6088             return;
6089         }
6090         
6091         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6092             cell = cell.findParent('td', false, true);
6093         }
6094         
6095         var row = cell.findParent('tr', false, true);
6096         var cellIndex = cell.dom.cellIndex;
6097         var rowIndex = row.dom.rowIndex - 1; // start from 0
6098         
6099         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6100         
6101     },
6102     
6103     onMouseout : function(e, el)
6104     {
6105         var cell = Roo.get(el);
6106         
6107         if(!cell){
6108             return;
6109         }
6110         
6111         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6112             cell = cell.findParent('td', false, true);
6113         }
6114         
6115         var row = cell.findParent('tr', false, true);
6116         var cellIndex = cell.dom.cellIndex;
6117         var rowIndex = row.dom.rowIndex - 1; // start from 0
6118         
6119         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6120         
6121     },
6122     
6123     onClick : function(e, el)
6124     {
6125         var cell = Roo.get(el);
6126         
6127         if(!cell || (!this.cellSelection && !this.rowSelection)){
6128             return;
6129         }
6130         
6131         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6132             cell = cell.findParent('td', false, true);
6133         }
6134         
6135         if(!cell || typeof(cell) == 'undefined'){
6136             return;
6137         }
6138         
6139         var row = cell.findParent('tr', false, true);
6140         
6141         if(!row || typeof(row) == 'undefined'){
6142             return;
6143         }
6144         
6145         var cellIndex = cell.dom.cellIndex;
6146         var rowIndex = this.getRowIndex(row);
6147         
6148         // why??? - should these not be based on SelectionModel?
6149         if(this.cellSelection){
6150             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6151         }
6152         
6153         if(this.rowSelection){
6154             this.fireEvent('rowclick', this, row, rowIndex, e);
6155         }
6156         
6157         
6158     },
6159     
6160     onDblClick : function(e,el)
6161     {
6162         var cell = Roo.get(el);
6163         
6164         if(!cell || (!this.CellSelection && !this.RowSelection)){
6165             return;
6166         }
6167         
6168         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6169             cell = cell.findParent('td', false, true);
6170         }
6171         
6172         if(!cell || typeof(cell) == 'undefined'){
6173             return;
6174         }
6175         
6176         var row = cell.findParent('tr', false, true);
6177         
6178         if(!row || typeof(row) == 'undefined'){
6179             return;
6180         }
6181         
6182         var cellIndex = cell.dom.cellIndex;
6183         var rowIndex = this.getRowIndex(row);
6184         
6185         if(this.CellSelection){
6186             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6187         }
6188         
6189         if(this.RowSelection){
6190             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6191         }
6192     },
6193     
6194     sort : function(e,el)
6195     {
6196         var col = Roo.get(el);
6197         
6198         if(!col.hasClass('sortable')){
6199             return;
6200         }
6201         
6202         var sort = col.attr('sort');
6203         var dir = 'ASC';
6204         
6205         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6206             dir = 'DESC';
6207         }
6208         
6209         this.store.sortInfo = {field : sort, direction : dir};
6210         
6211         if (this.footer) {
6212             Roo.log("calling footer first");
6213             this.footer.onClick('first');
6214         } else {
6215         
6216             this.store.load({ params : { start : 0 } });
6217         }
6218     },
6219     
6220     renderHeader : function()
6221     {
6222         var header = {
6223             tag: 'thead',
6224             cn : []
6225         };
6226         
6227         var cm = this.cm;
6228         this.totalWidth = 0;
6229         
6230         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6231             
6232             var config = cm.config[i];
6233             
6234             var c = {
6235                 tag: 'th',
6236                 style : '',
6237                 html: cm.getColumnHeader(i)
6238             };
6239             
6240             var hh = '';
6241             
6242             if(typeof(config.sortable) != 'undefined' && config.sortable){
6243                 c.cls = 'sortable';
6244                 c.html = '<i class="glyphicon"></i>' + c.html;
6245             }
6246             
6247             if(typeof(config.lgHeader) != 'undefined'){
6248                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6249             }
6250             
6251             if(typeof(config.mdHeader) != 'undefined'){
6252                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6253             }
6254             
6255             if(typeof(config.smHeader) != 'undefined'){
6256                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6257             }
6258             
6259             if(typeof(config.xsHeader) != 'undefined'){
6260                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6261             }
6262             
6263             if(hh.length){
6264                 c.html = hh;
6265             }
6266             
6267             if(typeof(config.tooltip) != 'undefined'){
6268                 c.tooltip = config.tooltip;
6269             }
6270             
6271             if(typeof(config.colspan) != 'undefined'){
6272                 c.colspan = config.colspan;
6273             }
6274             
6275             if(typeof(config.hidden) != 'undefined' && config.hidden){
6276                 c.style += ' display:none;';
6277             }
6278             
6279             if(typeof(config.dataIndex) != 'undefined'){
6280                 c.sort = config.dataIndex;
6281             }
6282             
6283            
6284             
6285             if(typeof(config.align) != 'undefined' && config.align.length){
6286                 c.style += ' text-align:' + config.align + ';';
6287             }
6288             
6289             if(typeof(config.width) != 'undefined'){
6290                 c.style += ' width:' + config.width + 'px;';
6291                 this.totalWidth += config.width;
6292             } else {
6293                 this.totalWidth += 100; // assume minimum of 100 per column?
6294             }
6295             
6296             if(typeof(config.cls) != 'undefined'){
6297                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6298             }
6299             
6300             ['xs','sm','md','lg'].map(function(size){
6301                 
6302                 if(typeof(config[size]) == 'undefined'){
6303                     return;
6304                 }
6305                 
6306                 if (!config[size]) { // 0 = hidden
6307                     c.cls += ' hidden-' + size;
6308                     return;
6309                 }
6310                 
6311                 c.cls += ' col-' + size + '-' + config[size];
6312
6313             });
6314             
6315             header.cn.push(c)
6316         }
6317         
6318         return header;
6319     },
6320     
6321     renderBody : function()
6322     {
6323         var body = {
6324             tag: 'tbody',
6325             cn : [
6326                 {
6327                     tag: 'tr',
6328                     cn : [
6329                         {
6330                             tag : 'td',
6331                             colspan :  this.cm.getColumnCount()
6332                         }
6333                     ]
6334                 }
6335             ]
6336         };
6337         
6338         return body;
6339     },
6340     
6341     renderFooter : function()
6342     {
6343         var footer = {
6344             tag: 'tfoot',
6345             cn : [
6346                 {
6347                     tag: 'tr',
6348                     cn : [
6349                         {
6350                             tag : 'td',
6351                             colspan :  this.cm.getColumnCount()
6352                         }
6353                     ]
6354                 }
6355             ]
6356         };
6357         
6358         return footer;
6359     },
6360     
6361     
6362     
6363     onLoad : function()
6364     {
6365 //        Roo.log('ds onload');
6366         this.clear();
6367         
6368         var _this = this;
6369         var cm = this.cm;
6370         var ds = this.store;
6371         
6372         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6373             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6374             if (_this.store.sortInfo) {
6375                     
6376                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6377                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6378                 }
6379                 
6380                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6381                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6382                 }
6383             }
6384         });
6385         
6386         var tbody =  this.mainBody;
6387               
6388         if(ds.getCount() > 0){
6389             ds.data.each(function(d,rowIndex){
6390                 var row =  this.renderRow(cm, ds, rowIndex);
6391                 
6392                 tbody.createChild(row);
6393                 
6394                 var _this = this;
6395                 
6396                 if(row.cellObjects.length){
6397                     Roo.each(row.cellObjects, function(r){
6398                         _this.renderCellObject(r);
6399                     })
6400                 }
6401                 
6402             }, this);
6403         }
6404         
6405         Roo.each(this.el.select('tbody td', true).elements, function(e){
6406             e.on('mouseover', _this.onMouseover, _this);
6407         });
6408         
6409         Roo.each(this.el.select('tbody td', true).elements, function(e){
6410             e.on('mouseout', _this.onMouseout, _this);
6411         });
6412         this.fireEvent('rowsrendered', this);
6413         //if(this.loadMask){
6414         //    this.maskEl.hide();
6415         //}
6416         
6417         this.autoSize();
6418     },
6419     
6420     
6421     onUpdate : function(ds,record)
6422     {
6423         this.refreshRow(record);
6424     },
6425     
6426     onRemove : function(ds, record, index, isUpdate){
6427         if(isUpdate !== true){
6428             this.fireEvent("beforerowremoved", this, index, record);
6429         }
6430         var bt = this.mainBody.dom;
6431         
6432         var rows = this.el.select('tbody > tr', true).elements;
6433         
6434         if(typeof(rows[index]) != 'undefined'){
6435             bt.removeChild(rows[index].dom);
6436         }
6437         
6438 //        if(bt.rows[index]){
6439 //            bt.removeChild(bt.rows[index]);
6440 //        }
6441         
6442         if(isUpdate !== true){
6443             //this.stripeRows(index);
6444             //this.syncRowHeights(index, index);
6445             //this.layout();
6446             this.fireEvent("rowremoved", this, index, record);
6447         }
6448     },
6449     
6450     onAdd : function(ds, records, rowIndex)
6451     {
6452         //Roo.log('on Add called');
6453         // - note this does not handle multiple adding very well..
6454         var bt = this.mainBody.dom;
6455         for (var i =0 ; i < records.length;i++) {
6456             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6457             //Roo.log(records[i]);
6458             //Roo.log(this.store.getAt(rowIndex+i));
6459             this.insertRow(this.store, rowIndex + i, false);
6460             return;
6461         }
6462         
6463     },
6464     
6465     
6466     refreshRow : function(record){
6467         var ds = this.store, index;
6468         if(typeof record == 'number'){
6469             index = record;
6470             record = ds.getAt(index);
6471         }else{
6472             index = ds.indexOf(record);
6473         }
6474         this.insertRow(ds, index, true);
6475         this.onRemove(ds, record, index+1, true);
6476         //this.syncRowHeights(index, index);
6477         //this.layout();
6478         this.fireEvent("rowupdated", this, index, record);
6479     },
6480     
6481     insertRow : function(dm, rowIndex, isUpdate){
6482         
6483         if(!isUpdate){
6484             this.fireEvent("beforerowsinserted", this, rowIndex);
6485         }
6486             //var s = this.getScrollState();
6487         var row = this.renderRow(this.cm, this.store, rowIndex);
6488         // insert before rowIndex..
6489         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6490         
6491         var _this = this;
6492                 
6493         if(row.cellObjects.length){
6494             Roo.each(row.cellObjects, function(r){
6495                 _this.renderCellObject(r);
6496             })
6497         }
6498             
6499         if(!isUpdate){
6500             this.fireEvent("rowsinserted", this, rowIndex);
6501             //this.syncRowHeights(firstRow, lastRow);
6502             //this.stripeRows(firstRow);
6503             //this.layout();
6504         }
6505         
6506     },
6507     
6508     
6509     getRowDom : function(rowIndex)
6510     {
6511         var rows = this.el.select('tbody > tr', true).elements;
6512         
6513         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6514         
6515     },
6516     // returns the object tree for a tr..
6517   
6518     
6519     renderRow : function(cm, ds, rowIndex) 
6520     {
6521         
6522         var d = ds.getAt(rowIndex);
6523         
6524         var row = {
6525             tag : 'tr',
6526             cn : []
6527         };
6528             
6529         var cellObjects = [];
6530         
6531         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6532             var config = cm.config[i];
6533             
6534             var renderer = cm.getRenderer(i);
6535             var value = '';
6536             var id = false;
6537             
6538             if(typeof(renderer) !== 'undefined'){
6539                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6540             }
6541             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6542             // and are rendered into the cells after the row is rendered - using the id for the element.
6543             
6544             if(typeof(value) === 'object'){
6545                 id = Roo.id();
6546                 cellObjects.push({
6547                     container : id,
6548                     cfg : value 
6549                 })
6550             }
6551             
6552             var rowcfg = {
6553                 record: d,
6554                 rowIndex : rowIndex,
6555                 colIndex : i,
6556                 rowClass : ''
6557             };
6558
6559             this.fireEvent('rowclass', this, rowcfg);
6560             
6561             var td = {
6562                 tag: 'td',
6563                 cls : rowcfg.rowClass,
6564                 style: '',
6565                 html: (typeof(value) === 'object') ? '' : value
6566             };
6567             
6568             if (id) {
6569                 td.id = id;
6570             }
6571             
6572             if(typeof(config.colspan) != 'undefined'){
6573                 td.colspan = config.colspan;
6574             }
6575             
6576             if(typeof(config.hidden) != 'undefined' && config.hidden){
6577                 td.style += ' display:none;';
6578             }
6579             
6580             if(typeof(config.align) != 'undefined' && config.align.length){
6581                 td.style += ' text-align:' + config.align + ';';
6582             }
6583             
6584             if(typeof(config.width) != 'undefined'){
6585                 td.style += ' width:' +  config.width + 'px;';
6586             }
6587             
6588             if(typeof(config.cursor) != 'undefined'){
6589                 td.style += ' cursor:' +  config.cursor + ';';
6590             }
6591             
6592             if(typeof(config.cls) != 'undefined'){
6593                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6594             }
6595             
6596             ['xs','sm','md','lg'].map(function(size){
6597                 
6598                 if(typeof(config[size]) == 'undefined'){
6599                     return;
6600                 }
6601                 
6602                 if (!config[size]) { // 0 = hidden
6603                     td.cls += ' hidden-' + size;
6604                     return;
6605                 }
6606                 
6607                 td.cls += ' col-' + size + '-' + config[size];
6608
6609             });
6610              
6611             row.cn.push(td);
6612            
6613         }
6614         
6615         row.cellObjects = cellObjects;
6616         
6617         return row;
6618           
6619     },
6620     
6621     
6622     
6623     onBeforeLoad : function()
6624     {
6625         //Roo.log('ds onBeforeLoad');
6626         
6627         //this.clear();
6628         
6629         //if(this.loadMask){
6630         //    this.maskEl.show();
6631         //}
6632     },
6633      /**
6634      * Remove all rows
6635      */
6636     clear : function()
6637     {
6638         this.el.select('tbody', true).first().dom.innerHTML = '';
6639     },
6640     /**
6641      * Show or hide a row.
6642      * @param {Number} rowIndex to show or hide
6643      * @param {Boolean} state hide
6644      */
6645     setRowVisibility : function(rowIndex, state)
6646     {
6647         var bt = this.mainBody.dom;
6648         
6649         var rows = this.el.select('tbody > tr', true).elements;
6650         
6651         if(typeof(rows[rowIndex]) == 'undefined'){
6652             return;
6653         }
6654         rows[rowIndex].dom.style.display = state ? '' : 'none';
6655     },
6656     
6657     
6658     getSelectionModel : function(){
6659         if(!this.selModel){
6660             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6661         }
6662         return this.selModel;
6663     },
6664     /*
6665      * Render the Roo.bootstrap object from renderder
6666      */
6667     renderCellObject : function(r)
6668     {
6669         var _this = this;
6670         
6671         var t = r.cfg.render(r.container);
6672         
6673         if(r.cfg.cn){
6674             Roo.each(r.cfg.cn, function(c){
6675                 var child = {
6676                     container: t.getChildContainer(),
6677                     cfg: c
6678                 };
6679                 _this.renderCellObject(child);
6680             })
6681         }
6682     },
6683     
6684     getRowIndex : function(row)
6685     {
6686         var rowIndex = -1;
6687         
6688         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6689             if(el != row){
6690                 return;
6691             }
6692             
6693             rowIndex = index;
6694         });
6695         
6696         return rowIndex;
6697     },
6698      /**
6699      * Returns the grid's underlying element = used by panel.Grid
6700      * @return {Element} The element
6701      */
6702     getGridEl : function(){
6703         return this.el;
6704     },
6705      /**
6706      * Forces a resize - used by panel.Grid
6707      * @return {Element} The element
6708      */
6709     autoSize : function()
6710     {
6711         //var ctr = Roo.get(this.container.dom.parentElement);
6712         var ctr = Roo.get(this.el.dom);
6713         
6714         var thd = this.getGridEl().select('thead',true).first();
6715         var tbd = this.getGridEl().select('tbody', true).first();
6716         
6717         
6718         var cw = ctr.getWidth();
6719         
6720         if (tbd) {
6721             
6722             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6723             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6724             cw -= barsize;
6725         }
6726         cw = Math.max(cw, this.totalWidth);
6727         this.getGridEl().select('tr',true).setWidth(cw);
6728         // resize 'expandable coloumn?
6729         
6730         return; // we doe not have a view in this design..
6731         
6732     },
6733     onBodyScroll: function()
6734     {
6735         
6736         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6737         this.mainHead.setStyle({
6738                     'position' : 'relative',
6739                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6740         });
6741         
6742         
6743     }
6744 });
6745
6746  
6747
6748  /*
6749  * - LGPL
6750  *
6751  * table cell
6752  * 
6753  */
6754
6755 /**
6756  * @class Roo.bootstrap.TableCell
6757  * @extends Roo.bootstrap.Component
6758  * Bootstrap TableCell class
6759  * @cfg {String} html cell contain text
6760  * @cfg {String} cls cell class
6761  * @cfg {String} tag cell tag (td|th) default td
6762  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6763  * @cfg {String} align Aligns the content in a cell
6764  * @cfg {String} axis Categorizes cells
6765  * @cfg {String} bgcolor Specifies the background color of a cell
6766  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6767  * @cfg {Number} colspan Specifies the number of columns a cell should span
6768  * @cfg {String} headers Specifies one or more header cells a cell is related to
6769  * @cfg {Number} height Sets the height of a cell
6770  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6771  * @cfg {Number} rowspan Sets the number of rows a cell should span
6772  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6773  * @cfg {String} valign Vertical aligns the content in a cell
6774  * @cfg {Number} width Specifies the width of a cell
6775  * 
6776  * @constructor
6777  * Create a new TableCell
6778  * @param {Object} config The config object
6779  */
6780
6781 Roo.bootstrap.TableCell = function(config){
6782     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6783 };
6784
6785 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6786     
6787     html: false,
6788     cls: false,
6789     tag: false,
6790     abbr: false,
6791     align: false,
6792     axis: false,
6793     bgcolor: false,
6794     charoff: false,
6795     colspan: false,
6796     headers: false,
6797     height: false,
6798     nowrap: false,
6799     rowspan: false,
6800     scope: false,
6801     valign: false,
6802     width: false,
6803     
6804     
6805     getAutoCreate : function(){
6806         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6807         
6808         cfg = {
6809             tag: 'td'
6810         };
6811         
6812         if(this.tag){
6813             cfg.tag = this.tag;
6814         }
6815         
6816         if (this.html) {
6817             cfg.html=this.html
6818         }
6819         if (this.cls) {
6820             cfg.cls=this.cls
6821         }
6822         if (this.abbr) {
6823             cfg.abbr=this.abbr
6824         }
6825         if (this.align) {
6826             cfg.align=this.align
6827         }
6828         if (this.axis) {
6829             cfg.axis=this.axis
6830         }
6831         if (this.bgcolor) {
6832             cfg.bgcolor=this.bgcolor
6833         }
6834         if (this.charoff) {
6835             cfg.charoff=this.charoff
6836         }
6837         if (this.colspan) {
6838             cfg.colspan=this.colspan
6839         }
6840         if (this.headers) {
6841             cfg.headers=this.headers
6842         }
6843         if (this.height) {
6844             cfg.height=this.height
6845         }
6846         if (this.nowrap) {
6847             cfg.nowrap=this.nowrap
6848         }
6849         if (this.rowspan) {
6850             cfg.rowspan=this.rowspan
6851         }
6852         if (this.scope) {
6853             cfg.scope=this.scope
6854         }
6855         if (this.valign) {
6856             cfg.valign=this.valign
6857         }
6858         if (this.width) {
6859             cfg.width=this.width
6860         }
6861         
6862         
6863         return cfg;
6864     }
6865    
6866 });
6867
6868  
6869
6870  /*
6871  * - LGPL
6872  *
6873  * table row
6874  * 
6875  */
6876
6877 /**
6878  * @class Roo.bootstrap.TableRow
6879  * @extends Roo.bootstrap.Component
6880  * Bootstrap TableRow class
6881  * @cfg {String} cls row class
6882  * @cfg {String} align Aligns the content in a table row
6883  * @cfg {String} bgcolor Specifies a background color for a table row
6884  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6885  * @cfg {String} valign Vertical aligns the content in a table row
6886  * 
6887  * @constructor
6888  * Create a new TableRow
6889  * @param {Object} config The config object
6890  */
6891
6892 Roo.bootstrap.TableRow = function(config){
6893     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6894 };
6895
6896 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6897     
6898     cls: false,
6899     align: false,
6900     bgcolor: false,
6901     charoff: false,
6902     valign: false,
6903     
6904     getAutoCreate : function(){
6905         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6906         
6907         cfg = {
6908             tag: 'tr'
6909         };
6910             
6911         if(this.cls){
6912             cfg.cls = this.cls;
6913         }
6914         if(this.align){
6915             cfg.align = this.align;
6916         }
6917         if(this.bgcolor){
6918             cfg.bgcolor = this.bgcolor;
6919         }
6920         if(this.charoff){
6921             cfg.charoff = this.charoff;
6922         }
6923         if(this.valign){
6924             cfg.valign = this.valign;
6925         }
6926         
6927         return cfg;
6928     }
6929    
6930 });
6931
6932  
6933
6934  /*
6935  * - LGPL
6936  *
6937  * table body
6938  * 
6939  */
6940
6941 /**
6942  * @class Roo.bootstrap.TableBody
6943  * @extends Roo.bootstrap.Component
6944  * Bootstrap TableBody class
6945  * @cfg {String} cls element class
6946  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6947  * @cfg {String} align Aligns the content inside the element
6948  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6949  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6950  * 
6951  * @constructor
6952  * Create a new TableBody
6953  * @param {Object} config The config object
6954  */
6955
6956 Roo.bootstrap.TableBody = function(config){
6957     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6958 };
6959
6960 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6961     
6962     cls: false,
6963     tag: false,
6964     align: false,
6965     charoff: false,
6966     valign: false,
6967     
6968     getAutoCreate : function(){
6969         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6970         
6971         cfg = {
6972             tag: 'tbody'
6973         };
6974             
6975         if (this.cls) {
6976             cfg.cls=this.cls
6977         }
6978         if(this.tag){
6979             cfg.tag = this.tag;
6980         }
6981         
6982         if(this.align){
6983             cfg.align = this.align;
6984         }
6985         if(this.charoff){
6986             cfg.charoff = this.charoff;
6987         }
6988         if(this.valign){
6989             cfg.valign = this.valign;
6990         }
6991         
6992         return cfg;
6993     }
6994     
6995     
6996 //    initEvents : function()
6997 //    {
6998 //        
6999 //        if(!this.store){
7000 //            return;
7001 //        }
7002 //        
7003 //        this.store = Roo.factory(this.store, Roo.data);
7004 //        this.store.on('load', this.onLoad, this);
7005 //        
7006 //        this.store.load();
7007 //        
7008 //    },
7009 //    
7010 //    onLoad: function () 
7011 //    {   
7012 //        this.fireEvent('load', this);
7013 //    }
7014 //    
7015 //   
7016 });
7017
7018  
7019
7020  /*
7021  * Based on:
7022  * Ext JS Library 1.1.1
7023  * Copyright(c) 2006-2007, Ext JS, LLC.
7024  *
7025  * Originally Released Under LGPL - original licence link has changed is not relivant.
7026  *
7027  * Fork - LGPL
7028  * <script type="text/javascript">
7029  */
7030
7031 // as we use this in bootstrap.
7032 Roo.namespace('Roo.form');
7033  /**
7034  * @class Roo.form.Action
7035  * Internal Class used to handle form actions
7036  * @constructor
7037  * @param {Roo.form.BasicForm} el The form element or its id
7038  * @param {Object} config Configuration options
7039  */
7040
7041  
7042  
7043 // define the action interface
7044 Roo.form.Action = function(form, options){
7045     this.form = form;
7046     this.options = options || {};
7047 };
7048 /**
7049  * Client Validation Failed
7050  * @const 
7051  */
7052 Roo.form.Action.CLIENT_INVALID = 'client';
7053 /**
7054  * Server Validation Failed
7055  * @const 
7056  */
7057 Roo.form.Action.SERVER_INVALID = 'server';
7058  /**
7059  * Connect to Server Failed
7060  * @const 
7061  */
7062 Roo.form.Action.CONNECT_FAILURE = 'connect';
7063 /**
7064  * Reading Data from Server Failed
7065  * @const 
7066  */
7067 Roo.form.Action.LOAD_FAILURE = 'load';
7068
7069 Roo.form.Action.prototype = {
7070     type : 'default',
7071     failureType : undefined,
7072     response : undefined,
7073     result : undefined,
7074
7075     // interface method
7076     run : function(options){
7077
7078     },
7079
7080     // interface method
7081     success : function(response){
7082
7083     },
7084
7085     // interface method
7086     handleResponse : function(response){
7087
7088     },
7089
7090     // default connection failure
7091     failure : function(response){
7092         
7093         this.response = response;
7094         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7095         this.form.afterAction(this, false);
7096     },
7097
7098     processResponse : function(response){
7099         this.response = response;
7100         if(!response.responseText){
7101             return true;
7102         }
7103         this.result = this.handleResponse(response);
7104         return this.result;
7105     },
7106
7107     // utility functions used internally
7108     getUrl : function(appendParams){
7109         var url = this.options.url || this.form.url || this.form.el.dom.action;
7110         if(appendParams){
7111             var p = this.getParams();
7112             if(p){
7113                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7114             }
7115         }
7116         return url;
7117     },
7118
7119     getMethod : function(){
7120         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7121     },
7122
7123     getParams : function(){
7124         var bp = this.form.baseParams;
7125         var p = this.options.params;
7126         if(p){
7127             if(typeof p == "object"){
7128                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7129             }else if(typeof p == 'string' && bp){
7130                 p += '&' + Roo.urlEncode(bp);
7131             }
7132         }else if(bp){
7133             p = Roo.urlEncode(bp);
7134         }
7135         return p;
7136     },
7137
7138     createCallback : function(){
7139         return {
7140             success: this.success,
7141             failure: this.failure,
7142             scope: this,
7143             timeout: (this.form.timeout*1000),
7144             upload: this.form.fileUpload ? this.success : undefined
7145         };
7146     }
7147 };
7148
7149 Roo.form.Action.Submit = function(form, options){
7150     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7151 };
7152
7153 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7154     type : 'submit',
7155
7156     haveProgress : false,
7157     uploadComplete : false,
7158     
7159     // uploadProgress indicator.
7160     uploadProgress : function()
7161     {
7162         if (!this.form.progressUrl) {
7163             return;
7164         }
7165         
7166         if (!this.haveProgress) {
7167             Roo.MessageBox.progress("Uploading", "Uploading");
7168         }
7169         if (this.uploadComplete) {
7170            Roo.MessageBox.hide();
7171            return;
7172         }
7173         
7174         this.haveProgress = true;
7175    
7176         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7177         
7178         var c = new Roo.data.Connection();
7179         c.request({
7180             url : this.form.progressUrl,
7181             params: {
7182                 id : uid
7183             },
7184             method: 'GET',
7185             success : function(req){
7186                //console.log(data);
7187                 var rdata = false;
7188                 var edata;
7189                 try  {
7190                    rdata = Roo.decode(req.responseText)
7191                 } catch (e) {
7192                     Roo.log("Invalid data from server..");
7193                     Roo.log(edata);
7194                     return;
7195                 }
7196                 if (!rdata || !rdata.success) {
7197                     Roo.log(rdata);
7198                     Roo.MessageBox.alert(Roo.encode(rdata));
7199                     return;
7200                 }
7201                 var data = rdata.data;
7202                 
7203                 if (this.uploadComplete) {
7204                    Roo.MessageBox.hide();
7205                    return;
7206                 }
7207                    
7208                 if (data){
7209                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7210                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7211                     );
7212                 }
7213                 this.uploadProgress.defer(2000,this);
7214             },
7215        
7216             failure: function(data) {
7217                 Roo.log('progress url failed ');
7218                 Roo.log(data);
7219             },
7220             scope : this
7221         });
7222            
7223     },
7224     
7225     
7226     run : function()
7227     {
7228         // run get Values on the form, so it syncs any secondary forms.
7229         this.form.getValues();
7230         
7231         var o = this.options;
7232         var method = this.getMethod();
7233         var isPost = method == 'POST';
7234         if(o.clientValidation === false || this.form.isValid()){
7235             
7236             if (this.form.progressUrl) {
7237                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7238                     (new Date() * 1) + '' + Math.random());
7239                     
7240             } 
7241             
7242             
7243             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7244                 form:this.form.el.dom,
7245                 url:this.getUrl(!isPost),
7246                 method: method,
7247                 params:isPost ? this.getParams() : null,
7248                 isUpload: this.form.fileUpload
7249             }));
7250             
7251             this.uploadProgress();
7252
7253         }else if (o.clientValidation !== false){ // client validation failed
7254             this.failureType = Roo.form.Action.CLIENT_INVALID;
7255             this.form.afterAction(this, false);
7256         }
7257     },
7258
7259     success : function(response)
7260     {
7261         this.uploadComplete= true;
7262         if (this.haveProgress) {
7263             Roo.MessageBox.hide();
7264         }
7265         
7266         
7267         var result = this.processResponse(response);
7268         if(result === true || result.success){
7269             this.form.afterAction(this, true);
7270             return;
7271         }
7272         if(result.errors){
7273             this.form.markInvalid(result.errors);
7274             this.failureType = Roo.form.Action.SERVER_INVALID;
7275         }
7276         this.form.afterAction(this, false);
7277     },
7278     failure : function(response)
7279     {
7280         this.uploadComplete= true;
7281         if (this.haveProgress) {
7282             Roo.MessageBox.hide();
7283         }
7284         
7285         this.response = response;
7286         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7287         this.form.afterAction(this, false);
7288     },
7289     
7290     handleResponse : function(response){
7291         if(this.form.errorReader){
7292             var rs = this.form.errorReader.read(response);
7293             var errors = [];
7294             if(rs.records){
7295                 for(var i = 0, len = rs.records.length; i < len; i++) {
7296                     var r = rs.records[i];
7297                     errors[i] = r.data;
7298                 }
7299             }
7300             if(errors.length < 1){
7301                 errors = null;
7302             }
7303             return {
7304                 success : rs.success,
7305                 errors : errors
7306             };
7307         }
7308         var ret = false;
7309         try {
7310             ret = Roo.decode(response.responseText);
7311         } catch (e) {
7312             ret = {
7313                 success: false,
7314                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7315                 errors : []
7316             };
7317         }
7318         return ret;
7319         
7320     }
7321 });
7322
7323
7324 Roo.form.Action.Load = function(form, options){
7325     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7326     this.reader = this.form.reader;
7327 };
7328
7329 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7330     type : 'load',
7331
7332     run : function(){
7333         
7334         Roo.Ajax.request(Roo.apply(
7335                 this.createCallback(), {
7336                     method:this.getMethod(),
7337                     url:this.getUrl(false),
7338                     params:this.getParams()
7339         }));
7340     },
7341
7342     success : function(response){
7343         
7344         var result = this.processResponse(response);
7345         if(result === true || !result.success || !result.data){
7346             this.failureType = Roo.form.Action.LOAD_FAILURE;
7347             this.form.afterAction(this, false);
7348             return;
7349         }
7350         this.form.clearInvalid();
7351         this.form.setValues(result.data);
7352         this.form.afterAction(this, true);
7353     },
7354
7355     handleResponse : function(response){
7356         if(this.form.reader){
7357             var rs = this.form.reader.read(response);
7358             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7359             return {
7360                 success : rs.success,
7361                 data : data
7362             };
7363         }
7364         return Roo.decode(response.responseText);
7365     }
7366 });
7367
7368 Roo.form.Action.ACTION_TYPES = {
7369     'load' : Roo.form.Action.Load,
7370     'submit' : Roo.form.Action.Submit
7371 };/*
7372  * - LGPL
7373  *
7374  * form
7375  * 
7376  */
7377
7378 /**
7379  * @class Roo.bootstrap.Form
7380  * @extends Roo.bootstrap.Component
7381  * Bootstrap Form class
7382  * @cfg {String} method  GET | POST (default POST)
7383  * @cfg {String} labelAlign top | left (default top)
7384  * @cfg {String} align left  | right - for navbars
7385  * @cfg {Boolean} loadMask load mask when submit (default true)
7386
7387  * 
7388  * @constructor
7389  * Create a new Form
7390  * @param {Object} config The config object
7391  */
7392
7393
7394 Roo.bootstrap.Form = function(config){
7395     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7396     this.addEvents({
7397         /**
7398          * @event clientvalidation
7399          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7400          * @param {Form} this
7401          * @param {Boolean} valid true if the form has passed client-side validation
7402          */
7403         clientvalidation: true,
7404         /**
7405          * @event beforeaction
7406          * Fires before any action is performed. Return false to cancel the action.
7407          * @param {Form} this
7408          * @param {Action} action The action to be performed
7409          */
7410         beforeaction: true,
7411         /**
7412          * @event actionfailed
7413          * Fires when an action fails.
7414          * @param {Form} this
7415          * @param {Action} action The action that failed
7416          */
7417         actionfailed : true,
7418         /**
7419          * @event actioncomplete
7420          * Fires when an action is completed.
7421          * @param {Form} this
7422          * @param {Action} action The action that completed
7423          */
7424         actioncomplete : true
7425     });
7426     
7427 };
7428
7429 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7430       
7431      /**
7432      * @cfg {String} method
7433      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7434      */
7435     method : 'POST',
7436     /**
7437      * @cfg {String} url
7438      * The URL to use for form actions if one isn't supplied in the action options.
7439      */
7440     /**
7441      * @cfg {Boolean} fileUpload
7442      * Set to true if this form is a file upload.
7443      */
7444      
7445     /**
7446      * @cfg {Object} baseParams
7447      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7448      */
7449       
7450     /**
7451      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7452      */
7453     timeout: 30,
7454     /**
7455      * @cfg {Sting} align (left|right) for navbar forms
7456      */
7457     align : 'left',
7458
7459     // private
7460     activeAction : null,
7461  
7462     /**
7463      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7464      * element by passing it or its id or mask the form itself by passing in true.
7465      * @type Mixed
7466      */
7467     waitMsgTarget : false,
7468     
7469     loadMask : true,
7470     
7471     getAutoCreate : function(){
7472         
7473         var cfg = {
7474             tag: 'form',
7475             method : this.method || 'POST',
7476             id : this.id || Roo.id(),
7477             cls : ''
7478         };
7479         if (this.parent().xtype.match(/^Nav/)) {
7480             cfg.cls = 'navbar-form navbar-' + this.align;
7481             
7482         }
7483         
7484         if (this.labelAlign == 'left' ) {
7485             cfg.cls += ' form-horizontal';
7486         }
7487         
7488         
7489         return cfg;
7490     },
7491     initEvents : function()
7492     {
7493         this.el.on('submit', this.onSubmit, this);
7494         // this was added as random key presses on the form where triggering form submit.
7495         this.el.on('keypress', function(e) {
7496             if (e.getCharCode() != 13) {
7497                 return true;
7498             }
7499             // we might need to allow it for textareas.. and some other items.
7500             // check e.getTarget().
7501             
7502             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7503                 return true;
7504             }
7505         
7506             Roo.log("keypress blocked");
7507             
7508             e.preventDefault();
7509             return false;
7510         });
7511         
7512     },
7513     // private
7514     onSubmit : function(e){
7515         e.stopEvent();
7516     },
7517     
7518      /**
7519      * Returns true if client-side validation on the form is successful.
7520      * @return Boolean
7521      */
7522     isValid : function(){
7523         var items = this.getItems();
7524         var valid = true;
7525         items.each(function(f){
7526            if(!f.validate()){
7527                valid = false;
7528                
7529            }
7530         });
7531         return valid;
7532     },
7533     /**
7534      * Returns true if any fields in this form have changed since their original load.
7535      * @return Boolean
7536      */
7537     isDirty : function(){
7538         var dirty = false;
7539         var items = this.getItems();
7540         items.each(function(f){
7541            if(f.isDirty()){
7542                dirty = true;
7543                return false;
7544            }
7545            return true;
7546         });
7547         return dirty;
7548     },
7549      /**
7550      * Performs a predefined action (submit or load) or custom actions you define on this form.
7551      * @param {String} actionName The name of the action type
7552      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7553      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7554      * accept other config options):
7555      * <pre>
7556 Property          Type             Description
7557 ----------------  ---------------  ----------------------------------------------------------------------------------
7558 url               String           The url for the action (defaults to the form's url)
7559 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7560 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7561 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7562                                    validate the form on the client (defaults to false)
7563      * </pre>
7564      * @return {BasicForm} this
7565      */
7566     doAction : function(action, options){
7567         if(typeof action == 'string'){
7568             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7569         }
7570         if(this.fireEvent('beforeaction', this, action) !== false){
7571             this.beforeAction(action);
7572             action.run.defer(100, action);
7573         }
7574         return this;
7575     },
7576     
7577     // private
7578     beforeAction : function(action){
7579         var o = action.options;
7580         
7581         if(this.loadMask){
7582             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7583         }
7584         // not really supported yet.. ??
7585         
7586         //if(this.waitMsgTarget === true){
7587         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7588         //}else if(this.waitMsgTarget){
7589         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7590         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7591         //}else {
7592         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7593        // }
7594          
7595     },
7596
7597     // private
7598     afterAction : function(action, success){
7599         this.activeAction = null;
7600         var o = action.options;
7601         
7602         //if(this.waitMsgTarget === true){
7603             this.el.unmask();
7604         //}else if(this.waitMsgTarget){
7605         //    this.waitMsgTarget.unmask();
7606         //}else{
7607         //    Roo.MessageBox.updateProgress(1);
7608         //    Roo.MessageBox.hide();
7609        // }
7610         // 
7611         if(success){
7612             if(o.reset){
7613                 this.reset();
7614             }
7615             Roo.callback(o.success, o.scope, [this, action]);
7616             this.fireEvent('actioncomplete', this, action);
7617             
7618         }else{
7619             
7620             // failure condition..
7621             // we have a scenario where updates need confirming.
7622             // eg. if a locking scenario exists..
7623             // we look for { errors : { needs_confirm : true }} in the response.
7624             if (
7625                 (typeof(action.result) != 'undefined')  &&
7626                 (typeof(action.result.errors) != 'undefined')  &&
7627                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7628            ){
7629                 var _t = this;
7630                 Roo.log("not supported yet");
7631                  /*
7632                 
7633                 Roo.MessageBox.confirm(
7634                     "Change requires confirmation",
7635                     action.result.errorMsg,
7636                     function(r) {
7637                         if (r != 'yes') {
7638                             return;
7639                         }
7640                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7641                     }
7642                     
7643                 );
7644                 */
7645                 
7646                 
7647                 return;
7648             }
7649             
7650             Roo.callback(o.failure, o.scope, [this, action]);
7651             // show an error message if no failed handler is set..
7652             if (!this.hasListener('actionfailed')) {
7653                 Roo.log("need to add dialog support");
7654                 /*
7655                 Roo.MessageBox.alert("Error",
7656                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7657                         action.result.errorMsg :
7658                         "Saving Failed, please check your entries or try again"
7659                 );
7660                 */
7661             }
7662             
7663             this.fireEvent('actionfailed', this, action);
7664         }
7665         
7666     },
7667     /**
7668      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7669      * @param {String} id The value to search for
7670      * @return Field
7671      */
7672     findField : function(id){
7673         var items = this.getItems();
7674         var field = items.get(id);
7675         if(!field){
7676              items.each(function(f){
7677                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7678                     field = f;
7679                     return false;
7680                 }
7681                 return true;
7682             });
7683         }
7684         return field || null;
7685     },
7686      /**
7687      * Mark fields in this form invalid in bulk.
7688      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7689      * @return {BasicForm} this
7690      */
7691     markInvalid : function(errors){
7692         if(errors instanceof Array){
7693             for(var i = 0, len = errors.length; i < len; i++){
7694                 var fieldError = errors[i];
7695                 var f = this.findField(fieldError.id);
7696                 if(f){
7697                     f.markInvalid(fieldError.msg);
7698                 }
7699             }
7700         }else{
7701             var field, id;
7702             for(id in errors){
7703                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7704                     field.markInvalid(errors[id]);
7705                 }
7706             }
7707         }
7708         //Roo.each(this.childForms || [], function (f) {
7709         //    f.markInvalid(errors);
7710         //});
7711         
7712         return this;
7713     },
7714
7715     /**
7716      * Set values for fields in this form in bulk.
7717      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7718      * @return {BasicForm} this
7719      */
7720     setValues : function(values){
7721         if(values instanceof Array){ // array of objects
7722             for(var i = 0, len = values.length; i < len; i++){
7723                 var v = values[i];
7724                 var f = this.findField(v.id);
7725                 if(f){
7726                     f.setValue(v.value);
7727                     if(this.trackResetOnLoad){
7728                         f.originalValue = f.getValue();
7729                     }
7730                 }
7731             }
7732         }else{ // object hash
7733             var field, id;
7734             for(id in values){
7735                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7736                     
7737                     if (field.setFromData && 
7738                         field.valueField && 
7739                         field.displayField &&
7740                         // combos' with local stores can 
7741                         // be queried via setValue()
7742                         // to set their value..
7743                         (field.store && !field.store.isLocal)
7744                         ) {
7745                         // it's a combo
7746                         var sd = { };
7747                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7748                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7749                         field.setFromData(sd);
7750                         
7751                     } else {
7752                         field.setValue(values[id]);
7753                     }
7754                     
7755                     
7756                     if(this.trackResetOnLoad){
7757                         field.originalValue = field.getValue();
7758                     }
7759                 }
7760             }
7761         }
7762          
7763         //Roo.each(this.childForms || [], function (f) {
7764         //    f.setValues(values);
7765         //});
7766                 
7767         return this;
7768     },
7769
7770     /**
7771      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7772      * they are returned as an array.
7773      * @param {Boolean} asString
7774      * @return {Object}
7775      */
7776     getValues : function(asString){
7777         //if (this.childForms) {
7778             // copy values from the child forms
7779         //    Roo.each(this.childForms, function (f) {
7780         //        this.setValues(f.getValues());
7781         //    }, this);
7782         //}
7783         
7784         
7785         
7786         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7787         if(asString === true){
7788             return fs;
7789         }
7790         return Roo.urlDecode(fs);
7791     },
7792     
7793     /**
7794      * Returns the fields in this form as an object with key/value pairs. 
7795      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7796      * @return {Object}
7797      */
7798     getFieldValues : function(with_hidden)
7799     {
7800         var items = this.getItems();
7801         var ret = {};
7802         items.each(function(f){
7803             if (!f.getName()) {
7804                 return;
7805             }
7806             var v = f.getValue();
7807             if (f.inputType =='radio') {
7808                 if (typeof(ret[f.getName()]) == 'undefined') {
7809                     ret[f.getName()] = ''; // empty..
7810                 }
7811                 
7812                 if (!f.el.dom.checked) {
7813                     return;
7814                     
7815                 }
7816                 v = f.el.dom.value;
7817                 
7818             }
7819             
7820             // not sure if this supported any more..
7821             if ((typeof(v) == 'object') && f.getRawValue) {
7822                 v = f.getRawValue() ; // dates..
7823             }
7824             // combo boxes where name != hiddenName...
7825             if (f.name != f.getName()) {
7826                 ret[f.name] = f.getRawValue();
7827             }
7828             ret[f.getName()] = v;
7829         });
7830         
7831         return ret;
7832     },
7833
7834     /**
7835      * Clears all invalid messages in this form.
7836      * @return {BasicForm} this
7837      */
7838     clearInvalid : function(){
7839         var items = this.getItems();
7840         
7841         items.each(function(f){
7842            f.clearInvalid();
7843         });
7844         
7845         
7846         
7847         return this;
7848     },
7849
7850     /**
7851      * Resets this form.
7852      * @return {BasicForm} this
7853      */
7854     reset : function(){
7855         var items = this.getItems();
7856         items.each(function(f){
7857             f.reset();
7858         });
7859         
7860         Roo.each(this.childForms || [], function (f) {
7861             f.reset();
7862         });
7863        
7864         
7865         return this;
7866     },
7867     getItems : function()
7868     {
7869         var r=new Roo.util.MixedCollection(false, function(o){
7870             return o.id || (o.id = Roo.id());
7871         });
7872         var iter = function(el) {
7873             if (el.inputEl) {
7874                 r.add(el);
7875             }
7876             if (!el.items) {
7877                 return;
7878             }
7879             Roo.each(el.items,function(e) {
7880                 iter(e);
7881             });
7882             
7883             
7884         };
7885         
7886         iter(this);
7887         return r;
7888         
7889         
7890         
7891         
7892     }
7893     
7894 });
7895
7896  
7897 /*
7898  * Based on:
7899  * Ext JS Library 1.1.1
7900  * Copyright(c) 2006-2007, Ext JS, LLC.
7901  *
7902  * Originally Released Under LGPL - original licence link has changed is not relivant.
7903  *
7904  * Fork - LGPL
7905  * <script type="text/javascript">
7906  */
7907 /**
7908  * @class Roo.form.VTypes
7909  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7910  * @singleton
7911  */
7912 Roo.form.VTypes = function(){
7913     // closure these in so they are only created once.
7914     var alpha = /^[a-zA-Z_]+$/;
7915     var alphanum = /^[a-zA-Z0-9_]+$/;
7916     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7917     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7918
7919     // All these messages and functions are configurable
7920     return {
7921         /**
7922          * The function used to validate email addresses
7923          * @param {String} value The email address
7924          */
7925         'email' : function(v){
7926             return email.test(v);
7927         },
7928         /**
7929          * The error text to display when the email validation function returns false
7930          * @type String
7931          */
7932         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7933         /**
7934          * The keystroke filter mask to be applied on email input
7935          * @type RegExp
7936          */
7937         'emailMask' : /[a-z0-9_\.\-@]/i,
7938
7939         /**
7940          * The function used to validate URLs
7941          * @param {String} value The URL
7942          */
7943         'url' : function(v){
7944             return url.test(v);
7945         },
7946         /**
7947          * The error text to display when the url validation function returns false
7948          * @type String
7949          */
7950         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7951         
7952         /**
7953          * The function used to validate alpha values
7954          * @param {String} value The value
7955          */
7956         'alpha' : function(v){
7957             return alpha.test(v);
7958         },
7959         /**
7960          * The error text to display when the alpha validation function returns false
7961          * @type String
7962          */
7963         'alphaText' : 'This field should only contain letters and _',
7964         /**
7965          * The keystroke filter mask to be applied on alpha input
7966          * @type RegExp
7967          */
7968         'alphaMask' : /[a-z_]/i,
7969
7970         /**
7971          * The function used to validate alphanumeric values
7972          * @param {String} value The value
7973          */
7974         'alphanum' : function(v){
7975             return alphanum.test(v);
7976         },
7977         /**
7978          * The error text to display when the alphanumeric validation function returns false
7979          * @type String
7980          */
7981         'alphanumText' : 'This field should only contain letters, numbers and _',
7982         /**
7983          * The keystroke filter mask to be applied on alphanumeric input
7984          * @type RegExp
7985          */
7986         'alphanumMask' : /[a-z0-9_]/i
7987     };
7988 }();/*
7989  * - LGPL
7990  *
7991  * Input
7992  * 
7993  */
7994
7995 /**
7996  * @class Roo.bootstrap.Input
7997  * @extends Roo.bootstrap.Component
7998  * Bootstrap Input class
7999  * @cfg {Boolean} disabled is it disabled
8000  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8001  * @cfg {String} name name of the input
8002  * @cfg {string} fieldLabel - the label associated
8003  * @cfg {string} placeholder - placeholder to put in text.
8004  * @cfg {string}  before - input group add on before
8005  * @cfg {string} after - input group add on after
8006  * @cfg {string} size - (lg|sm) or leave empty..
8007  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8008  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8009  * @cfg {Number} md colspan out of 12 for computer-sized screens
8010  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8011  * @cfg {string} value default value of the input
8012  * @cfg {Number} labelWidth set the width of label (0-12)
8013  * @cfg {String} labelAlign (top|left)
8014  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8015  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8016  * @cfg {String} indicatorpos (left|right) default left
8017
8018  * @cfg {String} align (left|center|right) Default left
8019  * @cfg {Boolean} forceFeedback (true|false) Default false
8020  * 
8021  * 
8022  * 
8023  * 
8024  * @constructor
8025  * Create a new Input
8026  * @param {Object} config The config object
8027  */
8028
8029 Roo.bootstrap.Input = function(config){
8030     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8031    
8032         this.addEvents({
8033             /**
8034              * @event focus
8035              * Fires when this field receives input focus.
8036              * @param {Roo.form.Field} this
8037              */
8038             focus : true,
8039             /**
8040              * @event blur
8041              * Fires when this field loses input focus.
8042              * @param {Roo.form.Field} this
8043              */
8044             blur : true,
8045             /**
8046              * @event specialkey
8047              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8048              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8049              * @param {Roo.form.Field} this
8050              * @param {Roo.EventObject} e The event object
8051              */
8052             specialkey : true,
8053             /**
8054              * @event change
8055              * Fires just before the field blurs if the field value has changed.
8056              * @param {Roo.form.Field} this
8057              * @param {Mixed} newValue The new value
8058              * @param {Mixed} oldValue The original value
8059              */
8060             change : true,
8061             /**
8062              * @event invalid
8063              * Fires after the field has been marked as invalid.
8064              * @param {Roo.form.Field} this
8065              * @param {String} msg The validation message
8066              */
8067             invalid : true,
8068             /**
8069              * @event valid
8070              * Fires after the field has been validated with no errors.
8071              * @param {Roo.form.Field} this
8072              */
8073             valid : true,
8074              /**
8075              * @event keyup
8076              * Fires after the key up
8077              * @param {Roo.form.Field} this
8078              * @param {Roo.EventObject}  e The event Object
8079              */
8080             keyup : true
8081         });
8082 };
8083
8084 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8085      /**
8086      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8087       automatic validation (defaults to "keyup").
8088      */
8089     validationEvent : "keyup",
8090      /**
8091      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8092      */
8093     validateOnBlur : true,
8094     /**
8095      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8096      */
8097     validationDelay : 250,
8098      /**
8099      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8100      */
8101     focusClass : "x-form-focus",  // not needed???
8102     
8103        
8104     /**
8105      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8106      */
8107     invalidClass : "has-warning",
8108     
8109     /**
8110      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8111      */
8112     validClass : "has-success",
8113     
8114     /**
8115      * @cfg {Boolean} hasFeedback (true|false) default true
8116      */
8117     hasFeedback : true,
8118     
8119     /**
8120      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8121      */
8122     invalidFeedbackClass : "glyphicon-warning-sign",
8123     
8124     /**
8125      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8126      */
8127     validFeedbackClass : "glyphicon-ok",
8128     
8129     /**
8130      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8131      */
8132     selectOnFocus : false,
8133     
8134      /**
8135      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8136      */
8137     maskRe : null,
8138        /**
8139      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8140      */
8141     vtype : null,
8142     
8143       /**
8144      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8145      */
8146     disableKeyFilter : false,
8147     
8148        /**
8149      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8150      */
8151     disabled : false,
8152      /**
8153      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8154      */
8155     allowBlank : true,
8156     /**
8157      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8158      */
8159     blankText : "This field is required",
8160     
8161      /**
8162      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8163      */
8164     minLength : 0,
8165     /**
8166      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8167      */
8168     maxLength : Number.MAX_VALUE,
8169     /**
8170      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8171      */
8172     minLengthText : "The minimum length for this field is {0}",
8173     /**
8174      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8175      */
8176     maxLengthText : "The maximum length for this field is {0}",
8177   
8178     
8179     /**
8180      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8181      * If available, this function will be called only after the basic validators all return true, and will be passed the
8182      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8183      */
8184     validator : null,
8185     /**
8186      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8187      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8188      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8189      */
8190     regex : null,
8191     /**
8192      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8193      */
8194     regexText : "",
8195     
8196     autocomplete: false,
8197     
8198     
8199     fieldLabel : '',
8200     inputType : 'text',
8201     
8202     name : false,
8203     placeholder: false,
8204     before : false,
8205     after : false,
8206     size : false,
8207     hasFocus : false,
8208     preventMark: false,
8209     isFormField : true,
8210     value : '',
8211     labelWidth : 2,
8212     labelAlign : false,
8213     readOnly : false,
8214     align : false,
8215     formatedValue : false,
8216     forceFeedback : false,
8217     
8218     indicatorpos : 'left',
8219     
8220     parentLabelAlign : function()
8221     {
8222         var parent = this;
8223         while (parent.parent()) {
8224             parent = parent.parent();
8225             if (typeof(parent.labelAlign) !='undefined') {
8226                 return parent.labelAlign;
8227             }
8228         }
8229         return 'left';
8230         
8231     },
8232     
8233     getAutoCreate : function()
8234     {
8235         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8236         
8237         var id = Roo.id();
8238         
8239         var cfg = {};
8240         
8241         if(this.inputType != 'hidden'){
8242             cfg.cls = 'form-group' //input-group
8243         }
8244         
8245         var input =  {
8246             tag: 'input',
8247             id : id,
8248             type : this.inputType,
8249             value : this.value,
8250             cls : 'form-control',
8251             placeholder : this.placeholder || '',
8252             autocomplete : this.autocomplete || 'new-password'
8253         };
8254         
8255         if(this.align){
8256             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8257         }
8258         
8259         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8260             input.maxLength = this.maxLength;
8261         }
8262         
8263         if (this.disabled) {
8264             input.disabled=true;
8265         }
8266         
8267         if (this.readOnly) {
8268             input.readonly=true;
8269         }
8270         
8271         if (this.name) {
8272             input.name = this.name;
8273         }
8274         
8275         if (this.size) {
8276             input.cls += ' input-' + this.size;
8277         }
8278         
8279         var settings=this;
8280         ['xs','sm','md','lg'].map(function(size){
8281             if (settings[size]) {
8282                 cfg.cls += ' col-' + size + '-' + settings[size];
8283             }
8284         });
8285         
8286         var inputblock = input;
8287         
8288         var feedback = {
8289             tag: 'span',
8290             cls: 'glyphicon form-control-feedback'
8291         };
8292             
8293         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8294             
8295             inputblock = {
8296                 cls : 'has-feedback',
8297                 cn :  [
8298                     input,
8299                     feedback
8300                 ] 
8301             };  
8302         }
8303         
8304         if (this.before || this.after) {
8305             
8306             inputblock = {
8307                 cls : 'input-group',
8308                 cn :  [] 
8309             };
8310             
8311             if (this.before && typeof(this.before) == 'string') {
8312                 
8313                 inputblock.cn.push({
8314                     tag :'span',
8315                     cls : 'roo-input-before input-group-addon',
8316                     html : this.before
8317                 });
8318             }
8319             if (this.before && typeof(this.before) == 'object') {
8320                 this.before = Roo.factory(this.before);
8321                 
8322                 inputblock.cn.push({
8323                     tag :'span',
8324                     cls : 'roo-input-before input-group-' +
8325                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8326                 });
8327             }
8328             
8329             inputblock.cn.push(input);
8330             
8331             if (this.after && typeof(this.after) == 'string') {
8332                 inputblock.cn.push({
8333                     tag :'span',
8334                     cls : 'roo-input-after input-group-addon',
8335                     html : this.after
8336                 });
8337             }
8338             if (this.after && typeof(this.after) == 'object') {
8339                 this.after = Roo.factory(this.after);
8340                 
8341                 inputblock.cn.push({
8342                     tag :'span',
8343                     cls : 'roo-input-after input-group-' +
8344                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8345                 });
8346             }
8347             
8348             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8349                 inputblock.cls += ' has-feedback';
8350                 inputblock.cn.push(feedback);
8351             }
8352         };
8353         
8354         if (align ==='left' && this.fieldLabel.length) {
8355             
8356             cfg.cn = [
8357                 {
8358                     tag : 'i',
8359                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8360                     tooltip : 'This field is required'
8361                 },
8362                 {
8363                     tag: 'label',
8364                     'for' :  id,
8365                     cls : 'control-label col-sm-' + this.labelWidth,
8366                     html : this.fieldLabel
8367
8368                 },
8369                 {
8370                     cls : "col-sm-" + (12 - this.labelWidth), 
8371                     cn: [
8372                         inputblock
8373                     ]
8374                 }
8375
8376             ];
8377             
8378             if(this.indicatorpos == 'right'){
8379                 cfg.cn = [
8380                     {
8381                         tag: 'label',
8382                         'for' :  id,
8383                         cls : 'control-label col-sm-' + this.labelWidth,
8384                         html : this.fieldLabel
8385
8386                     },
8387                     {
8388                         tag : 'i',
8389                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8390                         tooltip : 'This field is required'
8391                     },
8392                     {
8393                         cls : "col-sm-" + (12 - this.labelWidth), 
8394                         cn: [
8395                             inputblock
8396                         ]
8397                     }
8398
8399                 ];
8400             }
8401             
8402         } else if ( this.fieldLabel.length) {
8403                 
8404             cfg.cn = [
8405                 {
8406                     tag : 'i',
8407                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8408                     tooltip : 'This field is required'
8409                 },
8410                 {
8411                     tag: 'label',
8412                    //cls : 'input-group-addon',
8413                     html : this.fieldLabel
8414
8415                 },
8416
8417                inputblock
8418
8419            ];
8420            
8421            if(this.indicatorpos == 'right'){
8422                 
8423                 cfg.cn = [
8424                     {
8425                         tag: 'label',
8426                        //cls : 'input-group-addon',
8427                         html : this.fieldLabel
8428
8429                     },
8430                     {
8431                         tag : 'i',
8432                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8433                         tooltip : 'This field is required'
8434                     },
8435
8436                    inputblock
8437
8438                ];
8439
8440             }
8441
8442         } else {
8443             
8444             cfg.cn = [
8445
8446                     inputblock
8447
8448             ];
8449                 
8450                 
8451         };
8452         
8453         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8454            cfg.cls += ' navbar-form';
8455         }
8456         
8457         if (this.parentType === 'NavGroup') {
8458            cfg.cls += ' navbar-form';
8459            cfg.tag = 'li';
8460         }
8461         
8462         return cfg;
8463         
8464     },
8465     /**
8466      * return the real input element.
8467      */
8468     inputEl: function ()
8469     {
8470         return this.el.select('input.form-control',true).first();
8471     },
8472     
8473     tooltipEl : function()
8474     {
8475         return this.inputEl();
8476     },
8477     
8478     indicatorEl : function()
8479     {
8480         var indicator = this.el.select('i.roo-required-indicator',true).first();
8481         
8482         if(!indicator){
8483             return false;
8484         }
8485         
8486         return indicator;
8487         
8488     },
8489     
8490     setDisabled : function(v)
8491     {
8492         var i  = this.inputEl().dom;
8493         if (!v) {
8494             i.removeAttribute('disabled');
8495             return;
8496             
8497         }
8498         i.setAttribute('disabled','true');
8499     },
8500     initEvents : function()
8501     {
8502           
8503         this.inputEl().on("keydown" , this.fireKey,  this);
8504         this.inputEl().on("focus", this.onFocus,  this);
8505         this.inputEl().on("blur", this.onBlur,  this);
8506         
8507         this.inputEl().relayEvent('keyup', this);
8508         
8509         this.indicator = this.indicatorEl();
8510         
8511         if(this.indicator){
8512             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8513             this.indicator.hide();
8514         }
8515  
8516         // reference to original value for reset
8517         this.originalValue = this.getValue();
8518         //Roo.form.TextField.superclass.initEvents.call(this);
8519         if(this.validationEvent == 'keyup'){
8520             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8521             this.inputEl().on('keyup', this.filterValidation, this);
8522         }
8523         else if(this.validationEvent !== false){
8524             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8525         }
8526         
8527         if(this.selectOnFocus){
8528             this.on("focus", this.preFocus, this);
8529             
8530         }
8531         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8532             this.inputEl().on("keypress", this.filterKeys, this);
8533         }
8534        /* if(this.grow){
8535             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8536             this.el.on("click", this.autoSize,  this);
8537         }
8538         */
8539         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8540             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8541         }
8542         
8543         if (typeof(this.before) == 'object') {
8544             this.before.render(this.el.select('.roo-input-before',true).first());
8545         }
8546         if (typeof(this.after) == 'object') {
8547             this.after.render(this.el.select('.roo-input-after',true).first());
8548         }
8549         
8550         
8551     },
8552     filterValidation : function(e){
8553         if(!e.isNavKeyPress()){
8554             this.validationTask.delay(this.validationDelay);
8555         }
8556     },
8557      /**
8558      * Validates the field value
8559      * @return {Boolean} True if the value is valid, else false
8560      */
8561     validate : function(){
8562         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8563         if(this.disabled || this.validateValue(this.getRawValue())){
8564             this.markValid();
8565             return true;
8566         }
8567         
8568         this.markInvalid();
8569         return false;
8570     },
8571     
8572     
8573     /**
8574      * Validates a value according to the field's validation rules and marks the field as invalid
8575      * if the validation fails
8576      * @param {Mixed} value The value to validate
8577      * @return {Boolean} True if the value is valid, else false
8578      */
8579     validateValue : function(value){
8580         if(value.length < 1)  { // if it's blank
8581             if(this.allowBlank){
8582                 return true;
8583             }
8584             return false;
8585         }
8586         
8587         if(value.length < this.minLength){
8588             return false;
8589         }
8590         if(value.length > this.maxLength){
8591             return false;
8592         }
8593         if(this.vtype){
8594             var vt = Roo.form.VTypes;
8595             if(!vt[this.vtype](value, this)){
8596                 return false;
8597             }
8598         }
8599         if(typeof this.validator == "function"){
8600             var msg = this.validator(value);
8601             if(msg !== true){
8602                 return false;
8603             }
8604         }
8605         
8606         if(this.regex && !this.regex.test(value)){
8607             return false;
8608         }
8609         
8610         return true;
8611     },
8612
8613     
8614     
8615      // private
8616     fireKey : function(e){
8617         //Roo.log('field ' + e.getKey());
8618         if(e.isNavKeyPress()){
8619             this.fireEvent("specialkey", this, e);
8620         }
8621     },
8622     focus : function (selectText){
8623         if(this.rendered){
8624             this.inputEl().focus();
8625             if(selectText === true){
8626                 this.inputEl().dom.select();
8627             }
8628         }
8629         return this;
8630     } ,
8631     
8632     onFocus : function(){
8633         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8634            // this.el.addClass(this.focusClass);
8635         }
8636         if(!this.hasFocus){
8637             this.hasFocus = true;
8638             this.startValue = this.getValue();
8639             this.fireEvent("focus", this);
8640         }
8641     },
8642     
8643     beforeBlur : Roo.emptyFn,
8644
8645     
8646     // private
8647     onBlur : function(){
8648         this.beforeBlur();
8649         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8650             //this.el.removeClass(this.focusClass);
8651         }
8652         this.hasFocus = false;
8653         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8654             this.validate();
8655         }
8656         var v = this.getValue();
8657         if(String(v) !== String(this.startValue)){
8658             this.fireEvent('change', this, v, this.startValue);
8659         }
8660         this.fireEvent("blur", this);
8661     },
8662     
8663     /**
8664      * Resets the current field value to the originally loaded value and clears any validation messages
8665      */
8666     reset : function(){
8667         this.setValue(this.originalValue);
8668         this.validate();
8669     },
8670      /**
8671      * Returns the name of the field
8672      * @return {Mixed} name The name field
8673      */
8674     getName: function(){
8675         return this.name;
8676     },
8677      /**
8678      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8679      * @return {Mixed} value The field value
8680      */
8681     getValue : function(){
8682         
8683         var v = this.inputEl().getValue();
8684         
8685         return v;
8686     },
8687     /**
8688      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8689      * @return {Mixed} value The field value
8690      */
8691     getRawValue : function(){
8692         var v = this.inputEl().getValue();
8693         
8694         return v;
8695     },
8696     
8697     /**
8698      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8699      * @param {Mixed} value The value to set
8700      */
8701     setRawValue : function(v){
8702         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8703     },
8704     
8705     selectText : function(start, end){
8706         var v = this.getRawValue();
8707         if(v.length > 0){
8708             start = start === undefined ? 0 : start;
8709             end = end === undefined ? v.length : end;
8710             var d = this.inputEl().dom;
8711             if(d.setSelectionRange){
8712                 d.setSelectionRange(start, end);
8713             }else if(d.createTextRange){
8714                 var range = d.createTextRange();
8715                 range.moveStart("character", start);
8716                 range.moveEnd("character", v.length-end);
8717                 range.select();
8718             }
8719         }
8720     },
8721     
8722     /**
8723      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8724      * @param {Mixed} value The value to set
8725      */
8726     setValue : function(v){
8727         this.value = v;
8728         if(this.rendered){
8729             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8730             this.validate();
8731         }
8732     },
8733     
8734     /*
8735     processValue : function(value){
8736         if(this.stripCharsRe){
8737             var newValue = value.replace(this.stripCharsRe, '');
8738             if(newValue !== value){
8739                 this.setRawValue(newValue);
8740                 return newValue;
8741             }
8742         }
8743         return value;
8744     },
8745   */
8746     preFocus : function(){
8747         
8748         if(this.selectOnFocus){
8749             this.inputEl().dom.select();
8750         }
8751     },
8752     filterKeys : function(e){
8753         var k = e.getKey();
8754         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8755             return;
8756         }
8757         var c = e.getCharCode(), cc = String.fromCharCode(c);
8758         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8759             return;
8760         }
8761         if(!this.maskRe.test(cc)){
8762             e.stopEvent();
8763         }
8764     },
8765      /**
8766      * Clear any invalid styles/messages for this field
8767      */
8768     clearInvalid : function(){
8769         
8770         if(!this.el || this.preventMark){ // not rendered
8771             return;
8772         }
8773         
8774         if(this.indicator){
8775             this.indicator.hide();
8776         }
8777         
8778         this.el.removeClass(this.invalidClass);
8779         
8780         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8781             
8782             var feedback = this.el.select('.form-control-feedback', true).first();
8783             
8784             if(feedback){
8785                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8786             }
8787             
8788         }
8789         
8790         this.fireEvent('valid', this);
8791     },
8792     
8793      /**
8794      * Mark this field as valid
8795      */
8796     markValid : function()
8797     {
8798         if(!this.el  || this.preventMark){ // not rendered
8799             return;
8800         }
8801         
8802         this.el.removeClass([this.invalidClass, this.validClass]);
8803         
8804         var feedback = this.el.select('.form-control-feedback', true).first();
8805             
8806         if(feedback){
8807             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8808         }
8809
8810         if(this.disabled || this.allowBlank){
8811             return;
8812         }
8813         
8814         if(this.indicator){
8815             this.indicator.hide();
8816         }
8817         
8818         this.el.addClass(this.validClass);
8819         
8820         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8821             
8822             var feedback = this.el.select('.form-control-feedback', true).first();
8823             
8824             if(feedback){
8825                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8826                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8827             }
8828             
8829         }
8830         
8831         this.fireEvent('valid', this);
8832     },
8833     
8834      /**
8835      * Mark this field as invalid
8836      * @param {String} msg The validation message
8837      */
8838     markInvalid : function(msg)
8839     {
8840         if(!this.el  || this.preventMark){ // not rendered
8841             return;
8842         }
8843         
8844         this.el.removeClass([this.invalidClass, this.validClass]);
8845         
8846         var feedback = this.el.select('.form-control-feedback', true).first();
8847             
8848         if(feedback){
8849             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8850         }
8851
8852         if(this.disabled || this.allowBlank){
8853             return;
8854         }
8855         
8856         if(this.indicator){
8857             this.indicator.show();
8858         }
8859         
8860         this.el.addClass(this.invalidClass);
8861         
8862         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8863             
8864             var feedback = this.el.select('.form-control-feedback', true).first();
8865             
8866             if(feedback){
8867                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8868                 
8869                 if(this.getValue().length || this.forceFeedback){
8870                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8871                 }
8872                 
8873             }
8874             
8875         }
8876         
8877         this.fireEvent('invalid', this, msg);
8878     },
8879     // private
8880     SafariOnKeyDown : function(event)
8881     {
8882         // this is a workaround for a password hang bug on chrome/ webkit.
8883         
8884         var isSelectAll = false;
8885         
8886         if(this.inputEl().dom.selectionEnd > 0){
8887             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8888         }
8889         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8890             event.preventDefault();
8891             this.setValue('');
8892             return;
8893         }
8894         
8895         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8896             
8897             event.preventDefault();
8898             // this is very hacky as keydown always get's upper case.
8899             //
8900             var cc = String.fromCharCode(event.getCharCode());
8901             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8902             
8903         }
8904     },
8905     adjustWidth : function(tag, w){
8906         tag = tag.toLowerCase();
8907         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8908             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8909                 if(tag == 'input'){
8910                     return w + 2;
8911                 }
8912                 if(tag == 'textarea'){
8913                     return w-2;
8914                 }
8915             }else if(Roo.isOpera){
8916                 if(tag == 'input'){
8917                     return w + 2;
8918                 }
8919                 if(tag == 'textarea'){
8920                     return w-2;
8921                 }
8922             }
8923         }
8924         return w;
8925     }
8926     
8927 });
8928
8929  
8930 /*
8931  * - LGPL
8932  *
8933  * Input
8934  * 
8935  */
8936
8937 /**
8938  * @class Roo.bootstrap.TextArea
8939  * @extends Roo.bootstrap.Input
8940  * Bootstrap TextArea class
8941  * @cfg {Number} cols Specifies the visible width of a text area
8942  * @cfg {Number} rows Specifies the visible number of lines in a text area
8943  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8944  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8945  * @cfg {string} html text
8946  * 
8947  * @constructor
8948  * Create a new TextArea
8949  * @param {Object} config The config object
8950  */
8951
8952 Roo.bootstrap.TextArea = function(config){
8953     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8954    
8955 };
8956
8957 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8958      
8959     cols : false,
8960     rows : 5,
8961     readOnly : false,
8962     warp : 'soft',
8963     resize : false,
8964     value: false,
8965     html: false,
8966     
8967     getAutoCreate : function(){
8968         
8969         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8970         
8971         var id = Roo.id();
8972         
8973         var cfg = {};
8974         
8975         var input =  {
8976             tag: 'textarea',
8977             id : id,
8978             warp : this.warp,
8979             rows : this.rows,
8980             value : this.value || '',
8981             html: this.html || '',
8982             cls : 'form-control',
8983             placeholder : this.placeholder || '' 
8984             
8985         };
8986         
8987         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8988             input.maxLength = this.maxLength;
8989         }
8990         
8991         if(this.resize){
8992             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8993         }
8994         
8995         if(this.cols){
8996             input.cols = this.cols;
8997         }
8998         
8999         if (this.readOnly) {
9000             input.readonly = true;
9001         }
9002         
9003         if (this.name) {
9004             input.name = this.name;
9005         }
9006         
9007         if (this.size) {
9008             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9009         }
9010         
9011         var settings=this;
9012         ['xs','sm','md','lg'].map(function(size){
9013             if (settings[size]) {
9014                 cfg.cls += ' col-' + size + '-' + settings[size];
9015             }
9016         });
9017         
9018         var inputblock = input;
9019         
9020         if(this.hasFeedback && !this.allowBlank){
9021             
9022             var feedback = {
9023                 tag: 'span',
9024                 cls: 'glyphicon form-control-feedback'
9025             };
9026
9027             inputblock = {
9028                 cls : 'has-feedback',
9029                 cn :  [
9030                     input,
9031                     feedback
9032                 ] 
9033             };  
9034         }
9035         
9036         
9037         if (this.before || this.after) {
9038             
9039             inputblock = {
9040                 cls : 'input-group',
9041                 cn :  [] 
9042             };
9043             if (this.before) {
9044                 inputblock.cn.push({
9045                     tag :'span',
9046                     cls : 'input-group-addon',
9047                     html : this.before
9048                 });
9049             }
9050             
9051             inputblock.cn.push(input);
9052             
9053             if(this.hasFeedback && !this.allowBlank){
9054                 inputblock.cls += ' has-feedback';
9055                 inputblock.cn.push(feedback);
9056             }
9057             
9058             if (this.after) {
9059                 inputblock.cn.push({
9060                     tag :'span',
9061                     cls : 'input-group-addon',
9062                     html : this.after
9063                 });
9064             }
9065             
9066         }
9067         
9068         if (align ==='left' && this.fieldLabel.length) {
9069 //                Roo.log("left and has label");
9070                 cfg.cn = [
9071                     
9072                     {
9073                         tag: 'label',
9074                         'for' :  id,
9075                         cls : 'control-label col-sm-' + this.labelWidth,
9076                         html : this.fieldLabel
9077                         
9078                     },
9079                     {
9080                         cls : "col-sm-" + (12 - this.labelWidth), 
9081                         cn: [
9082                             inputblock
9083                         ]
9084                     }
9085                     
9086                 ];
9087         } else if ( this.fieldLabel.length) {
9088 //                Roo.log(" label");
9089                  cfg.cn = [
9090                    
9091                     {
9092                         tag: 'label',
9093                         //cls : 'input-group-addon',
9094                         html : this.fieldLabel
9095                         
9096                     },
9097                     
9098                     inputblock
9099                     
9100                 ];
9101
9102         } else {
9103             
9104 //                   Roo.log(" no label && no align");
9105                 cfg.cn = [
9106                     
9107                         inputblock
9108                     
9109                 ];
9110                 
9111                 
9112         }
9113         
9114         if (this.disabled) {
9115             input.disabled=true;
9116         }
9117         
9118         return cfg;
9119         
9120     },
9121     /**
9122      * return the real textarea element.
9123      */
9124     inputEl: function ()
9125     {
9126         return this.el.select('textarea.form-control',true).first();
9127     },
9128     
9129     /**
9130      * Clear any invalid styles/messages for this field
9131      */
9132     clearInvalid : function()
9133     {
9134         
9135         if(!this.el || this.preventMark){ // not rendered
9136             return;
9137         }
9138         
9139         var label = this.el.select('label', true).first();
9140         var icon = this.el.select('i.fa-star', true).first();
9141         
9142         if(label && icon){
9143             icon.remove();
9144         }
9145         
9146         this.el.removeClass(this.invalidClass);
9147         
9148         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9149             
9150             var feedback = this.el.select('.form-control-feedback', true).first();
9151             
9152             if(feedback){
9153                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9154             }
9155             
9156         }
9157         
9158         this.fireEvent('valid', this);
9159     },
9160     
9161      /**
9162      * Mark this field as valid
9163      */
9164     markValid : function()
9165     {
9166         if(!this.el  || this.preventMark){ // not rendered
9167             return;
9168         }
9169         
9170         this.el.removeClass([this.invalidClass, this.validClass]);
9171         
9172         var feedback = this.el.select('.form-control-feedback', true).first();
9173             
9174         if(feedback){
9175             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9176         }
9177
9178         if(this.disabled || this.allowBlank){
9179             return;
9180         }
9181         
9182         var label = this.el.select('label', true).first();
9183         var icon = this.el.select('i.fa-star', true).first();
9184         
9185         if(label && icon){
9186             icon.remove();
9187         }
9188         
9189         this.el.addClass(this.validClass);
9190         
9191         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9192             
9193             var feedback = this.el.select('.form-control-feedback', true).first();
9194             
9195             if(feedback){
9196                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9197                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9198             }
9199             
9200         }
9201         
9202         this.fireEvent('valid', this);
9203     },
9204     
9205      /**
9206      * Mark this field as invalid
9207      * @param {String} msg The validation message
9208      */
9209     markInvalid : function(msg)
9210     {
9211         if(!this.el  || this.preventMark){ // not rendered
9212             return;
9213         }
9214         
9215         this.el.removeClass([this.invalidClass, this.validClass]);
9216         
9217         var feedback = this.el.select('.form-control-feedback', true).first();
9218             
9219         if(feedback){
9220             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9221         }
9222
9223         if(this.disabled || this.allowBlank){
9224             return;
9225         }
9226         
9227         var label = this.el.select('label', true).first();
9228         var icon = this.el.select('i.fa-star', true).first();
9229         
9230         if(!this.getValue().length && label && !icon){
9231             this.el.createChild({
9232                 tag : 'i',
9233                 cls : 'text-danger fa fa-lg fa-star',
9234                 tooltip : 'This field is required',
9235                 style : 'margin-right:5px;'
9236             }, label, true);
9237         }
9238
9239         this.el.addClass(this.invalidClass);
9240         
9241         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9242             
9243             var feedback = this.el.select('.form-control-feedback', true).first();
9244             
9245             if(feedback){
9246                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9247                 
9248                 if(this.getValue().length || this.forceFeedback){
9249                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9250                 }
9251                 
9252             }
9253             
9254         }
9255         
9256         this.fireEvent('invalid', this, msg);
9257     }
9258 });
9259
9260  
9261 /*
9262  * - LGPL
9263  *
9264  * trigger field - base class for combo..
9265  * 
9266  */
9267  
9268 /**
9269  * @class Roo.bootstrap.TriggerField
9270  * @extends Roo.bootstrap.Input
9271  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9272  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9273  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9274  * for which you can provide a custom implementation.  For example:
9275  * <pre><code>
9276 var trigger = new Roo.bootstrap.TriggerField();
9277 trigger.onTriggerClick = myTriggerFn;
9278 trigger.applyTo('my-field');
9279 </code></pre>
9280  *
9281  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9282  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9283  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9284  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9285  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9286
9287  * @constructor
9288  * Create a new TriggerField.
9289  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9290  * to the base TextField)
9291  */
9292 Roo.bootstrap.TriggerField = function(config){
9293     this.mimicing = false;
9294     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9295 };
9296
9297 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9298     /**
9299      * @cfg {String} triggerClass A CSS class to apply to the trigger
9300      */
9301      /**
9302      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9303      */
9304     hideTrigger:false,
9305
9306     /**
9307      * @cfg {Boolean} removable (true|false) special filter default false
9308      */
9309     removable : false,
9310     
9311     /** @cfg {Boolean} grow @hide */
9312     /** @cfg {Number} growMin @hide */
9313     /** @cfg {Number} growMax @hide */
9314
9315     /**
9316      * @hide 
9317      * @method
9318      */
9319     autoSize: Roo.emptyFn,
9320     // private
9321     monitorTab : true,
9322     // private
9323     deferHeight : true,
9324
9325     
9326     actionMode : 'wrap',
9327     
9328     caret : false,
9329     
9330     
9331     getAutoCreate : function(){
9332        
9333         var align = this.labelAlign || this.parentLabelAlign();
9334         
9335         var id = Roo.id();
9336         
9337         var cfg = {
9338             cls: 'form-group' //input-group
9339         };
9340         
9341         
9342         var input =  {
9343             tag: 'input',
9344             id : id,
9345             type : this.inputType,
9346             cls : 'form-control',
9347             autocomplete: 'new-password',
9348             placeholder : this.placeholder || '' 
9349             
9350         };
9351         if (this.name) {
9352             input.name = this.name;
9353         }
9354         if (this.size) {
9355             input.cls += ' input-' + this.size;
9356         }
9357         
9358         if (this.disabled) {
9359             input.disabled=true;
9360         }
9361         
9362         var inputblock = input;
9363         
9364         if(this.hasFeedback && !this.allowBlank){
9365             
9366             var feedback = {
9367                 tag: 'span',
9368                 cls: 'glyphicon form-control-feedback'
9369             };
9370             
9371             if(this.removable && !this.editable && !this.tickable){
9372                 inputblock = {
9373                     cls : 'has-feedback',
9374                     cn :  [
9375                         inputblock,
9376                         {
9377                             tag: 'button',
9378                             html : 'x',
9379                             cls : 'roo-combo-removable-btn close'
9380                         },
9381                         feedback
9382                     ] 
9383                 };
9384             } else {
9385                 inputblock = {
9386                     cls : 'has-feedback',
9387                     cn :  [
9388                         inputblock,
9389                         feedback
9390                     ] 
9391                 };
9392             }
9393
9394         } else {
9395             if(this.removable && !this.editable && !this.tickable){
9396                 inputblock = {
9397                     cls : 'roo-removable',
9398                     cn :  [
9399                         inputblock,
9400                         {
9401                             tag: 'button',
9402                             html : 'x',
9403                             cls : 'roo-combo-removable-btn close'
9404                         }
9405                     ] 
9406                 };
9407             }
9408         }
9409         
9410         if (this.before || this.after) {
9411             
9412             inputblock = {
9413                 cls : 'input-group',
9414                 cn :  [] 
9415             };
9416             if (this.before) {
9417                 inputblock.cn.push({
9418                     tag :'span',
9419                     cls : 'input-group-addon',
9420                     html : this.before
9421                 });
9422             }
9423             
9424             inputblock.cn.push(input);
9425             
9426             if(this.hasFeedback && !this.allowBlank){
9427                 inputblock.cls += ' has-feedback';
9428                 inputblock.cn.push(feedback);
9429             }
9430             
9431             if (this.after) {
9432                 inputblock.cn.push({
9433                     tag :'span',
9434                     cls : 'input-group-addon',
9435                     html : this.after
9436                 });
9437             }
9438             
9439         };
9440         
9441         var box = {
9442             tag: 'div',
9443             cn: [
9444                 {
9445                     tag: 'input',
9446                     type : 'hidden',
9447                     cls: 'form-hidden-field'
9448                 },
9449                 inputblock
9450             ]
9451             
9452         };
9453         
9454         if(this.multiple){
9455             box = {
9456                 tag: 'div',
9457                 cn: [
9458                     {
9459                         tag: 'input',
9460                         type : 'hidden',
9461                         cls: 'form-hidden-field'
9462                     },
9463                     {
9464                         tag: 'ul',
9465                         cls: 'roo-select2-choices',
9466                         cn:[
9467                             {
9468                                 tag: 'li',
9469                                 cls: 'roo-select2-search-field',
9470                                 cn: [
9471
9472                                     inputblock
9473                                 ]
9474                             }
9475                         ]
9476                     }
9477                 ]
9478             }
9479         };
9480         
9481         var combobox = {
9482             cls: 'roo-select2-container input-group',
9483             cn: [
9484                 box
9485 //                {
9486 //                    tag: 'ul',
9487 //                    cls: 'typeahead typeahead-long dropdown-menu',
9488 //                    style: 'display:none'
9489 //                }
9490             ]
9491         };
9492         
9493         if(!this.multiple && this.showToggleBtn){
9494             
9495             var caret = {
9496                         tag: 'span',
9497                         cls: 'caret'
9498              };
9499             if (this.caret != false) {
9500                 caret = {
9501                      tag: 'i',
9502                      cls: 'fa fa-' + this.caret
9503                 };
9504                 
9505             }
9506             
9507             combobox.cn.push({
9508                 tag :'span',
9509                 cls : 'input-group-addon btn dropdown-toggle',
9510                 cn : [
9511                     caret,
9512                     {
9513                         tag: 'span',
9514                         cls: 'combobox-clear',
9515                         cn  : [
9516                             {
9517                                 tag : 'i',
9518                                 cls: 'icon-remove'
9519                             }
9520                         ]
9521                     }
9522                 ]
9523
9524             })
9525         }
9526         
9527         if(this.multiple){
9528             combobox.cls += ' roo-select2-container-multi';
9529         }
9530         
9531         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9532             
9533 //                Roo.log("left and has label");
9534             cfg.cn = [
9535                 {
9536                     tag : 'i',
9537                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9538                     tooltip : 'This field is required'
9539                 },
9540                 {
9541                     tag: 'label',
9542                     'for' :  id,
9543                     cls : 'control-label col-sm-' + this.labelWidth,
9544                     html : this.fieldLabel
9545
9546                 },
9547                 {
9548                     cls : "col-sm-" + (12 - this.labelWidth), 
9549                     cn: [
9550                         combobox
9551                     ]
9552                 }
9553
9554             ];
9555             
9556             if(this.indicatorpos == 'right'){
9557                 cfg.cn = [
9558                     {
9559                         tag: 'label',
9560                         'for' :  id,
9561                         cls : 'control-label col-sm-' + this.labelWidth,
9562                         html : this.fieldLabel
9563
9564                     },
9565                     {
9566                         tag : 'i',
9567                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9568                         tooltip : 'This field is required'
9569                     },
9570                     {
9571                         cls : "col-sm-" + (12 - this.labelWidth), 
9572                         cn: [
9573                             combobox
9574                         ]
9575                     }
9576
9577                 ];
9578             }
9579             
9580         } else if ( this.fieldLabel.length) {
9581 //                Roo.log(" label");
9582             cfg.cn = [
9583                 {
9584                    tag : 'i',
9585                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9586                    tooltip : 'This field is required'
9587                },
9588                {
9589                    tag: 'label',
9590                    //cls : 'input-group-addon',
9591                    html : this.fieldLabel
9592
9593                },
9594
9595                combobox
9596
9597             ];
9598             
9599             if(this.indicatorpos == 'right'){
9600                 
9601                 cfg.cn = [
9602                     {
9603                        tag: 'label',
9604                        //cls : 'input-group-addon',
9605                        html : this.fieldLabel
9606
9607                     },
9608                     {
9609                        tag : 'i',
9610                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9611                        tooltip : 'This field is required'
9612                     },
9613                     
9614                     combobox
9615
9616                 ];
9617
9618             }
9619
9620         } else {
9621             
9622 //                Roo.log(" no label && no align");
9623                 cfg = combobox
9624                      
9625                 
9626         }
9627          
9628         var settings=this;
9629         ['xs','sm','md','lg'].map(function(size){
9630             if (settings[size]) {
9631                 cfg.cls += ' col-' + size + '-' + settings[size];
9632             }
9633         });
9634         
9635         return cfg;
9636         
9637     },
9638     
9639     
9640     
9641     // private
9642     onResize : function(w, h){
9643 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9644 //        if(typeof w == 'number'){
9645 //            var x = w - this.trigger.getWidth();
9646 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9647 //            this.trigger.setStyle('left', x+'px');
9648 //        }
9649     },
9650
9651     // private
9652     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9653
9654     // private
9655     getResizeEl : function(){
9656         return this.inputEl();
9657     },
9658
9659     // private
9660     getPositionEl : function(){
9661         return this.inputEl();
9662     },
9663
9664     // private
9665     alignErrorIcon : function(){
9666         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9667     },
9668
9669     // private
9670     initEvents : function(){
9671         
9672         this.createList();
9673         
9674         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9675         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9676         if(!this.multiple && this.showToggleBtn){
9677             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9678             if(this.hideTrigger){
9679                 this.trigger.setDisplayed(false);
9680             }
9681             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9682         }
9683         
9684         if(this.multiple){
9685             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9686         }
9687         
9688         if(this.removable && !this.editable && !this.tickable){
9689             var close = this.closeTriggerEl();
9690             
9691             if(close){
9692                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9693                 close.on('click', this.removeBtnClick, this, close);
9694             }
9695         }
9696         
9697         //this.trigger.addClassOnOver('x-form-trigger-over');
9698         //this.trigger.addClassOnClick('x-form-trigger-click');
9699         
9700         //if(!this.width){
9701         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9702         //}
9703     },
9704     
9705     closeTriggerEl : function()
9706     {
9707         var close = this.el.select('.roo-combo-removable-btn', true).first();
9708         return close ? close : false;
9709     },
9710     
9711     removeBtnClick : function(e, h, el)
9712     {
9713         e.preventDefault();
9714         
9715         if(this.fireEvent("remove", this) !== false){
9716             this.reset();
9717             this.fireEvent("afterremove", this)
9718         }
9719     },
9720     
9721     createList : function()
9722     {
9723         this.list = Roo.get(document.body).createChild({
9724             tag: 'ul',
9725             cls: 'typeahead typeahead-long dropdown-menu',
9726             style: 'display:none'
9727         });
9728         
9729         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9730         
9731     },
9732
9733     // private
9734     initTrigger : function(){
9735        
9736     },
9737
9738     // private
9739     onDestroy : function(){
9740         if(this.trigger){
9741             this.trigger.removeAllListeners();
9742           //  this.trigger.remove();
9743         }
9744         //if(this.wrap){
9745         //    this.wrap.remove();
9746         //}
9747         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9748     },
9749
9750     // private
9751     onFocus : function(){
9752         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9753         /*
9754         if(!this.mimicing){
9755             this.wrap.addClass('x-trigger-wrap-focus');
9756             this.mimicing = true;
9757             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9758             if(this.monitorTab){
9759                 this.el.on("keydown", this.checkTab, this);
9760             }
9761         }
9762         */
9763     },
9764
9765     // private
9766     checkTab : function(e){
9767         if(e.getKey() == e.TAB){
9768             this.triggerBlur();
9769         }
9770     },
9771
9772     // private
9773     onBlur : function(){
9774         // do nothing
9775     },
9776
9777     // private
9778     mimicBlur : function(e, t){
9779         /*
9780         if(!this.wrap.contains(t) && this.validateBlur()){
9781             this.triggerBlur();
9782         }
9783         */
9784     },
9785
9786     // private
9787     triggerBlur : function(){
9788         this.mimicing = false;
9789         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9790         if(this.monitorTab){
9791             this.el.un("keydown", this.checkTab, this);
9792         }
9793         //this.wrap.removeClass('x-trigger-wrap-focus');
9794         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9795     },
9796
9797     // private
9798     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9799     validateBlur : function(e, t){
9800         return true;
9801     },
9802
9803     // private
9804     onDisable : function(){
9805         this.inputEl().dom.disabled = true;
9806         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9807         //if(this.wrap){
9808         //    this.wrap.addClass('x-item-disabled');
9809         //}
9810     },
9811
9812     // private
9813     onEnable : function(){
9814         this.inputEl().dom.disabled = false;
9815         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9816         //if(this.wrap){
9817         //    this.el.removeClass('x-item-disabled');
9818         //}
9819     },
9820
9821     // private
9822     onShow : function(){
9823         var ae = this.getActionEl();
9824         
9825         if(ae){
9826             ae.dom.style.display = '';
9827             ae.dom.style.visibility = 'visible';
9828         }
9829     },
9830
9831     // private
9832     
9833     onHide : function(){
9834         var ae = this.getActionEl();
9835         ae.dom.style.display = 'none';
9836     },
9837
9838     /**
9839      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9840      * by an implementing function.
9841      * @method
9842      * @param {EventObject} e
9843      */
9844     onTriggerClick : Roo.emptyFn
9845 });
9846  /*
9847  * Based on:
9848  * Ext JS Library 1.1.1
9849  * Copyright(c) 2006-2007, Ext JS, LLC.
9850  *
9851  * Originally Released Under LGPL - original licence link has changed is not relivant.
9852  *
9853  * Fork - LGPL
9854  * <script type="text/javascript">
9855  */
9856
9857
9858 /**
9859  * @class Roo.data.SortTypes
9860  * @singleton
9861  * Defines the default sorting (casting?) comparison functions used when sorting data.
9862  */
9863 Roo.data.SortTypes = {
9864     /**
9865      * Default sort that does nothing
9866      * @param {Mixed} s The value being converted
9867      * @return {Mixed} The comparison value
9868      */
9869     none : function(s){
9870         return s;
9871     },
9872     
9873     /**
9874      * The regular expression used to strip tags
9875      * @type {RegExp}
9876      * @property
9877      */
9878     stripTagsRE : /<\/?[^>]+>/gi,
9879     
9880     /**
9881      * Strips all HTML tags to sort on text only
9882      * @param {Mixed} s The value being converted
9883      * @return {String} The comparison value
9884      */
9885     asText : function(s){
9886         return String(s).replace(this.stripTagsRE, "");
9887     },
9888     
9889     /**
9890      * Strips all HTML tags to sort on text only - Case insensitive
9891      * @param {Mixed} s The value being converted
9892      * @return {String} The comparison value
9893      */
9894     asUCText : function(s){
9895         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9896     },
9897     
9898     /**
9899      * Case insensitive string
9900      * @param {Mixed} s The value being converted
9901      * @return {String} The comparison value
9902      */
9903     asUCString : function(s) {
9904         return String(s).toUpperCase();
9905     },
9906     
9907     /**
9908      * Date sorting
9909      * @param {Mixed} s The value being converted
9910      * @return {Number} The comparison value
9911      */
9912     asDate : function(s) {
9913         if(!s){
9914             return 0;
9915         }
9916         if(s instanceof Date){
9917             return s.getTime();
9918         }
9919         return Date.parse(String(s));
9920     },
9921     
9922     /**
9923      * Float sorting
9924      * @param {Mixed} s The value being converted
9925      * @return {Float} The comparison value
9926      */
9927     asFloat : function(s) {
9928         var val = parseFloat(String(s).replace(/,/g, ""));
9929         if(isNaN(val)) {
9930             val = 0;
9931         }
9932         return val;
9933     },
9934     
9935     /**
9936      * Integer sorting
9937      * @param {Mixed} s The value being converted
9938      * @return {Number} The comparison value
9939      */
9940     asInt : function(s) {
9941         var val = parseInt(String(s).replace(/,/g, ""));
9942         if(isNaN(val)) {
9943             val = 0;
9944         }
9945         return val;
9946     }
9947 };/*
9948  * Based on:
9949  * Ext JS Library 1.1.1
9950  * Copyright(c) 2006-2007, Ext JS, LLC.
9951  *
9952  * Originally Released Under LGPL - original licence link has changed is not relivant.
9953  *
9954  * Fork - LGPL
9955  * <script type="text/javascript">
9956  */
9957
9958 /**
9959 * @class Roo.data.Record
9960  * Instances of this class encapsulate both record <em>definition</em> information, and record
9961  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9962  * to access Records cached in an {@link Roo.data.Store} object.<br>
9963  * <p>
9964  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9965  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9966  * objects.<br>
9967  * <p>
9968  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9969  * @constructor
9970  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9971  * {@link #create}. The parameters are the same.
9972  * @param {Array} data An associative Array of data values keyed by the field name.
9973  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9974  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9975  * not specified an integer id is generated.
9976  */
9977 Roo.data.Record = function(data, id){
9978     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9979     this.data = data;
9980 };
9981
9982 /**
9983  * Generate a constructor for a specific record layout.
9984  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9985  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9986  * Each field definition object may contain the following properties: <ul>
9987  * <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,
9988  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9989  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9990  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9991  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9992  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9993  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9994  * this may be omitted.</p></li>
9995  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9996  * <ul><li>auto (Default, implies no conversion)</li>
9997  * <li>string</li>
9998  * <li>int</li>
9999  * <li>float</li>
10000  * <li>boolean</li>
10001  * <li>date</li></ul></p></li>
10002  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10003  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10004  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10005  * by the Reader into an object that will be stored in the Record. It is passed the
10006  * following parameters:<ul>
10007  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10008  * </ul></p></li>
10009  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10010  * </ul>
10011  * <br>usage:<br><pre><code>
10012 var TopicRecord = Roo.data.Record.create(
10013     {name: 'title', mapping: 'topic_title'},
10014     {name: 'author', mapping: 'username'},
10015     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10016     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10017     {name: 'lastPoster', mapping: 'user2'},
10018     {name: 'excerpt', mapping: 'post_text'}
10019 );
10020
10021 var myNewRecord = new TopicRecord({
10022     title: 'Do my job please',
10023     author: 'noobie',
10024     totalPosts: 1,
10025     lastPost: new Date(),
10026     lastPoster: 'Animal',
10027     excerpt: 'No way dude!'
10028 });
10029 myStore.add(myNewRecord);
10030 </code></pre>
10031  * @method create
10032  * @static
10033  */
10034 Roo.data.Record.create = function(o){
10035     var f = function(){
10036         f.superclass.constructor.apply(this, arguments);
10037     };
10038     Roo.extend(f, Roo.data.Record);
10039     var p = f.prototype;
10040     p.fields = new Roo.util.MixedCollection(false, function(field){
10041         return field.name;
10042     });
10043     for(var i = 0, len = o.length; i < len; i++){
10044         p.fields.add(new Roo.data.Field(o[i]));
10045     }
10046     f.getField = function(name){
10047         return p.fields.get(name);  
10048     };
10049     return f;
10050 };
10051
10052 Roo.data.Record.AUTO_ID = 1000;
10053 Roo.data.Record.EDIT = 'edit';
10054 Roo.data.Record.REJECT = 'reject';
10055 Roo.data.Record.COMMIT = 'commit';
10056
10057 Roo.data.Record.prototype = {
10058     /**
10059      * Readonly flag - true if this record has been modified.
10060      * @type Boolean
10061      */
10062     dirty : false,
10063     editing : false,
10064     error: null,
10065     modified: null,
10066
10067     // private
10068     join : function(store){
10069         this.store = store;
10070     },
10071
10072     /**
10073      * Set the named field to the specified value.
10074      * @param {String} name The name of the field to set.
10075      * @param {Object} value The value to set the field to.
10076      */
10077     set : function(name, value){
10078         if(this.data[name] == value){
10079             return;
10080         }
10081         this.dirty = true;
10082         if(!this.modified){
10083             this.modified = {};
10084         }
10085         if(typeof this.modified[name] == 'undefined'){
10086             this.modified[name] = this.data[name];
10087         }
10088         this.data[name] = value;
10089         if(!this.editing && this.store){
10090             this.store.afterEdit(this);
10091         }       
10092     },
10093
10094     /**
10095      * Get the value of the named field.
10096      * @param {String} name The name of the field to get the value of.
10097      * @return {Object} The value of the field.
10098      */
10099     get : function(name){
10100         return this.data[name]; 
10101     },
10102
10103     // private
10104     beginEdit : function(){
10105         this.editing = true;
10106         this.modified = {}; 
10107     },
10108
10109     // private
10110     cancelEdit : function(){
10111         this.editing = false;
10112         delete this.modified;
10113     },
10114
10115     // private
10116     endEdit : function(){
10117         this.editing = false;
10118         if(this.dirty && this.store){
10119             this.store.afterEdit(this);
10120         }
10121     },
10122
10123     /**
10124      * Usually called by the {@link Roo.data.Store} which owns the Record.
10125      * Rejects all changes made to the Record since either creation, or the last commit operation.
10126      * Modified fields are reverted to their original values.
10127      * <p>
10128      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10129      * of reject operations.
10130      */
10131     reject : function(){
10132         var m = this.modified;
10133         for(var n in m){
10134             if(typeof m[n] != "function"){
10135                 this.data[n] = m[n];
10136             }
10137         }
10138         this.dirty = false;
10139         delete this.modified;
10140         this.editing = false;
10141         if(this.store){
10142             this.store.afterReject(this);
10143         }
10144     },
10145
10146     /**
10147      * Usually called by the {@link Roo.data.Store} which owns the Record.
10148      * Commits all changes made to the Record since either creation, or the last commit operation.
10149      * <p>
10150      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10151      * of commit operations.
10152      */
10153     commit : function(){
10154         this.dirty = false;
10155         delete this.modified;
10156         this.editing = false;
10157         if(this.store){
10158             this.store.afterCommit(this);
10159         }
10160     },
10161
10162     // private
10163     hasError : function(){
10164         return this.error != null;
10165     },
10166
10167     // private
10168     clearError : function(){
10169         this.error = null;
10170     },
10171
10172     /**
10173      * Creates a copy of this record.
10174      * @param {String} id (optional) A new record id if you don't want to use this record's id
10175      * @return {Record}
10176      */
10177     copy : function(newId) {
10178         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10179     }
10180 };/*
10181  * Based on:
10182  * Ext JS Library 1.1.1
10183  * Copyright(c) 2006-2007, Ext JS, LLC.
10184  *
10185  * Originally Released Under LGPL - original licence link has changed is not relivant.
10186  *
10187  * Fork - LGPL
10188  * <script type="text/javascript">
10189  */
10190
10191
10192
10193 /**
10194  * @class Roo.data.Store
10195  * @extends Roo.util.Observable
10196  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10197  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10198  * <p>
10199  * 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
10200  * has no knowledge of the format of the data returned by the Proxy.<br>
10201  * <p>
10202  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10203  * instances from the data object. These records are cached and made available through accessor functions.
10204  * @constructor
10205  * Creates a new Store.
10206  * @param {Object} config A config object containing the objects needed for the Store to access data,
10207  * and read the data into Records.
10208  */
10209 Roo.data.Store = function(config){
10210     this.data = new Roo.util.MixedCollection(false);
10211     this.data.getKey = function(o){
10212         return o.id;
10213     };
10214     this.baseParams = {};
10215     // private
10216     this.paramNames = {
10217         "start" : "start",
10218         "limit" : "limit",
10219         "sort" : "sort",
10220         "dir" : "dir",
10221         "multisort" : "_multisort"
10222     };
10223
10224     if(config && config.data){
10225         this.inlineData = config.data;
10226         delete config.data;
10227     }
10228
10229     Roo.apply(this, config);
10230     
10231     if(this.reader){ // reader passed
10232         this.reader = Roo.factory(this.reader, Roo.data);
10233         this.reader.xmodule = this.xmodule || false;
10234         if(!this.recordType){
10235             this.recordType = this.reader.recordType;
10236         }
10237         if(this.reader.onMetaChange){
10238             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10239         }
10240     }
10241
10242     if(this.recordType){
10243         this.fields = this.recordType.prototype.fields;
10244     }
10245     this.modified = [];
10246
10247     this.addEvents({
10248         /**
10249          * @event datachanged
10250          * Fires when the data cache has changed, and a widget which is using this Store
10251          * as a Record cache should refresh its view.
10252          * @param {Store} this
10253          */
10254         datachanged : true,
10255         /**
10256          * @event metachange
10257          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10258          * @param {Store} this
10259          * @param {Object} meta The JSON metadata
10260          */
10261         metachange : true,
10262         /**
10263          * @event add
10264          * Fires when Records have been added to the Store
10265          * @param {Store} this
10266          * @param {Roo.data.Record[]} records The array of Records added
10267          * @param {Number} index The index at which the record(s) were added
10268          */
10269         add : true,
10270         /**
10271          * @event remove
10272          * Fires when a Record has been removed from the Store
10273          * @param {Store} this
10274          * @param {Roo.data.Record} record The Record that was removed
10275          * @param {Number} index The index at which the record was removed
10276          */
10277         remove : true,
10278         /**
10279          * @event update
10280          * Fires when a Record has been updated
10281          * @param {Store} this
10282          * @param {Roo.data.Record} record The Record that was updated
10283          * @param {String} operation The update operation being performed.  Value may be one of:
10284          * <pre><code>
10285  Roo.data.Record.EDIT
10286  Roo.data.Record.REJECT
10287  Roo.data.Record.COMMIT
10288          * </code></pre>
10289          */
10290         update : true,
10291         /**
10292          * @event clear
10293          * Fires when the data cache has been cleared.
10294          * @param {Store} this
10295          */
10296         clear : true,
10297         /**
10298          * @event beforeload
10299          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10300          * the load action will be canceled.
10301          * @param {Store} this
10302          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10303          */
10304         beforeload : true,
10305         /**
10306          * @event beforeloadadd
10307          * Fires after a new set of Records has been loaded.
10308          * @param {Store} this
10309          * @param {Roo.data.Record[]} records The Records that were loaded
10310          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10311          */
10312         beforeloadadd : true,
10313         /**
10314          * @event load
10315          * Fires after a new set of Records has been loaded, before they are added to the store.
10316          * @param {Store} this
10317          * @param {Roo.data.Record[]} records The Records that were loaded
10318          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10319          * @params {Object} return from reader
10320          */
10321         load : true,
10322         /**
10323          * @event loadexception
10324          * Fires if an exception occurs in the Proxy during loading.
10325          * Called with the signature of the Proxy's "loadexception" event.
10326          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10327          * 
10328          * @param {Proxy} 
10329          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10330          * @param {Object} load options 
10331          * @param {Object} jsonData from your request (normally this contains the Exception)
10332          */
10333         loadexception : true
10334     });
10335     
10336     if(this.proxy){
10337         this.proxy = Roo.factory(this.proxy, Roo.data);
10338         this.proxy.xmodule = this.xmodule || false;
10339         this.relayEvents(this.proxy,  ["loadexception"]);
10340     }
10341     this.sortToggle = {};
10342     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10343
10344     Roo.data.Store.superclass.constructor.call(this);
10345
10346     if(this.inlineData){
10347         this.loadData(this.inlineData);
10348         delete this.inlineData;
10349     }
10350 };
10351
10352 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10353      /**
10354     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10355     * without a remote query - used by combo/forms at present.
10356     */
10357     
10358     /**
10359     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10360     */
10361     /**
10362     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10363     */
10364     /**
10365     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10366     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10367     */
10368     /**
10369     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10370     * on any HTTP request
10371     */
10372     /**
10373     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10374     */
10375     /**
10376     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10377     */
10378     multiSort: false,
10379     /**
10380     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10381     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10382     */
10383     remoteSort : false,
10384
10385     /**
10386     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10387      * loaded or when a record is removed. (defaults to false).
10388     */
10389     pruneModifiedRecords : false,
10390
10391     // private
10392     lastOptions : null,
10393
10394     /**
10395      * Add Records to the Store and fires the add event.
10396      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10397      */
10398     add : function(records){
10399         records = [].concat(records);
10400         for(var i = 0, len = records.length; i < len; i++){
10401             records[i].join(this);
10402         }
10403         var index = this.data.length;
10404         this.data.addAll(records);
10405         this.fireEvent("add", this, records, index);
10406     },
10407
10408     /**
10409      * Remove a Record from the Store and fires the remove event.
10410      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10411      */
10412     remove : function(record){
10413         var index = this.data.indexOf(record);
10414         this.data.removeAt(index);
10415         if(this.pruneModifiedRecords){
10416             this.modified.remove(record);
10417         }
10418         this.fireEvent("remove", this, record, index);
10419     },
10420
10421     /**
10422      * Remove all Records from the Store and fires the clear event.
10423      */
10424     removeAll : function(){
10425         this.data.clear();
10426         if(this.pruneModifiedRecords){
10427             this.modified = [];
10428         }
10429         this.fireEvent("clear", this);
10430     },
10431
10432     /**
10433      * Inserts Records to the Store at the given index and fires the add event.
10434      * @param {Number} index The start index at which to insert the passed Records.
10435      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10436      */
10437     insert : function(index, records){
10438         records = [].concat(records);
10439         for(var i = 0, len = records.length; i < len; i++){
10440             this.data.insert(index, records[i]);
10441             records[i].join(this);
10442         }
10443         this.fireEvent("add", this, records, index);
10444     },
10445
10446     /**
10447      * Get the index within the cache of the passed Record.
10448      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10449      * @return {Number} The index of the passed Record. Returns -1 if not found.
10450      */
10451     indexOf : function(record){
10452         return this.data.indexOf(record);
10453     },
10454
10455     /**
10456      * Get the index within the cache of the Record with the passed id.
10457      * @param {String} id The id of the Record to find.
10458      * @return {Number} The index of the Record. Returns -1 if not found.
10459      */
10460     indexOfId : function(id){
10461         return this.data.indexOfKey(id);
10462     },
10463
10464     /**
10465      * Get the Record with the specified id.
10466      * @param {String} id The id of the Record to find.
10467      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10468      */
10469     getById : function(id){
10470         return this.data.key(id);
10471     },
10472
10473     /**
10474      * Get the Record at the specified index.
10475      * @param {Number} index The index of the Record to find.
10476      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10477      */
10478     getAt : function(index){
10479         return this.data.itemAt(index);
10480     },
10481
10482     /**
10483      * Returns a range of Records between specified indices.
10484      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10485      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10486      * @return {Roo.data.Record[]} An array of Records
10487      */
10488     getRange : function(start, end){
10489         return this.data.getRange(start, end);
10490     },
10491
10492     // private
10493     storeOptions : function(o){
10494         o = Roo.apply({}, o);
10495         delete o.callback;
10496         delete o.scope;
10497         this.lastOptions = o;
10498     },
10499
10500     /**
10501      * Loads the Record cache from the configured Proxy using the configured Reader.
10502      * <p>
10503      * If using remote paging, then the first load call must specify the <em>start</em>
10504      * and <em>limit</em> properties in the options.params property to establish the initial
10505      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10506      * <p>
10507      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10508      * and this call will return before the new data has been loaded. Perform any post-processing
10509      * in a callback function, or in a "load" event handler.</strong>
10510      * <p>
10511      * @param {Object} options An object containing properties which control loading options:<ul>
10512      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10513      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10514      * passed the following arguments:<ul>
10515      * <li>r : Roo.data.Record[]</li>
10516      * <li>options: Options object from the load call</li>
10517      * <li>success: Boolean success indicator</li></ul></li>
10518      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10519      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10520      * </ul>
10521      */
10522     load : function(options){
10523         options = options || {};
10524         if(this.fireEvent("beforeload", this, options) !== false){
10525             this.storeOptions(options);
10526             var p = Roo.apply(options.params || {}, this.baseParams);
10527             // if meta was not loaded from remote source.. try requesting it.
10528             if (!this.reader.metaFromRemote) {
10529                 p._requestMeta = 1;
10530             }
10531             if(this.sortInfo && this.remoteSort){
10532                 var pn = this.paramNames;
10533                 p[pn["sort"]] = this.sortInfo.field;
10534                 p[pn["dir"]] = this.sortInfo.direction;
10535             }
10536             if (this.multiSort) {
10537                 var pn = this.paramNames;
10538                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10539             }
10540             
10541             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10542         }
10543     },
10544
10545     /**
10546      * Reloads the Record cache from the configured Proxy using the configured Reader and
10547      * the options from the last load operation performed.
10548      * @param {Object} options (optional) An object containing properties which may override the options
10549      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10550      * the most recently used options are reused).
10551      */
10552     reload : function(options){
10553         this.load(Roo.applyIf(options||{}, this.lastOptions));
10554     },
10555
10556     // private
10557     // Called as a callback by the Reader during a load operation.
10558     loadRecords : function(o, options, success){
10559         if(!o || success === false){
10560             if(success !== false){
10561                 this.fireEvent("load", this, [], options, o);
10562             }
10563             if(options.callback){
10564                 options.callback.call(options.scope || this, [], options, false);
10565             }
10566             return;
10567         }
10568         // if data returned failure - throw an exception.
10569         if (o.success === false) {
10570             // show a message if no listener is registered.
10571             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10572                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10573             }
10574             // loadmask wil be hooked into this..
10575             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10576             return;
10577         }
10578         var r = o.records, t = o.totalRecords || r.length;
10579         
10580         this.fireEvent("beforeloadadd", this, r, options, o);
10581         
10582         if(!options || options.add !== true){
10583             if(this.pruneModifiedRecords){
10584                 this.modified = [];
10585             }
10586             for(var i = 0, len = r.length; i < len; i++){
10587                 r[i].join(this);
10588             }
10589             if(this.snapshot){
10590                 this.data = this.snapshot;
10591                 delete this.snapshot;
10592             }
10593             this.data.clear();
10594             this.data.addAll(r);
10595             this.totalLength = t;
10596             this.applySort();
10597             this.fireEvent("datachanged", this);
10598         }else{
10599             this.totalLength = Math.max(t, this.data.length+r.length);
10600             this.add(r);
10601         }
10602         this.fireEvent("load", this, r, options, o);
10603         if(options.callback){
10604             options.callback.call(options.scope || this, r, options, true);
10605         }
10606     },
10607
10608
10609     /**
10610      * Loads data from a passed data block. A Reader which understands the format of the data
10611      * must have been configured in the constructor.
10612      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10613      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10614      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10615      */
10616     loadData : function(o, append){
10617         var r = this.reader.readRecords(o);
10618         this.loadRecords(r, {add: append}, true);
10619     },
10620
10621     /**
10622      * Gets the number of cached records.
10623      * <p>
10624      * <em>If using paging, this may not be the total size of the dataset. If the data object
10625      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10626      * the data set size</em>
10627      */
10628     getCount : function(){
10629         return this.data.length || 0;
10630     },
10631
10632     /**
10633      * Gets the total number of records in the dataset as returned by the server.
10634      * <p>
10635      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10636      * the dataset size</em>
10637      */
10638     getTotalCount : function(){
10639         return this.totalLength || 0;
10640     },
10641
10642     /**
10643      * Returns the sort state of the Store as an object with two properties:
10644      * <pre><code>
10645  field {String} The name of the field by which the Records are sorted
10646  direction {String} The sort order, "ASC" or "DESC"
10647      * </code></pre>
10648      */
10649     getSortState : function(){
10650         return this.sortInfo;
10651     },
10652
10653     // private
10654     applySort : function(){
10655         if(this.sortInfo && !this.remoteSort){
10656             var s = this.sortInfo, f = s.field;
10657             var st = this.fields.get(f).sortType;
10658             var fn = function(r1, r2){
10659                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10660                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10661             };
10662             this.data.sort(s.direction, fn);
10663             if(this.snapshot && this.snapshot != this.data){
10664                 this.snapshot.sort(s.direction, fn);
10665             }
10666         }
10667     },
10668
10669     /**
10670      * Sets the default sort column and order to be used by the next load operation.
10671      * @param {String} fieldName The name of the field to sort by.
10672      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10673      */
10674     setDefaultSort : function(field, dir){
10675         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10676     },
10677
10678     /**
10679      * Sort the Records.
10680      * If remote sorting is used, the sort is performed on the server, and the cache is
10681      * reloaded. If local sorting is used, the cache is sorted internally.
10682      * @param {String} fieldName The name of the field to sort by.
10683      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10684      */
10685     sort : function(fieldName, dir){
10686         var f = this.fields.get(fieldName);
10687         if(!dir){
10688             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10689             
10690             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10691                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10692             }else{
10693                 dir = f.sortDir;
10694             }
10695         }
10696         this.sortToggle[f.name] = dir;
10697         this.sortInfo = {field: f.name, direction: dir};
10698         if(!this.remoteSort){
10699             this.applySort();
10700             this.fireEvent("datachanged", this);
10701         }else{
10702             this.load(this.lastOptions);
10703         }
10704     },
10705
10706     /**
10707      * Calls the specified function for each of the Records in the cache.
10708      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10709      * Returning <em>false</em> aborts and exits the iteration.
10710      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10711      */
10712     each : function(fn, scope){
10713         this.data.each(fn, scope);
10714     },
10715
10716     /**
10717      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10718      * (e.g., during paging).
10719      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10720      */
10721     getModifiedRecords : function(){
10722         return this.modified;
10723     },
10724
10725     // private
10726     createFilterFn : function(property, value, anyMatch){
10727         if(!value.exec){ // not a regex
10728             value = String(value);
10729             if(value.length == 0){
10730                 return false;
10731             }
10732             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10733         }
10734         return function(r){
10735             return value.test(r.data[property]);
10736         };
10737     },
10738
10739     /**
10740      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10741      * @param {String} property A field on your records
10742      * @param {Number} start The record index to start at (defaults to 0)
10743      * @param {Number} end The last record index to include (defaults to length - 1)
10744      * @return {Number} The sum
10745      */
10746     sum : function(property, start, end){
10747         var rs = this.data.items, v = 0;
10748         start = start || 0;
10749         end = (end || end === 0) ? end : rs.length-1;
10750
10751         for(var i = start; i <= end; i++){
10752             v += (rs[i].data[property] || 0);
10753         }
10754         return v;
10755     },
10756
10757     /**
10758      * Filter the records by a specified property.
10759      * @param {String} field A field on your records
10760      * @param {String/RegExp} value Either a string that the field
10761      * should start with or a RegExp to test against the field
10762      * @param {Boolean} anyMatch True to match any part not just the beginning
10763      */
10764     filter : function(property, value, anyMatch){
10765         var fn = this.createFilterFn(property, value, anyMatch);
10766         return fn ? this.filterBy(fn) : this.clearFilter();
10767     },
10768
10769     /**
10770      * Filter by a function. The specified function will be called with each
10771      * record in this data source. If the function returns true the record is included,
10772      * otherwise it is filtered.
10773      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10774      * @param {Object} scope (optional) The scope of the function (defaults to this)
10775      */
10776     filterBy : function(fn, scope){
10777         this.snapshot = this.snapshot || this.data;
10778         this.data = this.queryBy(fn, scope||this);
10779         this.fireEvent("datachanged", this);
10780     },
10781
10782     /**
10783      * Query the records by a specified property.
10784      * @param {String} field A field on your records
10785      * @param {String/RegExp} value Either a string that the field
10786      * should start with or a RegExp to test against the field
10787      * @param {Boolean} anyMatch True to match any part not just the beginning
10788      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10789      */
10790     query : function(property, value, anyMatch){
10791         var fn = this.createFilterFn(property, value, anyMatch);
10792         return fn ? this.queryBy(fn) : this.data.clone();
10793     },
10794
10795     /**
10796      * Query by a function. The specified function will be called with each
10797      * record in this data source. If the function returns true the record is included
10798      * in the results.
10799      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10800      * @param {Object} scope (optional) The scope of the function (defaults to this)
10801       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10802      **/
10803     queryBy : function(fn, scope){
10804         var data = this.snapshot || this.data;
10805         return data.filterBy(fn, scope||this);
10806     },
10807
10808     /**
10809      * Collects unique values for a particular dataIndex from this store.
10810      * @param {String} dataIndex The property to collect
10811      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10812      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10813      * @return {Array} An array of the unique values
10814      **/
10815     collect : function(dataIndex, allowNull, bypassFilter){
10816         var d = (bypassFilter === true && this.snapshot) ?
10817                 this.snapshot.items : this.data.items;
10818         var v, sv, r = [], l = {};
10819         for(var i = 0, len = d.length; i < len; i++){
10820             v = d[i].data[dataIndex];
10821             sv = String(v);
10822             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10823                 l[sv] = true;
10824                 r[r.length] = v;
10825             }
10826         }
10827         return r;
10828     },
10829
10830     /**
10831      * Revert to a view of the Record cache with no filtering applied.
10832      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10833      */
10834     clearFilter : function(suppressEvent){
10835         if(this.snapshot && this.snapshot != this.data){
10836             this.data = this.snapshot;
10837             delete this.snapshot;
10838             if(suppressEvent !== true){
10839                 this.fireEvent("datachanged", this);
10840             }
10841         }
10842     },
10843
10844     // private
10845     afterEdit : function(record){
10846         if(this.modified.indexOf(record) == -1){
10847             this.modified.push(record);
10848         }
10849         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10850     },
10851     
10852     // private
10853     afterReject : function(record){
10854         this.modified.remove(record);
10855         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10856     },
10857
10858     // private
10859     afterCommit : function(record){
10860         this.modified.remove(record);
10861         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10862     },
10863
10864     /**
10865      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10866      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10867      */
10868     commitChanges : function(){
10869         var m = this.modified.slice(0);
10870         this.modified = [];
10871         for(var i = 0, len = m.length; i < len; i++){
10872             m[i].commit();
10873         }
10874     },
10875
10876     /**
10877      * Cancel outstanding changes on all changed records.
10878      */
10879     rejectChanges : function(){
10880         var m = this.modified.slice(0);
10881         this.modified = [];
10882         for(var i = 0, len = m.length; i < len; i++){
10883             m[i].reject();
10884         }
10885     },
10886
10887     onMetaChange : function(meta, rtype, o){
10888         this.recordType = rtype;
10889         this.fields = rtype.prototype.fields;
10890         delete this.snapshot;
10891         this.sortInfo = meta.sortInfo || this.sortInfo;
10892         this.modified = [];
10893         this.fireEvent('metachange', this, this.reader.meta);
10894     },
10895     
10896     moveIndex : function(data, type)
10897     {
10898         var index = this.indexOf(data);
10899         
10900         var newIndex = index + type;
10901         
10902         this.remove(data);
10903         
10904         this.insert(newIndex, data);
10905         
10906     }
10907 });/*
10908  * Based on:
10909  * Ext JS Library 1.1.1
10910  * Copyright(c) 2006-2007, Ext JS, LLC.
10911  *
10912  * Originally Released Under LGPL - original licence link has changed is not relivant.
10913  *
10914  * Fork - LGPL
10915  * <script type="text/javascript">
10916  */
10917
10918 /**
10919  * @class Roo.data.SimpleStore
10920  * @extends Roo.data.Store
10921  * Small helper class to make creating Stores from Array data easier.
10922  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10923  * @cfg {Array} fields An array of field definition objects, or field name strings.
10924  * @cfg {Array} data The multi-dimensional array of data
10925  * @constructor
10926  * @param {Object} config
10927  */
10928 Roo.data.SimpleStore = function(config){
10929     Roo.data.SimpleStore.superclass.constructor.call(this, {
10930         isLocal : true,
10931         reader: new Roo.data.ArrayReader({
10932                 id: config.id
10933             },
10934             Roo.data.Record.create(config.fields)
10935         ),
10936         proxy : new Roo.data.MemoryProxy(config.data)
10937     });
10938     this.load();
10939 };
10940 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10941  * Based on:
10942  * Ext JS Library 1.1.1
10943  * Copyright(c) 2006-2007, Ext JS, LLC.
10944  *
10945  * Originally Released Under LGPL - original licence link has changed is not relivant.
10946  *
10947  * Fork - LGPL
10948  * <script type="text/javascript">
10949  */
10950
10951 /**
10952 /**
10953  * @extends Roo.data.Store
10954  * @class Roo.data.JsonStore
10955  * Small helper class to make creating Stores for JSON data easier. <br/>
10956 <pre><code>
10957 var store = new Roo.data.JsonStore({
10958     url: 'get-images.php',
10959     root: 'images',
10960     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10961 });
10962 </code></pre>
10963  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10964  * JsonReader and HttpProxy (unless inline data is provided).</b>
10965  * @cfg {Array} fields An array of field definition objects, or field name strings.
10966  * @constructor
10967  * @param {Object} config
10968  */
10969 Roo.data.JsonStore = function(c){
10970     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10971         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10972         reader: new Roo.data.JsonReader(c, c.fields)
10973     }));
10974 };
10975 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10976  * Based on:
10977  * Ext JS Library 1.1.1
10978  * Copyright(c) 2006-2007, Ext JS, LLC.
10979  *
10980  * Originally Released Under LGPL - original licence link has changed is not relivant.
10981  *
10982  * Fork - LGPL
10983  * <script type="text/javascript">
10984  */
10985
10986  
10987 Roo.data.Field = function(config){
10988     if(typeof config == "string"){
10989         config = {name: config};
10990     }
10991     Roo.apply(this, config);
10992     
10993     if(!this.type){
10994         this.type = "auto";
10995     }
10996     
10997     var st = Roo.data.SortTypes;
10998     // named sortTypes are supported, here we look them up
10999     if(typeof this.sortType == "string"){
11000         this.sortType = st[this.sortType];
11001     }
11002     
11003     // set default sortType for strings and dates
11004     if(!this.sortType){
11005         switch(this.type){
11006             case "string":
11007                 this.sortType = st.asUCString;
11008                 break;
11009             case "date":
11010                 this.sortType = st.asDate;
11011                 break;
11012             default:
11013                 this.sortType = st.none;
11014         }
11015     }
11016
11017     // define once
11018     var stripRe = /[\$,%]/g;
11019
11020     // prebuilt conversion function for this field, instead of
11021     // switching every time we're reading a value
11022     if(!this.convert){
11023         var cv, dateFormat = this.dateFormat;
11024         switch(this.type){
11025             case "":
11026             case "auto":
11027             case undefined:
11028                 cv = function(v){ return v; };
11029                 break;
11030             case "string":
11031                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11032                 break;
11033             case "int":
11034                 cv = function(v){
11035                     return v !== undefined && v !== null && v !== '' ?
11036                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11037                     };
11038                 break;
11039             case "float":
11040                 cv = function(v){
11041                     return v !== undefined && v !== null && v !== '' ?
11042                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11043                     };
11044                 break;
11045             case "bool":
11046             case "boolean":
11047                 cv = function(v){ return v === true || v === "true" || v == 1; };
11048                 break;
11049             case "date":
11050                 cv = function(v){
11051                     if(!v){
11052                         return '';
11053                     }
11054                     if(v instanceof Date){
11055                         return v;
11056                     }
11057                     if(dateFormat){
11058                         if(dateFormat == "timestamp"){
11059                             return new Date(v*1000);
11060                         }
11061                         return Date.parseDate(v, dateFormat);
11062                     }
11063                     var parsed = Date.parse(v);
11064                     return parsed ? new Date(parsed) : null;
11065                 };
11066              break;
11067             
11068         }
11069         this.convert = cv;
11070     }
11071 };
11072
11073 Roo.data.Field.prototype = {
11074     dateFormat: null,
11075     defaultValue: "",
11076     mapping: null,
11077     sortType : null,
11078     sortDir : "ASC"
11079 };/*
11080  * Based on:
11081  * Ext JS Library 1.1.1
11082  * Copyright(c) 2006-2007, Ext JS, LLC.
11083  *
11084  * Originally Released Under LGPL - original licence link has changed is not relivant.
11085  *
11086  * Fork - LGPL
11087  * <script type="text/javascript">
11088  */
11089  
11090 // Base class for reading structured data from a data source.  This class is intended to be
11091 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11092
11093 /**
11094  * @class Roo.data.DataReader
11095  * Base class for reading structured data from a data source.  This class is intended to be
11096  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11097  */
11098
11099 Roo.data.DataReader = function(meta, recordType){
11100     
11101     this.meta = meta;
11102     
11103     this.recordType = recordType instanceof Array ? 
11104         Roo.data.Record.create(recordType) : recordType;
11105 };
11106
11107 Roo.data.DataReader.prototype = {
11108      /**
11109      * Create an empty record
11110      * @param {Object} data (optional) - overlay some values
11111      * @return {Roo.data.Record} record created.
11112      */
11113     newRow :  function(d) {
11114         var da =  {};
11115         this.recordType.prototype.fields.each(function(c) {
11116             switch( c.type) {
11117                 case 'int' : da[c.name] = 0; break;
11118                 case 'date' : da[c.name] = new Date(); break;
11119                 case 'float' : da[c.name] = 0.0; break;
11120                 case 'boolean' : da[c.name] = false; break;
11121                 default : da[c.name] = ""; break;
11122             }
11123             
11124         });
11125         return new this.recordType(Roo.apply(da, d));
11126     }
11127     
11128 };/*
11129  * Based on:
11130  * Ext JS Library 1.1.1
11131  * Copyright(c) 2006-2007, Ext JS, LLC.
11132  *
11133  * Originally Released Under LGPL - original licence link has changed is not relivant.
11134  *
11135  * Fork - LGPL
11136  * <script type="text/javascript">
11137  */
11138
11139 /**
11140  * @class Roo.data.DataProxy
11141  * @extends Roo.data.Observable
11142  * This class is an abstract base class for implementations which provide retrieval of
11143  * unformatted data objects.<br>
11144  * <p>
11145  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11146  * (of the appropriate type which knows how to parse the data object) to provide a block of
11147  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11148  * <p>
11149  * Custom implementations must implement the load method as described in
11150  * {@link Roo.data.HttpProxy#load}.
11151  */
11152 Roo.data.DataProxy = function(){
11153     this.addEvents({
11154         /**
11155          * @event beforeload
11156          * Fires before a network request is made to retrieve a data object.
11157          * @param {Object} This DataProxy object.
11158          * @param {Object} params The params parameter to the load function.
11159          */
11160         beforeload : true,
11161         /**
11162          * @event load
11163          * Fires before the load method's callback is called.
11164          * @param {Object} This DataProxy object.
11165          * @param {Object} o The data object.
11166          * @param {Object} arg The callback argument object passed to the load function.
11167          */
11168         load : true,
11169         /**
11170          * @event loadexception
11171          * Fires if an Exception occurs during data retrieval.
11172          * @param {Object} This DataProxy object.
11173          * @param {Object} o The data object.
11174          * @param {Object} arg The callback argument object passed to the load function.
11175          * @param {Object} e The Exception.
11176          */
11177         loadexception : true
11178     });
11179     Roo.data.DataProxy.superclass.constructor.call(this);
11180 };
11181
11182 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11183
11184     /**
11185      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11186      */
11187 /*
11188  * Based on:
11189  * Ext JS Library 1.1.1
11190  * Copyright(c) 2006-2007, Ext JS, LLC.
11191  *
11192  * Originally Released Under LGPL - original licence link has changed is not relivant.
11193  *
11194  * Fork - LGPL
11195  * <script type="text/javascript">
11196  */
11197 /**
11198  * @class Roo.data.MemoryProxy
11199  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11200  * to the Reader when its load method is called.
11201  * @constructor
11202  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11203  */
11204 Roo.data.MemoryProxy = function(data){
11205     if (data.data) {
11206         data = data.data;
11207     }
11208     Roo.data.MemoryProxy.superclass.constructor.call(this);
11209     this.data = data;
11210 };
11211
11212 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11213     
11214     /**
11215      * Load data from the requested source (in this case an in-memory
11216      * data object passed to the constructor), read the data object into
11217      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11218      * process that block using the passed callback.
11219      * @param {Object} params This parameter is not used by the MemoryProxy class.
11220      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11221      * object into a block of Roo.data.Records.
11222      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11223      * The function must be passed <ul>
11224      * <li>The Record block object</li>
11225      * <li>The "arg" argument from the load function</li>
11226      * <li>A boolean success indicator</li>
11227      * </ul>
11228      * @param {Object} scope The scope in which to call the callback
11229      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11230      */
11231     load : function(params, reader, callback, scope, arg){
11232         params = params || {};
11233         var result;
11234         try {
11235             result = reader.readRecords(this.data);
11236         }catch(e){
11237             this.fireEvent("loadexception", this, arg, null, e);
11238             callback.call(scope, null, arg, false);
11239             return;
11240         }
11241         callback.call(scope, result, arg, true);
11242     },
11243     
11244     // private
11245     update : function(params, records){
11246         
11247     }
11248 });/*
11249  * Based on:
11250  * Ext JS Library 1.1.1
11251  * Copyright(c) 2006-2007, Ext JS, LLC.
11252  *
11253  * Originally Released Under LGPL - original licence link has changed is not relivant.
11254  *
11255  * Fork - LGPL
11256  * <script type="text/javascript">
11257  */
11258 /**
11259  * @class Roo.data.HttpProxy
11260  * @extends Roo.data.DataProxy
11261  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11262  * configured to reference a certain URL.<br><br>
11263  * <p>
11264  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11265  * from which the running page was served.<br><br>
11266  * <p>
11267  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11268  * <p>
11269  * Be aware that to enable the browser to parse an XML document, the server must set
11270  * the Content-Type header in the HTTP response to "text/xml".
11271  * @constructor
11272  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11273  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11274  * will be used to make the request.
11275  */
11276 Roo.data.HttpProxy = function(conn){
11277     Roo.data.HttpProxy.superclass.constructor.call(this);
11278     // is conn a conn config or a real conn?
11279     this.conn = conn;
11280     this.useAjax = !conn || !conn.events;
11281   
11282 };
11283
11284 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11285     // thse are take from connection...
11286     
11287     /**
11288      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11289      */
11290     /**
11291      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11292      * extra parameters to each request made by this object. (defaults to undefined)
11293      */
11294     /**
11295      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11296      *  to each request made by this object. (defaults to undefined)
11297      */
11298     /**
11299      * @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)
11300      */
11301     /**
11302      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11303      */
11304      /**
11305      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11306      * @type Boolean
11307      */
11308   
11309
11310     /**
11311      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11312      * @type Boolean
11313      */
11314     /**
11315      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11316      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11317      * a finer-grained basis than the DataProxy events.
11318      */
11319     getConnection : function(){
11320         return this.useAjax ? Roo.Ajax : this.conn;
11321     },
11322
11323     /**
11324      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11325      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11326      * process that block using the passed callback.
11327      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11328      * for the request to the remote server.
11329      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11330      * object into a block of Roo.data.Records.
11331      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11332      * The function must be passed <ul>
11333      * <li>The Record block object</li>
11334      * <li>The "arg" argument from the load function</li>
11335      * <li>A boolean success indicator</li>
11336      * </ul>
11337      * @param {Object} scope The scope in which to call the callback
11338      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11339      */
11340     load : function(params, reader, callback, scope, arg){
11341         if(this.fireEvent("beforeload", this, params) !== false){
11342             var  o = {
11343                 params : params || {},
11344                 request: {
11345                     callback : callback,
11346                     scope : scope,
11347                     arg : arg
11348                 },
11349                 reader: reader,
11350                 callback : this.loadResponse,
11351                 scope: this
11352             };
11353             if(this.useAjax){
11354                 Roo.applyIf(o, this.conn);
11355                 if(this.activeRequest){
11356                     Roo.Ajax.abort(this.activeRequest);
11357                 }
11358                 this.activeRequest = Roo.Ajax.request(o);
11359             }else{
11360                 this.conn.request(o);
11361             }
11362         }else{
11363             callback.call(scope||this, null, arg, false);
11364         }
11365     },
11366
11367     // private
11368     loadResponse : function(o, success, response){
11369         delete this.activeRequest;
11370         if(!success){
11371             this.fireEvent("loadexception", this, o, response);
11372             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11373             return;
11374         }
11375         var result;
11376         try {
11377             result = o.reader.read(response);
11378         }catch(e){
11379             this.fireEvent("loadexception", this, o, response, e);
11380             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11381             return;
11382         }
11383         
11384         this.fireEvent("load", this, o, o.request.arg);
11385         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11386     },
11387
11388     // private
11389     update : function(dataSet){
11390
11391     },
11392
11393     // private
11394     updateResponse : function(dataSet){
11395
11396     }
11397 });/*
11398  * Based on:
11399  * Ext JS Library 1.1.1
11400  * Copyright(c) 2006-2007, Ext JS, LLC.
11401  *
11402  * Originally Released Under LGPL - original licence link has changed is not relivant.
11403  *
11404  * Fork - LGPL
11405  * <script type="text/javascript">
11406  */
11407
11408 /**
11409  * @class Roo.data.ScriptTagProxy
11410  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11411  * other than the originating domain of the running page.<br><br>
11412  * <p>
11413  * <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
11414  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11415  * <p>
11416  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11417  * source code that is used as the source inside a &lt;script> tag.<br><br>
11418  * <p>
11419  * In order for the browser to process the returned data, the server must wrap the data object
11420  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11421  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11422  * depending on whether the callback name was passed:
11423  * <p>
11424  * <pre><code>
11425 boolean scriptTag = false;
11426 String cb = request.getParameter("callback");
11427 if (cb != null) {
11428     scriptTag = true;
11429     response.setContentType("text/javascript");
11430 } else {
11431     response.setContentType("application/x-json");
11432 }
11433 Writer out = response.getWriter();
11434 if (scriptTag) {
11435     out.write(cb + "(");
11436 }
11437 out.print(dataBlock.toJsonString());
11438 if (scriptTag) {
11439     out.write(");");
11440 }
11441 </pre></code>
11442  *
11443  * @constructor
11444  * @param {Object} config A configuration object.
11445  */
11446 Roo.data.ScriptTagProxy = function(config){
11447     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11448     Roo.apply(this, config);
11449     this.head = document.getElementsByTagName("head")[0];
11450 };
11451
11452 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11453
11454 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11455     /**
11456      * @cfg {String} url The URL from which to request the data object.
11457      */
11458     /**
11459      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11460      */
11461     timeout : 30000,
11462     /**
11463      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11464      * the server the name of the callback function set up by the load call to process the returned data object.
11465      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11466      * javascript output which calls this named function passing the data object as its only parameter.
11467      */
11468     callbackParam : "callback",
11469     /**
11470      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11471      * name to the request.
11472      */
11473     nocache : true,
11474
11475     /**
11476      * Load data from the configured URL, read the data object into
11477      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11478      * process that block using the passed callback.
11479      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11480      * for the request to the remote server.
11481      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11482      * object into a block of Roo.data.Records.
11483      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11484      * The function must be passed <ul>
11485      * <li>The Record block object</li>
11486      * <li>The "arg" argument from the load function</li>
11487      * <li>A boolean success indicator</li>
11488      * </ul>
11489      * @param {Object} scope The scope in which to call the callback
11490      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11491      */
11492     load : function(params, reader, callback, scope, arg){
11493         if(this.fireEvent("beforeload", this, params) !== false){
11494
11495             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11496
11497             var url = this.url;
11498             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11499             if(this.nocache){
11500                 url += "&_dc=" + (new Date().getTime());
11501             }
11502             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11503             var trans = {
11504                 id : transId,
11505                 cb : "stcCallback"+transId,
11506                 scriptId : "stcScript"+transId,
11507                 params : params,
11508                 arg : arg,
11509                 url : url,
11510                 callback : callback,
11511                 scope : scope,
11512                 reader : reader
11513             };
11514             var conn = this;
11515
11516             window[trans.cb] = function(o){
11517                 conn.handleResponse(o, trans);
11518             };
11519
11520             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11521
11522             if(this.autoAbort !== false){
11523                 this.abort();
11524             }
11525
11526             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11527
11528             var script = document.createElement("script");
11529             script.setAttribute("src", url);
11530             script.setAttribute("type", "text/javascript");
11531             script.setAttribute("id", trans.scriptId);
11532             this.head.appendChild(script);
11533
11534             this.trans = trans;
11535         }else{
11536             callback.call(scope||this, null, arg, false);
11537         }
11538     },
11539
11540     // private
11541     isLoading : function(){
11542         return this.trans ? true : false;
11543     },
11544
11545     /**
11546      * Abort the current server request.
11547      */
11548     abort : function(){
11549         if(this.isLoading()){
11550             this.destroyTrans(this.trans);
11551         }
11552     },
11553
11554     // private
11555     destroyTrans : function(trans, isLoaded){
11556         this.head.removeChild(document.getElementById(trans.scriptId));
11557         clearTimeout(trans.timeoutId);
11558         if(isLoaded){
11559             window[trans.cb] = undefined;
11560             try{
11561                 delete window[trans.cb];
11562             }catch(e){}
11563         }else{
11564             // if hasn't been loaded, wait for load to remove it to prevent script error
11565             window[trans.cb] = function(){
11566                 window[trans.cb] = undefined;
11567                 try{
11568                     delete window[trans.cb];
11569                 }catch(e){}
11570             };
11571         }
11572     },
11573
11574     // private
11575     handleResponse : function(o, trans){
11576         this.trans = false;
11577         this.destroyTrans(trans, true);
11578         var result;
11579         try {
11580             result = trans.reader.readRecords(o);
11581         }catch(e){
11582             this.fireEvent("loadexception", this, o, trans.arg, e);
11583             trans.callback.call(trans.scope||window, null, trans.arg, false);
11584             return;
11585         }
11586         this.fireEvent("load", this, o, trans.arg);
11587         trans.callback.call(trans.scope||window, result, trans.arg, true);
11588     },
11589
11590     // private
11591     handleFailure : function(trans){
11592         this.trans = false;
11593         this.destroyTrans(trans, false);
11594         this.fireEvent("loadexception", this, null, trans.arg);
11595         trans.callback.call(trans.scope||window, null, trans.arg, false);
11596     }
11597 });/*
11598  * Based on:
11599  * Ext JS Library 1.1.1
11600  * Copyright(c) 2006-2007, Ext JS, LLC.
11601  *
11602  * Originally Released Under LGPL - original licence link has changed is not relivant.
11603  *
11604  * Fork - LGPL
11605  * <script type="text/javascript">
11606  */
11607
11608 /**
11609  * @class Roo.data.JsonReader
11610  * @extends Roo.data.DataReader
11611  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11612  * based on mappings in a provided Roo.data.Record constructor.
11613  * 
11614  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11615  * in the reply previously. 
11616  * 
11617  * <p>
11618  * Example code:
11619  * <pre><code>
11620 var RecordDef = Roo.data.Record.create([
11621     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11622     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11623 ]);
11624 var myReader = new Roo.data.JsonReader({
11625     totalProperty: "results",    // The property which contains the total dataset size (optional)
11626     root: "rows",                // The property which contains an Array of row objects
11627     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11628 }, RecordDef);
11629 </code></pre>
11630  * <p>
11631  * This would consume a JSON file like this:
11632  * <pre><code>
11633 { 'results': 2, 'rows': [
11634     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11635     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11636 }
11637 </code></pre>
11638  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11639  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11640  * paged from the remote server.
11641  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11642  * @cfg {String} root name of the property which contains the Array of row objects.
11643  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11644  * @cfg {Array} fields Array of field definition objects
11645  * @constructor
11646  * Create a new JsonReader
11647  * @param {Object} meta Metadata configuration options
11648  * @param {Object} recordType Either an Array of field definition objects,
11649  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11650  */
11651 Roo.data.JsonReader = function(meta, recordType){
11652     
11653     meta = meta || {};
11654     // set some defaults:
11655     Roo.applyIf(meta, {
11656         totalProperty: 'total',
11657         successProperty : 'success',
11658         root : 'data',
11659         id : 'id'
11660     });
11661     
11662     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11663 };
11664 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11665     
11666     /**
11667      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11668      * Used by Store query builder to append _requestMeta to params.
11669      * 
11670      */
11671     metaFromRemote : false,
11672     /**
11673      * This method is only used by a DataProxy which has retrieved data from a remote server.
11674      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11675      * @return {Object} data A data block which is used by an Roo.data.Store object as
11676      * a cache of Roo.data.Records.
11677      */
11678     read : function(response){
11679         var json = response.responseText;
11680        
11681         var o = /* eval:var:o */ eval("("+json+")");
11682         if(!o) {
11683             throw {message: "JsonReader.read: Json object not found"};
11684         }
11685         
11686         if(o.metaData){
11687             
11688             delete this.ef;
11689             this.metaFromRemote = true;
11690             this.meta = o.metaData;
11691             this.recordType = Roo.data.Record.create(o.metaData.fields);
11692             this.onMetaChange(this.meta, this.recordType, o);
11693         }
11694         return this.readRecords(o);
11695     },
11696
11697     // private function a store will implement
11698     onMetaChange : function(meta, recordType, o){
11699
11700     },
11701
11702     /**
11703          * @ignore
11704          */
11705     simpleAccess: function(obj, subsc) {
11706         return obj[subsc];
11707     },
11708
11709         /**
11710          * @ignore
11711          */
11712     getJsonAccessor: function(){
11713         var re = /[\[\.]/;
11714         return function(expr) {
11715             try {
11716                 return(re.test(expr))
11717                     ? new Function("obj", "return obj." + expr)
11718                     : function(obj){
11719                         return obj[expr];
11720                     };
11721             } catch(e){}
11722             return Roo.emptyFn;
11723         };
11724     }(),
11725
11726     /**
11727      * Create a data block containing Roo.data.Records from an XML document.
11728      * @param {Object} o An object which contains an Array of row objects in the property specified
11729      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11730      * which contains the total size of the dataset.
11731      * @return {Object} data A data block which is used by an Roo.data.Store object as
11732      * a cache of Roo.data.Records.
11733      */
11734     readRecords : function(o){
11735         /**
11736          * After any data loads, the raw JSON data is available for further custom processing.
11737          * @type Object
11738          */
11739         this.o = o;
11740         var s = this.meta, Record = this.recordType,
11741             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11742
11743 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11744         if (!this.ef) {
11745             if(s.totalProperty) {
11746                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11747                 }
11748                 if(s.successProperty) {
11749                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11750                 }
11751                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11752                 if (s.id) {
11753                         var g = this.getJsonAccessor(s.id);
11754                         this.getId = function(rec) {
11755                                 var r = g(rec);  
11756                                 return (r === undefined || r === "") ? null : r;
11757                         };
11758                 } else {
11759                         this.getId = function(){return null;};
11760                 }
11761             this.ef = [];
11762             for(var jj = 0; jj < fl; jj++){
11763                 f = fi[jj];
11764                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11765                 this.ef[jj] = this.getJsonAccessor(map);
11766             }
11767         }
11768
11769         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11770         if(s.totalProperty){
11771             var vt = parseInt(this.getTotal(o), 10);
11772             if(!isNaN(vt)){
11773                 totalRecords = vt;
11774             }
11775         }
11776         if(s.successProperty){
11777             var vs = this.getSuccess(o);
11778             if(vs === false || vs === 'false'){
11779                 success = false;
11780             }
11781         }
11782         var records = [];
11783         for(var i = 0; i < c; i++){
11784                 var n = root[i];
11785             var values = {};
11786             var id = this.getId(n);
11787             for(var j = 0; j < fl; j++){
11788                 f = fi[j];
11789             var v = this.ef[j](n);
11790             if (!f.convert) {
11791                 Roo.log('missing convert for ' + f.name);
11792                 Roo.log(f);
11793                 continue;
11794             }
11795             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11796             }
11797             var record = new Record(values, id);
11798             record.json = n;
11799             records[i] = record;
11800         }
11801         return {
11802             raw : o,
11803             success : success,
11804             records : records,
11805             totalRecords : totalRecords
11806         };
11807     }
11808 });/*
11809  * Based on:
11810  * Ext JS Library 1.1.1
11811  * Copyright(c) 2006-2007, Ext JS, LLC.
11812  *
11813  * Originally Released Under LGPL - original licence link has changed is not relivant.
11814  *
11815  * Fork - LGPL
11816  * <script type="text/javascript">
11817  */
11818
11819 /**
11820  * @class Roo.data.ArrayReader
11821  * @extends Roo.data.DataReader
11822  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11823  * Each element of that Array represents a row of data fields. The
11824  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11825  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11826  * <p>
11827  * Example code:.
11828  * <pre><code>
11829 var RecordDef = Roo.data.Record.create([
11830     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11831     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11832 ]);
11833 var myReader = new Roo.data.ArrayReader({
11834     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11835 }, RecordDef);
11836 </code></pre>
11837  * <p>
11838  * This would consume an Array like this:
11839  * <pre><code>
11840 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11841   </code></pre>
11842  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11843  * @constructor
11844  * Create a new JsonReader
11845  * @param {Object} meta Metadata configuration options.
11846  * @param {Object} recordType Either an Array of field definition objects
11847  * as specified to {@link Roo.data.Record#create},
11848  * or an {@link Roo.data.Record} object
11849  * created using {@link Roo.data.Record#create}.
11850  */
11851 Roo.data.ArrayReader = function(meta, recordType){
11852     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11853 };
11854
11855 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11856     /**
11857      * Create a data block containing Roo.data.Records from an XML document.
11858      * @param {Object} o An Array of row objects which represents the dataset.
11859      * @return {Object} data A data block which is used by an Roo.data.Store object as
11860      * a cache of Roo.data.Records.
11861      */
11862     readRecords : function(o){
11863         var sid = this.meta ? this.meta.id : null;
11864         var recordType = this.recordType, fields = recordType.prototype.fields;
11865         var records = [];
11866         var root = o;
11867             for(var i = 0; i < root.length; i++){
11868                     var n = root[i];
11869                 var values = {};
11870                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11871                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11872                 var f = fields.items[j];
11873                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11874                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11875                 v = f.convert(v);
11876                 values[f.name] = v;
11877             }
11878                 var record = new recordType(values, id);
11879                 record.json = n;
11880                 records[records.length] = record;
11881             }
11882             return {
11883                 records : records,
11884                 totalRecords : records.length
11885             };
11886     }
11887 });/*
11888  * - LGPL
11889  * * 
11890  */
11891
11892 /**
11893  * @class Roo.bootstrap.ComboBox
11894  * @extends Roo.bootstrap.TriggerField
11895  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11896  * @cfg {Boolean} append (true|false) default false
11897  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11898  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11899  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11900  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11901  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11902  * @cfg {Boolean} animate default true
11903  * @cfg {Boolean} emptyResultText only for touch device
11904  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11905  * @constructor
11906  * Create a new ComboBox.
11907  * @param {Object} config Configuration options
11908  */
11909 Roo.bootstrap.ComboBox = function(config){
11910     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11911     this.addEvents({
11912         /**
11913          * @event expand
11914          * Fires when the dropdown list is expanded
11915              * @param {Roo.bootstrap.ComboBox} combo This combo box
11916              */
11917         'expand' : true,
11918         /**
11919          * @event collapse
11920          * Fires when the dropdown list is collapsed
11921              * @param {Roo.bootstrap.ComboBox} combo This combo box
11922              */
11923         'collapse' : true,
11924         /**
11925          * @event beforeselect
11926          * Fires before a list item is selected. Return false to cancel the selection.
11927              * @param {Roo.bootstrap.ComboBox} combo This combo box
11928              * @param {Roo.data.Record} record The data record returned from the underlying store
11929              * @param {Number} index The index of the selected item in the dropdown list
11930              */
11931         'beforeselect' : true,
11932         /**
11933          * @event select
11934          * Fires when a list item is selected
11935              * @param {Roo.bootstrap.ComboBox} combo This combo box
11936              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11937              * @param {Number} index The index of the selected item in the dropdown list
11938              */
11939         'select' : true,
11940         /**
11941          * @event beforequery
11942          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11943          * The event object passed has these properties:
11944              * @param {Roo.bootstrap.ComboBox} combo This combo box
11945              * @param {String} query The query
11946              * @param {Boolean} forceAll true to force "all" query
11947              * @param {Boolean} cancel true to cancel the query
11948              * @param {Object} e The query event object
11949              */
11950         'beforequery': true,
11951          /**
11952          * @event add
11953          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11954              * @param {Roo.bootstrap.ComboBox} combo This combo box
11955              */
11956         'add' : true,
11957         /**
11958          * @event edit
11959          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11960              * @param {Roo.bootstrap.ComboBox} combo This combo box
11961              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11962              */
11963         'edit' : true,
11964         /**
11965          * @event remove
11966          * Fires when the remove value from the combobox array
11967              * @param {Roo.bootstrap.ComboBox} combo This combo box
11968              */
11969         'remove' : true,
11970         /**
11971          * @event afterremove
11972          * Fires when the remove value from the combobox array
11973              * @param {Roo.bootstrap.ComboBox} combo This combo box
11974              */
11975         'afterremove' : true,
11976         /**
11977          * @event specialfilter
11978          * Fires when specialfilter
11979             * @param {Roo.bootstrap.ComboBox} combo This combo box
11980             */
11981         'specialfilter' : true,
11982         /**
11983          * @event tick
11984          * Fires when tick the element
11985             * @param {Roo.bootstrap.ComboBox} combo This combo box
11986             */
11987         'tick' : true,
11988         /**
11989          * @event touchviewdisplay
11990          * Fires when touch view require special display (default is using displayField)
11991             * @param {Roo.bootstrap.ComboBox} combo This combo box
11992             * @param {Object} cfg set html .
11993             */
11994         'touchviewdisplay' : true
11995         
11996     });
11997     
11998     this.item = [];
11999     this.tickItems = [];
12000     
12001     this.selectedIndex = -1;
12002     if(this.mode == 'local'){
12003         if(config.queryDelay === undefined){
12004             this.queryDelay = 10;
12005         }
12006         if(config.minChars === undefined){
12007             this.minChars = 0;
12008         }
12009     }
12010 };
12011
12012 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12013      
12014     /**
12015      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12016      * rendering into an Roo.Editor, defaults to false)
12017      */
12018     /**
12019      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12020      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12021      */
12022     /**
12023      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12024      */
12025     /**
12026      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12027      * the dropdown list (defaults to undefined, with no header element)
12028      */
12029
12030      /**
12031      * @cfg {String/Roo.Template} tpl The template to use to render the output
12032      */
12033      
12034      /**
12035      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12036      */
12037     listWidth: undefined,
12038     /**
12039      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12040      * mode = 'remote' or 'text' if mode = 'local')
12041      */
12042     displayField: undefined,
12043     
12044     /**
12045      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12046      * mode = 'remote' or 'value' if mode = 'local'). 
12047      * Note: use of a valueField requires the user make a selection
12048      * in order for a value to be mapped.
12049      */
12050     valueField: undefined,
12051     /**
12052      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12053      */
12054     modalTitle : '',
12055     
12056     /**
12057      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12058      * field's data value (defaults to the underlying DOM element's name)
12059      */
12060     hiddenName: undefined,
12061     /**
12062      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12063      */
12064     listClass: '',
12065     /**
12066      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12067      */
12068     selectedClass: 'active',
12069     
12070     /**
12071      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12072      */
12073     shadow:'sides',
12074     /**
12075      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12076      * anchor positions (defaults to 'tl-bl')
12077      */
12078     listAlign: 'tl-bl?',
12079     /**
12080      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12081      */
12082     maxHeight: 300,
12083     /**
12084      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12085      * query specified by the allQuery config option (defaults to 'query')
12086      */
12087     triggerAction: 'query',
12088     /**
12089      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12090      * (defaults to 4, does not apply if editable = false)
12091      */
12092     minChars : 4,
12093     /**
12094      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12095      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12096      */
12097     typeAhead: false,
12098     /**
12099      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12100      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12101      */
12102     queryDelay: 500,
12103     /**
12104      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12105      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12106      */
12107     pageSize: 0,
12108     /**
12109      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12110      * when editable = true (defaults to false)
12111      */
12112     selectOnFocus:false,
12113     /**
12114      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12115      */
12116     queryParam: 'query',
12117     /**
12118      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12119      * when mode = 'remote' (defaults to 'Loading...')
12120      */
12121     loadingText: 'Loading...',
12122     /**
12123      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12124      */
12125     resizable: false,
12126     /**
12127      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12128      */
12129     handleHeight : 8,
12130     /**
12131      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12132      * traditional select (defaults to true)
12133      */
12134     editable: true,
12135     /**
12136      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12137      */
12138     allQuery: '',
12139     /**
12140      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12141      */
12142     mode: 'remote',
12143     /**
12144      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12145      * listWidth has a higher value)
12146      */
12147     minListWidth : 70,
12148     /**
12149      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12150      * allow the user to set arbitrary text into the field (defaults to false)
12151      */
12152     forceSelection:false,
12153     /**
12154      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12155      * if typeAhead = true (defaults to 250)
12156      */
12157     typeAheadDelay : 250,
12158     /**
12159      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12160      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12161      */
12162     valueNotFoundText : undefined,
12163     /**
12164      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12165      */
12166     blockFocus : false,
12167     
12168     /**
12169      * @cfg {Boolean} disableClear Disable showing of clear button.
12170      */
12171     disableClear : false,
12172     /**
12173      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12174      */
12175     alwaysQuery : false,
12176     
12177     /**
12178      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12179      */
12180     multiple : false,
12181     
12182     /**
12183      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12184      */
12185     invalidClass : "has-warning",
12186     
12187     /**
12188      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12189      */
12190     validClass : "has-success",
12191     
12192     /**
12193      * @cfg {Boolean} specialFilter (true|false) special filter default false
12194      */
12195     specialFilter : false,
12196     
12197     /**
12198      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12199      */
12200     mobileTouchView : true,
12201     
12202     //private
12203     addicon : false,
12204     editicon: false,
12205     
12206     page: 0,
12207     hasQuery: false,
12208     append: false,
12209     loadNext: false,
12210     autoFocus : true,
12211     tickable : false,
12212     btnPosition : 'right',
12213     triggerList : true,
12214     showToggleBtn : true,
12215     animate : true,
12216     emptyResultText: 'Empty',
12217     triggerText : 'Select',
12218     
12219     // element that contains real text value.. (when hidden is used..)
12220     
12221     getAutoCreate : function()
12222     {
12223         var cfg = false;
12224         
12225         /*
12226          * Touch Devices
12227          */
12228         
12229         if(Roo.isTouch && this.mobileTouchView){
12230             cfg = this.getAutoCreateTouchView();
12231             return cfg;;
12232         }
12233         
12234         /*
12235          *  Normal ComboBox
12236          */
12237         if(!this.tickable){
12238             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12239             return cfg;
12240         }
12241         
12242         /*
12243          *  ComboBox with tickable selections
12244          */
12245              
12246         var align = this.labelAlign || this.parentLabelAlign();
12247         
12248         cfg = {
12249             cls : 'form-group roo-combobox-tickable' //input-group
12250         };
12251         
12252         var buttons = {
12253             tag : 'div',
12254             cls : 'tickable-buttons',
12255             cn : [
12256                 {
12257                     tag : 'button',
12258                     type : 'button',
12259                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12260                     html : this.triggerText
12261                 },
12262                 {
12263                     tag : 'button',
12264                     type : 'button',
12265                     name : 'ok',
12266                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12267                     html : 'Done'
12268                 },
12269                 {
12270                     tag : 'button',
12271                     type : 'button',
12272                     name : 'cancel',
12273                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12274                     html : 'Cancel'
12275                 }
12276             ]
12277         };
12278         
12279         if(this.editable){
12280             buttons.cn.unshift({
12281                 tag: 'input',
12282                 cls: 'roo-select2-search-field-input'
12283             });
12284         }
12285         
12286         var _this = this;
12287         
12288         Roo.each(buttons.cn, function(c){
12289             if (_this.size) {
12290                 c.cls += ' btn-' + _this.size;
12291             }
12292
12293             if (_this.disabled) {
12294                 c.disabled = true;
12295             }
12296         });
12297         
12298         var box = {
12299             tag: 'div',
12300             cn: [
12301                 {
12302                     tag: 'input',
12303                     type : 'hidden',
12304                     cls: 'form-hidden-field'
12305                 },
12306                 {
12307                     tag: 'ul',
12308                     cls: 'roo-select2-choices',
12309                     cn:[
12310                         {
12311                             tag: 'li',
12312                             cls: 'roo-select2-search-field',
12313                             cn: [
12314
12315                                 buttons
12316                             ]
12317                         }
12318                     ]
12319                 }
12320             ]
12321         };
12322         
12323         var combobox = {
12324             cls: 'roo-select2-container input-group roo-select2-container-multi',
12325             cn: [
12326                 box
12327 //                {
12328 //                    tag: 'ul',
12329 //                    cls: 'typeahead typeahead-long dropdown-menu',
12330 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12331 //                }
12332             ]
12333         };
12334         
12335         if(this.hasFeedback && !this.allowBlank){
12336             
12337             var feedback = {
12338                 tag: 'span',
12339                 cls: 'glyphicon form-control-feedback'
12340             };
12341
12342             combobox.cn.push(feedback);
12343         }
12344         
12345         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12346             
12347 //                Roo.log("left and has label");
12348             cfg.cn = [
12349                 {
12350                     tag : 'i',
12351                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12352                     tooltip : 'This field is required'
12353                 },
12354                 {
12355                     tag: 'label',
12356                     'for' :  id,
12357                     cls : 'control-label col-sm-' + this.labelWidth,
12358                     html : this.fieldLabel
12359
12360                 },
12361                 {
12362                     cls : "col-sm-" + (12 - this.labelWidth), 
12363                     cn: [
12364                         combobox
12365                     ]
12366                 }
12367
12368             ];
12369
12370             if(this.indicatorpos == 'right'){
12371                 
12372                 cfg.cn = [
12373                     {
12374                         tag: 'label',
12375                         'for' :  id,
12376                         cls : 'control-label col-sm-' + this.labelWidth,
12377                         html : this.fieldLabel
12378
12379                     },
12380                     {
12381                         tag : 'i',
12382                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12383                         tooltip : 'This field is required'
12384                     },
12385                     {
12386                         cls : "col-sm-" + (12 - this.labelWidth), 
12387                         cn: [
12388                             combobox
12389                         ]
12390                     }
12391
12392                 ];
12393             
12394             }
12395                 
12396                 
12397         } else if ( this.fieldLabel.length) {
12398 //                Roo.log(" label");
12399                  cfg.cn = [
12400                     {
12401                         tag : 'i',
12402                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12403                         tooltip : 'This field is required'
12404                     },
12405                     {
12406                         tag: 'label',
12407                         //cls : 'input-group-addon',
12408                         html : this.fieldLabel
12409                         
12410                     },
12411                     
12412                     combobox
12413                     
12414                 ];
12415                 
12416                 if(this.indicatorpos == 'right'){
12417                     
12418                     cfg.cn = [
12419                         {
12420                             tag: 'label',
12421                             //cls : 'input-group-addon',
12422                             html : this.fieldLabel
12423
12424                         },
12425                         
12426                         {
12427                             tag : 'i',
12428                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12429                             tooltip : 'This field is required'
12430                         },
12431                         
12432                         combobox
12433
12434                     ];
12435                 
12436                 }
12437
12438         } else {
12439             
12440 //                Roo.log(" no label && no align");
12441                 cfg = combobox
12442                      
12443                 
12444         }
12445          
12446         var settings=this;
12447         ['xs','sm','md','lg'].map(function(size){
12448             if (settings[size]) {
12449                 cfg.cls += ' col-' + size + '-' + settings[size];
12450             }
12451         });
12452         
12453         return cfg;
12454         
12455     },
12456     
12457     _initEventsCalled : false,
12458     
12459     // private
12460     initEvents: function()
12461     {
12462         
12463         if (this._initEventsCalled) { // as we call render... prevent looping...
12464             return;
12465         }
12466         this._initEventsCalled = true;
12467         
12468         if (!this.store) {
12469             throw "can not find store for combo";
12470         }
12471         
12472         this.store = Roo.factory(this.store, Roo.data);
12473         
12474         // if we are building from html. then this element is so complex, that we can not really
12475         // use the rendered HTML.
12476         // so we have to trash and replace the previous code.
12477         if (Roo.XComponent.build_from_html) {
12478             
12479             // remove this element....
12480             var e = this.el.dom, k=0;
12481             while (e ) { e = e.previousSibling;  ++k;}
12482
12483             this.el.remove();
12484             
12485             this.el=false;
12486             this.rendered = false;
12487             
12488             this.render(this.parent().getChildContainer(true), k);
12489             
12490             
12491             
12492         }
12493         
12494         
12495         /*
12496          * Touch Devices
12497          */
12498         
12499         if(Roo.isTouch && this.mobileTouchView){
12500             this.initTouchView();
12501             return;
12502         }
12503         
12504         if(this.tickable){
12505             this.initTickableEvents();
12506             return;
12507         }
12508         
12509         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12510         
12511         if(this.hiddenName){
12512             
12513             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12514             
12515             this.hiddenField.dom.value =
12516                 this.hiddenValue !== undefined ? this.hiddenValue :
12517                 this.value !== undefined ? this.value : '';
12518
12519             // prevent input submission
12520             this.el.dom.removeAttribute('name');
12521             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12522              
12523              
12524         }
12525         //if(Roo.isGecko){
12526         //    this.el.dom.setAttribute('autocomplete', 'off');
12527         //}
12528         
12529         var cls = 'x-combo-list';
12530         
12531         //this.list = new Roo.Layer({
12532         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12533         //});
12534         
12535         var _this = this;
12536         
12537         (function(){
12538             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12539             _this.list.setWidth(lw);
12540         }).defer(100);
12541         
12542         this.list.on('mouseover', this.onViewOver, this);
12543         this.list.on('mousemove', this.onViewMove, this);
12544         
12545         this.list.on('scroll', this.onViewScroll, this);
12546         
12547         /*
12548         this.list.swallowEvent('mousewheel');
12549         this.assetHeight = 0;
12550
12551         if(this.title){
12552             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12553             this.assetHeight += this.header.getHeight();
12554         }
12555
12556         this.innerList = this.list.createChild({cls:cls+'-inner'});
12557         this.innerList.on('mouseover', this.onViewOver, this);
12558         this.innerList.on('mousemove', this.onViewMove, this);
12559         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12560         
12561         if(this.allowBlank && !this.pageSize && !this.disableClear){
12562             this.footer = this.list.createChild({cls:cls+'-ft'});
12563             this.pageTb = new Roo.Toolbar(this.footer);
12564            
12565         }
12566         if(this.pageSize){
12567             this.footer = this.list.createChild({cls:cls+'-ft'});
12568             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12569                     {pageSize: this.pageSize});
12570             
12571         }
12572         
12573         if (this.pageTb && this.allowBlank && !this.disableClear) {
12574             var _this = this;
12575             this.pageTb.add(new Roo.Toolbar.Fill(), {
12576                 cls: 'x-btn-icon x-btn-clear',
12577                 text: '&#160;',
12578                 handler: function()
12579                 {
12580                     _this.collapse();
12581                     _this.clearValue();
12582                     _this.onSelect(false, -1);
12583                 }
12584             });
12585         }
12586         if (this.footer) {
12587             this.assetHeight += this.footer.getHeight();
12588         }
12589         */
12590             
12591         if(!this.tpl){
12592             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12593         }
12594
12595         this.view = new Roo.View(this.list, this.tpl, {
12596             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12597         });
12598         //this.view.wrapEl.setDisplayed(false);
12599         this.view.on('click', this.onViewClick, this);
12600         
12601         
12602         
12603         this.store.on('beforeload', this.onBeforeLoad, this);
12604         this.store.on('load', this.onLoad, this);
12605         this.store.on('loadexception', this.onLoadException, this);
12606         /*
12607         if(this.resizable){
12608             this.resizer = new Roo.Resizable(this.list,  {
12609                pinned:true, handles:'se'
12610             });
12611             this.resizer.on('resize', function(r, w, h){
12612                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12613                 this.listWidth = w;
12614                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12615                 this.restrictHeight();
12616             }, this);
12617             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12618         }
12619         */
12620         if(!this.editable){
12621             this.editable = true;
12622             this.setEditable(false);
12623         }
12624         
12625         /*
12626         
12627         if (typeof(this.events.add.listeners) != 'undefined') {
12628             
12629             this.addicon = this.wrap.createChild(
12630                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12631        
12632             this.addicon.on('click', function(e) {
12633                 this.fireEvent('add', this);
12634             }, this);
12635         }
12636         if (typeof(this.events.edit.listeners) != 'undefined') {
12637             
12638             this.editicon = this.wrap.createChild(
12639                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12640             if (this.addicon) {
12641                 this.editicon.setStyle('margin-left', '40px');
12642             }
12643             this.editicon.on('click', function(e) {
12644                 
12645                 // we fire even  if inothing is selected..
12646                 this.fireEvent('edit', this, this.lastData );
12647                 
12648             }, this);
12649         }
12650         */
12651         
12652         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12653             "up" : function(e){
12654                 this.inKeyMode = true;
12655                 this.selectPrev();
12656             },
12657
12658             "down" : function(e){
12659                 if(!this.isExpanded()){
12660                     this.onTriggerClick();
12661                 }else{
12662                     this.inKeyMode = true;
12663                     this.selectNext();
12664                 }
12665             },
12666
12667             "enter" : function(e){
12668 //                this.onViewClick();
12669                 //return true;
12670                 this.collapse();
12671                 
12672                 if(this.fireEvent("specialkey", this, e)){
12673                     this.onViewClick(false);
12674                 }
12675                 
12676                 return true;
12677             },
12678
12679             "esc" : function(e){
12680                 this.collapse();
12681             },
12682
12683             "tab" : function(e){
12684                 this.collapse();
12685                 
12686                 if(this.fireEvent("specialkey", this, e)){
12687                     this.onViewClick(false);
12688                 }
12689                 
12690                 return true;
12691             },
12692
12693             scope : this,
12694
12695             doRelay : function(foo, bar, hname){
12696                 if(hname == 'down' || this.scope.isExpanded()){
12697                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12698                 }
12699                 return true;
12700             },
12701
12702             forceKeyDown: true
12703         });
12704         
12705         
12706         this.queryDelay = Math.max(this.queryDelay || 10,
12707                 this.mode == 'local' ? 10 : 250);
12708         
12709         
12710         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12711         
12712         if(this.typeAhead){
12713             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12714         }
12715         if(this.editable !== false){
12716             this.inputEl().on("keyup", this.onKeyUp, this);
12717         }
12718         if(this.forceSelection){
12719             this.inputEl().on('blur', this.doForce, this);
12720         }
12721         
12722         if(this.multiple){
12723             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12724             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12725         }
12726     },
12727     
12728     initTickableEvents: function()
12729     {   
12730         this.createList();
12731         
12732         if(this.hiddenName){
12733             
12734             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12735             
12736             this.hiddenField.dom.value =
12737                 this.hiddenValue !== undefined ? this.hiddenValue :
12738                 this.value !== undefined ? this.value : '';
12739
12740             // prevent input submission
12741             this.el.dom.removeAttribute('name');
12742             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12743              
12744              
12745         }
12746         
12747 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12748         
12749         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12750         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12751         if(this.triggerList){
12752             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12753         }
12754          
12755         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12756         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12757         
12758         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12759         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12760         
12761         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12762         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12763         
12764         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12765         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12766         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12767         
12768         this.okBtn.hide();
12769         this.cancelBtn.hide();
12770         
12771         var _this = this;
12772         
12773         (function(){
12774             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12775             _this.list.setWidth(lw);
12776         }).defer(100);
12777         
12778         this.list.on('mouseover', this.onViewOver, this);
12779         this.list.on('mousemove', this.onViewMove, this);
12780         
12781         this.list.on('scroll', this.onViewScroll, this);
12782         
12783         if(!this.tpl){
12784             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>';
12785         }
12786
12787         this.view = new Roo.View(this.list, this.tpl, {
12788             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12789         });
12790         
12791         //this.view.wrapEl.setDisplayed(false);
12792         this.view.on('click', this.onViewClick, this);
12793         
12794         
12795         
12796         this.store.on('beforeload', this.onBeforeLoad, this);
12797         this.store.on('load', this.onLoad, this);
12798         this.store.on('loadexception', this.onLoadException, this);
12799         
12800         if(this.editable){
12801             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12802                 "up" : function(e){
12803                     this.inKeyMode = true;
12804                     this.selectPrev();
12805                 },
12806
12807                 "down" : function(e){
12808                     this.inKeyMode = true;
12809                     this.selectNext();
12810                 },
12811
12812                 "enter" : function(e){
12813                     if(this.fireEvent("specialkey", this, e)){
12814                         this.onViewClick(false);
12815                     }
12816                     
12817                     return true;
12818                 },
12819
12820                 "esc" : function(e){
12821                     this.onTickableFooterButtonClick(e, false, false);
12822                 },
12823
12824                 "tab" : function(e){
12825                     this.fireEvent("specialkey", this, e);
12826                     
12827                     this.onTickableFooterButtonClick(e, false, false);
12828                     
12829                     return true;
12830                 },
12831
12832                 scope : this,
12833
12834                 doRelay : function(e, fn, key){
12835                     if(this.scope.isExpanded()){
12836                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12837                     }
12838                     return true;
12839                 },
12840
12841                 forceKeyDown: true
12842             });
12843         }
12844         
12845         this.queryDelay = Math.max(this.queryDelay || 10,
12846                 this.mode == 'local' ? 10 : 250);
12847         
12848         
12849         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12850         
12851         if(this.typeAhead){
12852             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12853         }
12854         
12855         if(this.editable !== false){
12856             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12857         }
12858         
12859     },
12860
12861     onDestroy : function(){
12862         if(this.view){
12863             this.view.setStore(null);
12864             this.view.el.removeAllListeners();
12865             this.view.el.remove();
12866             this.view.purgeListeners();
12867         }
12868         if(this.list){
12869             this.list.dom.innerHTML  = '';
12870         }
12871         
12872         if(this.store){
12873             this.store.un('beforeload', this.onBeforeLoad, this);
12874             this.store.un('load', this.onLoad, this);
12875             this.store.un('loadexception', this.onLoadException, this);
12876         }
12877         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12878     },
12879
12880     // private
12881     fireKey : function(e){
12882         if(e.isNavKeyPress() && !this.list.isVisible()){
12883             this.fireEvent("specialkey", this, e);
12884         }
12885     },
12886
12887     // private
12888     onResize: function(w, h){
12889 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12890 //        
12891 //        if(typeof w != 'number'){
12892 //            // we do not handle it!?!?
12893 //            return;
12894 //        }
12895 //        var tw = this.trigger.getWidth();
12896 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12897 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12898 //        var x = w - tw;
12899 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12900 //            
12901 //        //this.trigger.setStyle('left', x+'px');
12902 //        
12903 //        if(this.list && this.listWidth === undefined){
12904 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12905 //            this.list.setWidth(lw);
12906 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12907 //        }
12908         
12909     
12910         
12911     },
12912
12913     /**
12914      * Allow or prevent the user from directly editing the field text.  If false is passed,
12915      * the user will only be able to select from the items defined in the dropdown list.  This method
12916      * is the runtime equivalent of setting the 'editable' config option at config time.
12917      * @param {Boolean} value True to allow the user to directly edit the field text
12918      */
12919     setEditable : function(value){
12920         if(value == this.editable){
12921             return;
12922         }
12923         this.editable = value;
12924         if(!value){
12925             this.inputEl().dom.setAttribute('readOnly', true);
12926             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12927             this.inputEl().addClass('x-combo-noedit');
12928         }else{
12929             this.inputEl().dom.setAttribute('readOnly', false);
12930             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12931             this.inputEl().removeClass('x-combo-noedit');
12932         }
12933     },
12934
12935     // private
12936     
12937     onBeforeLoad : function(combo,opts){
12938         if(!this.hasFocus){
12939             return;
12940         }
12941          if (!opts.add) {
12942             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12943          }
12944         this.restrictHeight();
12945         this.selectedIndex = -1;
12946     },
12947
12948     // private
12949     onLoad : function(){
12950         
12951         this.hasQuery = false;
12952         
12953         if(!this.hasFocus){
12954             return;
12955         }
12956         
12957         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12958             this.loading.hide();
12959         }
12960              
12961         if(this.store.getCount() > 0){
12962             this.expand();
12963             this.restrictHeight();
12964             if(this.lastQuery == this.allQuery){
12965                 if(this.editable && !this.tickable){
12966                     this.inputEl().dom.select();
12967                 }
12968                 
12969                 if(
12970                     !this.selectByValue(this.value, true) &&
12971                     this.autoFocus && 
12972                     (
12973                         !this.store.lastOptions ||
12974                         typeof(this.store.lastOptions.add) == 'undefined' || 
12975                         this.store.lastOptions.add != true
12976                     )
12977                 ){
12978                     this.select(0, true);
12979                 }
12980             }else{
12981                 if(this.autoFocus){
12982                     this.selectNext();
12983                 }
12984                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12985                     this.taTask.delay(this.typeAheadDelay);
12986                 }
12987             }
12988         }else{
12989             this.onEmptyResults();
12990         }
12991         
12992         //this.el.focus();
12993     },
12994     // private
12995     onLoadException : function()
12996     {
12997         this.hasQuery = false;
12998         
12999         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13000             this.loading.hide();
13001         }
13002         
13003         if(this.tickable && this.editable){
13004             return;
13005         }
13006         
13007         this.collapse();
13008         // only causes errors at present
13009         //Roo.log(this.store.reader.jsonData);
13010         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13011             // fixme
13012             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13013         //}
13014         
13015         
13016     },
13017     // private
13018     onTypeAhead : function(){
13019         if(this.store.getCount() > 0){
13020             var r = this.store.getAt(0);
13021             var newValue = r.data[this.displayField];
13022             var len = newValue.length;
13023             var selStart = this.getRawValue().length;
13024             
13025             if(selStart != len){
13026                 this.setRawValue(newValue);
13027                 this.selectText(selStart, newValue.length);
13028             }
13029         }
13030     },
13031
13032     // private
13033     onSelect : function(record, index){
13034         
13035         if(this.fireEvent('beforeselect', this, record, index) !== false){
13036         
13037             this.setFromData(index > -1 ? record.data : false);
13038             
13039             this.collapse();
13040             this.fireEvent('select', this, record, index);
13041         }
13042     },
13043
13044     /**
13045      * Returns the currently selected field value or empty string if no value is set.
13046      * @return {String} value The selected value
13047      */
13048     getValue : function(){
13049         
13050         if(this.multiple){
13051             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13052         }
13053         
13054         if(this.valueField){
13055             return typeof this.value != 'undefined' ? this.value : '';
13056         }else{
13057             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13058         }
13059     },
13060
13061     /**
13062      * Clears any text/value currently set in the field
13063      */
13064     clearValue : function(){
13065         if(this.hiddenField){
13066             this.hiddenField.dom.value = '';
13067         }
13068         this.value = '';
13069         this.setRawValue('');
13070         this.lastSelectionText = '';
13071         this.lastData = false;
13072         
13073         var close = this.closeTriggerEl();
13074         
13075         if(close){
13076             close.hide();
13077         }
13078         
13079         this.validate();
13080         
13081     },
13082
13083     /**
13084      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13085      * will be displayed in the field.  If the value does not match the data value of an existing item,
13086      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13087      * Otherwise the field will be blank (although the value will still be set).
13088      * @param {String} value The value to match
13089      */
13090     setValue : function(v){
13091         if(this.multiple){
13092             this.syncValue();
13093             return;
13094         }
13095         
13096         var text = v;
13097         if(this.valueField){
13098             var r = this.findRecord(this.valueField, v);
13099             if(r){
13100                 text = r.data[this.displayField];
13101             }else if(this.valueNotFoundText !== undefined){
13102                 text = this.valueNotFoundText;
13103             }
13104         }
13105         this.lastSelectionText = text;
13106         if(this.hiddenField){
13107             this.hiddenField.dom.value = v;
13108         }
13109         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13110         this.value = v;
13111         
13112         var close = this.closeTriggerEl();
13113         
13114         if(close){
13115             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13116         }
13117         
13118         this.validate();
13119     },
13120     /**
13121      * @property {Object} the last set data for the element
13122      */
13123     
13124     lastData : false,
13125     /**
13126      * Sets the value of the field based on a object which is related to the record format for the store.
13127      * @param {Object} value the value to set as. or false on reset?
13128      */
13129     setFromData : function(o){
13130         
13131         if(this.multiple){
13132             this.addItem(o);
13133             return;
13134         }
13135             
13136         var dv = ''; // display value
13137         var vv = ''; // value value..
13138         this.lastData = o;
13139         if (this.displayField) {
13140             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13141         } else {
13142             // this is an error condition!!!
13143             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13144         }
13145         
13146         if(this.valueField){
13147             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13148         }
13149         
13150         var close = this.closeTriggerEl();
13151         
13152         if(close){
13153             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13154         }
13155         
13156         if(this.hiddenField){
13157             this.hiddenField.dom.value = vv;
13158             
13159             this.lastSelectionText = dv;
13160             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13161             this.value = vv;
13162             return;
13163         }
13164         // no hidden field.. - we store the value in 'value', but still display
13165         // display field!!!!
13166         this.lastSelectionText = dv;
13167         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13168         this.value = vv;
13169         
13170         
13171         
13172     },
13173     // private
13174     reset : function(){
13175         // overridden so that last data is reset..
13176         
13177         if(this.multiple){
13178             this.clearItem();
13179             return;
13180         }
13181         
13182         this.setValue(this.originalValue);
13183         //this.clearInvalid();
13184         this.lastData = false;
13185         if (this.view) {
13186             this.view.clearSelections();
13187         }
13188         
13189         this.validate();
13190     },
13191     // private
13192     findRecord : function(prop, value){
13193         var record;
13194         if(this.store.getCount() > 0){
13195             this.store.each(function(r){
13196                 if(r.data[prop] == value){
13197                     record = r;
13198                     return false;
13199                 }
13200                 return true;
13201             });
13202         }
13203         return record;
13204     },
13205     
13206     getName: function()
13207     {
13208         // returns hidden if it's set..
13209         if (!this.rendered) {return ''};
13210         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13211         
13212     },
13213     // private
13214     onViewMove : function(e, t){
13215         this.inKeyMode = false;
13216     },
13217
13218     // private
13219     onViewOver : function(e, t){
13220         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13221             return;
13222         }
13223         var item = this.view.findItemFromChild(t);
13224         
13225         if(item){
13226             var index = this.view.indexOf(item);
13227             this.select(index, false);
13228         }
13229     },
13230
13231     // private
13232     onViewClick : function(view, doFocus, el, e)
13233     {
13234         var index = this.view.getSelectedIndexes()[0];
13235         
13236         var r = this.store.getAt(index);
13237         
13238         if(this.tickable){
13239             
13240             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13241                 return;
13242             }
13243             
13244             var rm = false;
13245             var _this = this;
13246             
13247             Roo.each(this.tickItems, function(v,k){
13248                 
13249                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13250                     Roo.log(v);
13251                     _this.tickItems.splice(k, 1);
13252                     
13253                     if(typeof(e) == 'undefined' && view == false){
13254                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13255                     }
13256                     
13257                     rm = true;
13258                     return;
13259                 }
13260             });
13261             
13262             if(rm){
13263                 return;
13264             }
13265             
13266             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13267                 this.tickItems.push(r.data);
13268             }
13269             
13270             if(typeof(e) == 'undefined' && view == false){
13271                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13272             }
13273                     
13274             return;
13275         }
13276         
13277         if(r){
13278             this.onSelect(r, index);
13279         }
13280         if(doFocus !== false && !this.blockFocus){
13281             this.inputEl().focus();
13282         }
13283     },
13284
13285     // private
13286     restrictHeight : function(){
13287         //this.innerList.dom.style.height = '';
13288         //var inner = this.innerList.dom;
13289         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13290         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13291         //this.list.beginUpdate();
13292         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13293         this.list.alignTo(this.inputEl(), this.listAlign);
13294         this.list.alignTo(this.inputEl(), this.listAlign);
13295         //this.list.endUpdate();
13296     },
13297
13298     // private
13299     onEmptyResults : function(){
13300         
13301         if(this.tickable && this.editable){
13302             this.restrictHeight();
13303             return;
13304         }
13305         
13306         this.collapse();
13307     },
13308
13309     /**
13310      * Returns true if the dropdown list is expanded, else false.
13311      */
13312     isExpanded : function(){
13313         return this.list.isVisible();
13314     },
13315
13316     /**
13317      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13318      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13319      * @param {String} value The data value of the item to select
13320      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13321      * selected item if it is not currently in view (defaults to true)
13322      * @return {Boolean} True if the value matched an item in the list, else false
13323      */
13324     selectByValue : function(v, scrollIntoView){
13325         if(v !== undefined && v !== null){
13326             var r = this.findRecord(this.valueField || this.displayField, v);
13327             if(r){
13328                 this.select(this.store.indexOf(r), scrollIntoView);
13329                 return true;
13330             }
13331         }
13332         return false;
13333     },
13334
13335     /**
13336      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13337      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13338      * @param {Number} index The zero-based index of the list item to select
13339      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13340      * selected item if it is not currently in view (defaults to true)
13341      */
13342     select : function(index, scrollIntoView){
13343         this.selectedIndex = index;
13344         this.view.select(index);
13345         if(scrollIntoView !== false){
13346             var el = this.view.getNode(index);
13347             /*
13348              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13349              */
13350             if(el){
13351                 this.list.scrollChildIntoView(el, false);
13352             }
13353         }
13354     },
13355
13356     // private
13357     selectNext : function(){
13358         var ct = this.store.getCount();
13359         if(ct > 0){
13360             if(this.selectedIndex == -1){
13361                 this.select(0);
13362             }else if(this.selectedIndex < ct-1){
13363                 this.select(this.selectedIndex+1);
13364             }
13365         }
13366     },
13367
13368     // private
13369     selectPrev : function(){
13370         var ct = this.store.getCount();
13371         if(ct > 0){
13372             if(this.selectedIndex == -1){
13373                 this.select(0);
13374             }else if(this.selectedIndex != 0){
13375                 this.select(this.selectedIndex-1);
13376             }
13377         }
13378     },
13379
13380     // private
13381     onKeyUp : function(e){
13382         if(this.editable !== false && !e.isSpecialKey()){
13383             this.lastKey = e.getKey();
13384             this.dqTask.delay(this.queryDelay);
13385         }
13386     },
13387
13388     // private
13389     validateBlur : function(){
13390         return !this.list || !this.list.isVisible();   
13391     },
13392
13393     // private
13394     initQuery : function(){
13395         
13396         var v = this.getRawValue();
13397         
13398         if(this.tickable && this.editable){
13399             v = this.tickableInputEl().getValue();
13400         }
13401         
13402         this.doQuery(v);
13403     },
13404
13405     // private
13406     doForce : function(){
13407         if(this.inputEl().dom.value.length > 0){
13408             this.inputEl().dom.value =
13409                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13410              
13411         }
13412     },
13413
13414     /**
13415      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13416      * query allowing the query action to be canceled if needed.
13417      * @param {String} query The SQL query to execute
13418      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13419      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13420      * saved in the current store (defaults to false)
13421      */
13422     doQuery : function(q, forceAll){
13423         
13424         if(q === undefined || q === null){
13425             q = '';
13426         }
13427         var qe = {
13428             query: q,
13429             forceAll: forceAll,
13430             combo: this,
13431             cancel:false
13432         };
13433         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13434             return false;
13435         }
13436         q = qe.query;
13437         
13438         forceAll = qe.forceAll;
13439         if(forceAll === true || (q.length >= this.minChars)){
13440             
13441             this.hasQuery = true;
13442             
13443             if(this.lastQuery != q || this.alwaysQuery){
13444                 this.lastQuery = q;
13445                 if(this.mode == 'local'){
13446                     this.selectedIndex = -1;
13447                     if(forceAll){
13448                         this.store.clearFilter();
13449                     }else{
13450                         
13451                         if(this.specialFilter){
13452                             this.fireEvent('specialfilter', this);
13453                             this.onLoad();
13454                             return;
13455                         }
13456                         
13457                         this.store.filter(this.displayField, q);
13458                     }
13459                     
13460                     this.store.fireEvent("datachanged", this.store);
13461                     
13462                     this.onLoad();
13463                     
13464                     
13465                 }else{
13466                     
13467                     this.store.baseParams[this.queryParam] = q;
13468                     
13469                     var options = {params : this.getParams(q)};
13470                     
13471                     if(this.loadNext){
13472                         options.add = true;
13473                         options.params.start = this.page * this.pageSize;
13474                     }
13475                     
13476                     this.store.load(options);
13477                     
13478                     /*
13479                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13480                      *  we should expand the list on onLoad
13481                      *  so command out it
13482                      */
13483 //                    this.expand();
13484                 }
13485             }else{
13486                 this.selectedIndex = -1;
13487                 this.onLoad();   
13488             }
13489         }
13490         
13491         this.loadNext = false;
13492     },
13493     
13494     // private
13495     getParams : function(q){
13496         var p = {};
13497         //p[this.queryParam] = q;
13498         
13499         if(this.pageSize){
13500             p.start = 0;
13501             p.limit = this.pageSize;
13502         }
13503         return p;
13504     },
13505
13506     /**
13507      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13508      */
13509     collapse : function(){
13510         if(!this.isExpanded()){
13511             return;
13512         }
13513         
13514         this.list.hide();
13515         
13516         if(this.tickable){
13517             this.hasFocus = false;
13518             this.okBtn.hide();
13519             this.cancelBtn.hide();
13520             this.trigger.show();
13521             
13522             if(this.editable){
13523                 this.tickableInputEl().dom.value = '';
13524                 this.tickableInputEl().blur();
13525             }
13526             
13527         }
13528         
13529         Roo.get(document).un('mousedown', this.collapseIf, this);
13530         Roo.get(document).un('mousewheel', this.collapseIf, this);
13531         if (!this.editable) {
13532             Roo.get(document).un('keydown', this.listKeyPress, this);
13533         }
13534         this.fireEvent('collapse', this);
13535         
13536         this.validate();
13537     },
13538
13539     // private
13540     collapseIf : function(e){
13541         var in_combo  = e.within(this.el);
13542         var in_list =  e.within(this.list);
13543         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13544         
13545         if (in_combo || in_list || is_list) {
13546             //e.stopPropagation();
13547             return;
13548         }
13549         
13550         if(this.tickable){
13551             this.onTickableFooterButtonClick(e, false, false);
13552         }
13553
13554         this.collapse();
13555         
13556     },
13557
13558     /**
13559      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13560      */
13561     expand : function(){
13562        
13563         if(this.isExpanded() || !this.hasFocus){
13564             return;
13565         }
13566         
13567         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13568         this.list.setWidth(lw);
13569         
13570         
13571          Roo.log('expand');
13572         
13573         this.list.show();
13574         
13575         this.restrictHeight();
13576         
13577         if(this.tickable){
13578             
13579             this.tickItems = Roo.apply([], this.item);
13580             
13581             this.okBtn.show();
13582             this.cancelBtn.show();
13583             this.trigger.hide();
13584             
13585             if(this.editable){
13586                 this.tickableInputEl().focus();
13587             }
13588             
13589         }
13590         
13591         Roo.get(document).on('mousedown', this.collapseIf, this);
13592         Roo.get(document).on('mousewheel', this.collapseIf, this);
13593         if (!this.editable) {
13594             Roo.get(document).on('keydown', this.listKeyPress, this);
13595         }
13596         
13597         this.fireEvent('expand', this);
13598     },
13599
13600     // private
13601     // Implements the default empty TriggerField.onTriggerClick function
13602     onTriggerClick : function(e)
13603     {
13604         Roo.log('trigger click');
13605         
13606         if(this.disabled || !this.triggerList){
13607             return;
13608         }
13609         
13610         this.page = 0;
13611         this.loadNext = false;
13612         
13613         if(this.isExpanded()){
13614             this.collapse();
13615             if (!this.blockFocus) {
13616                 this.inputEl().focus();
13617             }
13618             
13619         }else {
13620             this.hasFocus = true;
13621             if(this.triggerAction == 'all') {
13622                 this.doQuery(this.allQuery, true);
13623             } else {
13624                 this.doQuery(this.getRawValue());
13625             }
13626             if (!this.blockFocus) {
13627                 this.inputEl().focus();
13628             }
13629         }
13630     },
13631     
13632     onTickableTriggerClick : function(e)
13633     {
13634         if(this.disabled){
13635             return;
13636         }
13637         
13638         this.page = 0;
13639         this.loadNext = false;
13640         this.hasFocus = true;
13641         
13642         if(this.triggerAction == 'all') {
13643             this.doQuery(this.allQuery, true);
13644         } else {
13645             this.doQuery(this.getRawValue());
13646         }
13647     },
13648     
13649     onSearchFieldClick : function(e)
13650     {
13651         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13652             this.onTickableFooterButtonClick(e, false, false);
13653             return;
13654         }
13655         
13656         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13657             return;
13658         }
13659         
13660         this.page = 0;
13661         this.loadNext = false;
13662         this.hasFocus = true;
13663         
13664         if(this.triggerAction == 'all') {
13665             this.doQuery(this.allQuery, true);
13666         } else {
13667             this.doQuery(this.getRawValue());
13668         }
13669     },
13670     
13671     listKeyPress : function(e)
13672     {
13673         //Roo.log('listkeypress');
13674         // scroll to first matching element based on key pres..
13675         if (e.isSpecialKey()) {
13676             return false;
13677         }
13678         var k = String.fromCharCode(e.getKey()).toUpperCase();
13679         //Roo.log(k);
13680         var match  = false;
13681         var csel = this.view.getSelectedNodes();
13682         var cselitem = false;
13683         if (csel.length) {
13684             var ix = this.view.indexOf(csel[0]);
13685             cselitem  = this.store.getAt(ix);
13686             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13687                 cselitem = false;
13688             }
13689             
13690         }
13691         
13692         this.store.each(function(v) { 
13693             if (cselitem) {
13694                 // start at existing selection.
13695                 if (cselitem.id == v.id) {
13696                     cselitem = false;
13697                 }
13698                 return true;
13699             }
13700                 
13701             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13702                 match = this.store.indexOf(v);
13703                 return false;
13704             }
13705             return true;
13706         }, this);
13707         
13708         if (match === false) {
13709             return true; // no more action?
13710         }
13711         // scroll to?
13712         this.view.select(match);
13713         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13714         sn.scrollIntoView(sn.dom.parentNode, false);
13715     },
13716     
13717     onViewScroll : function(e, t){
13718         
13719         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){
13720             return;
13721         }
13722         
13723         this.hasQuery = true;
13724         
13725         this.loading = this.list.select('.loading', true).first();
13726         
13727         if(this.loading === null){
13728             this.list.createChild({
13729                 tag: 'div',
13730                 cls: 'loading roo-select2-more-results roo-select2-active',
13731                 html: 'Loading more results...'
13732             });
13733             
13734             this.loading = this.list.select('.loading', true).first();
13735             
13736             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13737             
13738             this.loading.hide();
13739         }
13740         
13741         this.loading.show();
13742         
13743         var _combo = this;
13744         
13745         this.page++;
13746         this.loadNext = true;
13747         
13748         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13749         
13750         return;
13751     },
13752     
13753     addItem : function(o)
13754     {   
13755         var dv = ''; // display value
13756         
13757         if (this.displayField) {
13758             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13759         } else {
13760             // this is an error condition!!!
13761             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13762         }
13763         
13764         if(!dv.length){
13765             return;
13766         }
13767         
13768         var choice = this.choices.createChild({
13769             tag: 'li',
13770             cls: 'roo-select2-search-choice',
13771             cn: [
13772                 {
13773                     tag: 'div',
13774                     html: dv
13775                 },
13776                 {
13777                     tag: 'a',
13778                     href: '#',
13779                     cls: 'roo-select2-search-choice-close',
13780                     tabindex: '-1'
13781                 }
13782             ]
13783             
13784         }, this.searchField);
13785         
13786         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13787         
13788         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13789         
13790         this.item.push(o);
13791         
13792         this.lastData = o;
13793         
13794         this.syncValue();
13795         
13796         this.inputEl().dom.value = '';
13797         
13798         this.validate();
13799     },
13800     
13801     onRemoveItem : function(e, _self, o)
13802     {
13803         e.preventDefault();
13804         
13805         this.lastItem = Roo.apply([], this.item);
13806         
13807         var index = this.item.indexOf(o.data) * 1;
13808         
13809         if( index < 0){
13810             Roo.log('not this item?!');
13811             return;
13812         }
13813         
13814         this.item.splice(index, 1);
13815         o.item.remove();
13816         
13817         this.syncValue();
13818         
13819         this.fireEvent('remove', this, e);
13820         
13821         this.validate();
13822         
13823     },
13824     
13825     syncValue : function()
13826     {
13827         if(!this.item.length){
13828             this.clearValue();
13829             return;
13830         }
13831             
13832         var value = [];
13833         var _this = this;
13834         Roo.each(this.item, function(i){
13835             if(_this.valueField){
13836                 value.push(i[_this.valueField]);
13837                 return;
13838             }
13839
13840             value.push(i);
13841         });
13842
13843         this.value = value.join(',');
13844
13845         if(this.hiddenField){
13846             this.hiddenField.dom.value = this.value;
13847         }
13848         
13849         this.store.fireEvent("datachanged", this.store);
13850         
13851         this.validate();
13852     },
13853     
13854     clearItem : function()
13855     {
13856         if(!this.multiple){
13857             return;
13858         }
13859         
13860         this.item = [];
13861         
13862         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13863            c.remove();
13864         });
13865         
13866         this.syncValue();
13867         
13868         this.validate();
13869         
13870         if(this.tickable && !Roo.isTouch){
13871             this.view.refresh();
13872         }
13873     },
13874     
13875     inputEl: function ()
13876     {
13877         if(Roo.isTouch && this.mobileTouchView){
13878             return this.el.select('input.form-control',true).first();
13879         }
13880         
13881         if(this.tickable){
13882             return this.searchField;
13883         }
13884         
13885         return this.el.select('input.form-control',true).first();
13886     },
13887     
13888     
13889     onTickableFooterButtonClick : function(e, btn, el)
13890     {
13891         e.preventDefault();
13892         
13893         this.lastItem = Roo.apply([], this.item);
13894         
13895         if(btn && btn.name == 'cancel'){
13896             this.tickItems = Roo.apply([], this.item);
13897             this.collapse();
13898             return;
13899         }
13900         
13901         this.clearItem();
13902         
13903         var _this = this;
13904         
13905         Roo.each(this.tickItems, function(o){
13906             _this.addItem(o);
13907         });
13908         
13909         this.collapse();
13910         
13911     },
13912     
13913     validate : function()
13914     {
13915         var v = this.getRawValue();
13916         
13917         if(this.multiple){
13918             v = this.getValue();
13919         }
13920         
13921         if(this.disabled || this.allowBlank || v.length){
13922             this.markValid();
13923             return true;
13924         }
13925         
13926         this.markInvalid();
13927         return false;
13928     },
13929     
13930     tickableInputEl : function()
13931     {
13932         if(!this.tickable || !this.editable){
13933             return this.inputEl();
13934         }
13935         
13936         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13937     },
13938     
13939     
13940     getAutoCreateTouchView : function()
13941     {
13942         var id = Roo.id();
13943         
13944         var cfg = {
13945             cls: 'form-group' //input-group
13946         };
13947         
13948         var input =  {
13949             tag: 'input',
13950             id : id,
13951             type : this.inputType,
13952             cls : 'form-control x-combo-noedit',
13953             autocomplete: 'new-password',
13954             placeholder : this.placeholder || '',
13955             readonly : true
13956         };
13957         
13958         if (this.name) {
13959             input.name = this.name;
13960         }
13961         
13962         if (this.size) {
13963             input.cls += ' input-' + this.size;
13964         }
13965         
13966         if (this.disabled) {
13967             input.disabled = true;
13968         }
13969         
13970         var inputblock = {
13971             cls : '',
13972             cn : [
13973                 input
13974             ]
13975         };
13976         
13977         if(this.before){
13978             inputblock.cls += ' input-group';
13979             
13980             inputblock.cn.unshift({
13981                 tag :'span',
13982                 cls : 'input-group-addon',
13983                 html : this.before
13984             });
13985         }
13986         
13987         if(this.removable && !this.multiple){
13988             inputblock.cls += ' roo-removable';
13989             
13990             inputblock.cn.push({
13991                 tag: 'button',
13992                 html : 'x',
13993                 cls : 'roo-combo-removable-btn close'
13994             });
13995         }
13996
13997         if(this.hasFeedback && !this.allowBlank){
13998             
13999             inputblock.cls += ' has-feedback';
14000             
14001             inputblock.cn.push({
14002                 tag: 'span',
14003                 cls: 'glyphicon form-control-feedback'
14004             });
14005             
14006         }
14007         
14008         if (this.after) {
14009             
14010             inputblock.cls += (this.before) ? '' : ' input-group';
14011             
14012             inputblock.cn.push({
14013                 tag :'span',
14014                 cls : 'input-group-addon',
14015                 html : this.after
14016             });
14017         }
14018
14019         var box = {
14020             tag: 'div',
14021             cn: [
14022                 {
14023                     tag: 'input',
14024                     type : 'hidden',
14025                     cls: 'form-hidden-field'
14026                 },
14027                 inputblock
14028             ]
14029             
14030         };
14031         
14032         if(this.multiple){
14033             box = {
14034                 tag: 'div',
14035                 cn: [
14036                     {
14037                         tag: 'input',
14038                         type : 'hidden',
14039                         cls: 'form-hidden-field'
14040                     },
14041                     {
14042                         tag: 'ul',
14043                         cls: 'roo-select2-choices',
14044                         cn:[
14045                             {
14046                                 tag: 'li',
14047                                 cls: 'roo-select2-search-field',
14048                                 cn: [
14049
14050                                     inputblock
14051                                 ]
14052                             }
14053                         ]
14054                     }
14055                 ]
14056             }
14057         };
14058         
14059         var combobox = {
14060             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14061             cn: [
14062                 box
14063             ]
14064         };
14065         
14066         if(!this.multiple && this.showToggleBtn){
14067             
14068             var caret = {
14069                         tag: 'span',
14070                         cls: 'caret'
14071             };
14072             
14073             if (this.caret != false) {
14074                 caret = {
14075                      tag: 'i',
14076                      cls: 'fa fa-' + this.caret
14077                 };
14078                 
14079             }
14080             
14081             combobox.cn.push({
14082                 tag :'span',
14083                 cls : 'input-group-addon btn dropdown-toggle',
14084                 cn : [
14085                     caret,
14086                     {
14087                         tag: 'span',
14088                         cls: 'combobox-clear',
14089                         cn  : [
14090                             {
14091                                 tag : 'i',
14092                                 cls: 'icon-remove'
14093                             }
14094                         ]
14095                     }
14096                 ]
14097
14098             })
14099         }
14100         
14101         if(this.multiple){
14102             combobox.cls += ' roo-select2-container-multi';
14103         }
14104         
14105         var align = this.labelAlign || this.parentLabelAlign();
14106         
14107         cfg.cn = combobox;
14108         
14109         if(this.fieldLabel.length && this.labelWidth){
14110             
14111             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14112             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14113             
14114             cfg.cn = [
14115                 {
14116                    tag : 'i',
14117                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14118                    tooltip : 'This field is required'
14119                 },
14120                 {
14121                     tag: 'label',
14122                     cls : 'control-label ' + lw,
14123                     html : this.fieldLabel
14124
14125                 },
14126                 {
14127                     cls : cw, 
14128                     cn: [
14129                         combobox
14130                     ]
14131                 }
14132             ];
14133             
14134             if(this.indicatorpos == 'right'){
14135                 cfg.cn = [
14136                     {
14137                         tag: 'label',
14138                         cls : 'control-label ' + lw,
14139                         html : this.fieldLabel
14140
14141                     },
14142                     {
14143                        tag : 'i',
14144                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14145                        tooltip : 'This field is required'
14146                     },
14147                     {
14148                         cls : cw, 
14149                         cn: [
14150                             combobox
14151                         ]
14152                     }
14153                 ];
14154             }
14155         }
14156         
14157         var settings = this;
14158         
14159         ['xs','sm','md','lg'].map(function(size){
14160             if (settings[size]) {
14161                 cfg.cls += ' col-' + size + '-' + settings[size];
14162             }
14163         });
14164         
14165         return cfg;
14166     },
14167     
14168     initTouchView : function()
14169     {
14170         this.renderTouchView();
14171         
14172         this.touchViewEl.on('scroll', function(){
14173             this.el.dom.scrollTop = 0;
14174         }, this);
14175         
14176         this.originalValue = this.getValue();
14177         
14178         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14179         
14180         this.inputEl().on("click", this.showTouchView, this);
14181         this.triggerEl.on("click", this.showTouchView, this);
14182         
14183         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14184         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14185         
14186         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14187         
14188         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14189         this.store.on('load', this.onTouchViewLoad, this);
14190         this.store.on('loadexception', this.onTouchViewLoadException, this);
14191         
14192         if(this.hiddenName){
14193             
14194             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14195             
14196             this.hiddenField.dom.value =
14197                 this.hiddenValue !== undefined ? this.hiddenValue :
14198                 this.value !== undefined ? this.value : '';
14199         
14200             this.el.dom.removeAttribute('name');
14201             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14202         }
14203         
14204         if(this.multiple){
14205             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14206             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14207         }
14208         
14209         if(this.removable && !this.multiple){
14210             var close = this.closeTriggerEl();
14211             if(close){
14212                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14213                 close.on('click', this.removeBtnClick, this, close);
14214             }
14215         }
14216         /*
14217          * fix the bug in Safari iOS8
14218          */
14219         this.inputEl().on("focus", function(e){
14220             document.activeElement.blur();
14221         }, this);
14222         
14223         return;
14224         
14225         
14226     },
14227     
14228     renderTouchView : function()
14229     {
14230         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14231         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14232         
14233         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14234         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14235         
14236         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14237         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14238         this.touchViewBodyEl.setStyle('overflow', 'auto');
14239         
14240         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14241         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14242         
14243         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14244         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14245         
14246     },
14247     
14248     showTouchView : function()
14249     {
14250         if(this.disabled){
14251             return;
14252         }
14253         
14254         this.touchViewHeaderEl.hide();
14255
14256         if(this.modalTitle.length){
14257             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14258             this.touchViewHeaderEl.show();
14259         }
14260
14261         this.touchViewEl.show();
14262
14263         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14264         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14265                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14266
14267         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14268
14269         if(this.modalTitle.length){
14270             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14271         }
14272         
14273         this.touchViewBodyEl.setHeight(bodyHeight);
14274
14275         if(this.animate){
14276             var _this = this;
14277             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14278         }else{
14279             this.touchViewEl.addClass('in');
14280         }
14281
14282         this.doTouchViewQuery();
14283         
14284     },
14285     
14286     hideTouchView : function()
14287     {
14288         this.touchViewEl.removeClass('in');
14289
14290         if(this.animate){
14291             var _this = this;
14292             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14293         }else{
14294             this.touchViewEl.setStyle('display', 'none');
14295         }
14296         
14297     },
14298     
14299     setTouchViewValue : function()
14300     {
14301         if(this.multiple){
14302             this.clearItem();
14303         
14304             var _this = this;
14305
14306             Roo.each(this.tickItems, function(o){
14307                 this.addItem(o);
14308             }, this);
14309         }
14310         
14311         this.hideTouchView();
14312     },
14313     
14314     doTouchViewQuery : function()
14315     {
14316         var qe = {
14317             query: '',
14318             forceAll: true,
14319             combo: this,
14320             cancel:false
14321         };
14322         
14323         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14324             return false;
14325         }
14326         
14327         if(!this.alwaysQuery || this.mode == 'local'){
14328             this.onTouchViewLoad();
14329             return;
14330         }
14331         
14332         this.store.load();
14333     },
14334     
14335     onTouchViewBeforeLoad : function(combo,opts)
14336     {
14337         return;
14338     },
14339
14340     // private
14341     onTouchViewLoad : function()
14342     {
14343         if(this.store.getCount() < 1){
14344             this.onTouchViewEmptyResults();
14345             return;
14346         }
14347         
14348         this.clearTouchView();
14349         
14350         var rawValue = this.getRawValue();
14351         
14352         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14353         
14354         this.tickItems = [];
14355         
14356         this.store.data.each(function(d, rowIndex){
14357             var row = this.touchViewListGroup.createChild(template);
14358             
14359             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14360                 row.addClass(d.data.cls);
14361             }
14362             
14363             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14364                 var cfg = {
14365                     data : d.data,
14366                     html : d.data[this.displayField]
14367                 };
14368                 
14369                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14370                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14371                 }
14372             }
14373             row.removeClass('selected');
14374             if(!this.multiple && this.valueField &&
14375                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14376             {
14377                 // radio buttons..
14378                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14379                 row.addClass('selected');
14380             }
14381             
14382             if(this.multiple && this.valueField &&
14383                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14384             {
14385                 
14386                 // checkboxes...
14387                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14388                 this.tickItems.push(d.data);
14389             }
14390             
14391             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14392             
14393         }, this);
14394         
14395         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14396         
14397         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14398
14399         if(this.modalTitle.length){
14400             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14401         }
14402
14403         var listHeight = this.touchViewListGroup.getHeight();
14404         
14405         var _this = this;
14406         
14407         if(firstChecked && listHeight > bodyHeight){
14408             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14409         }
14410         
14411     },
14412     
14413     onTouchViewLoadException : function()
14414     {
14415         this.hideTouchView();
14416     },
14417     
14418     onTouchViewEmptyResults : function()
14419     {
14420         this.clearTouchView();
14421         
14422         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14423         
14424         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14425         
14426     },
14427     
14428     clearTouchView : function()
14429     {
14430         this.touchViewListGroup.dom.innerHTML = '';
14431     },
14432     
14433     onTouchViewClick : function(e, el, o)
14434     {
14435         e.preventDefault();
14436         
14437         var row = o.row;
14438         var rowIndex = o.rowIndex;
14439         
14440         var r = this.store.getAt(rowIndex);
14441         
14442         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14443             
14444             if(!this.multiple){
14445                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14446                     c.dom.removeAttribute('checked');
14447                 }, this);
14448
14449                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14450
14451                 this.setFromData(r.data);
14452
14453                 var close = this.closeTriggerEl();
14454
14455                 if(close){
14456                     close.show();
14457                 }
14458
14459                 this.hideTouchView();
14460
14461                 this.fireEvent('select', this, r, rowIndex);
14462
14463                 return;
14464             }
14465
14466             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14467                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14468                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14469                 return;
14470             }
14471
14472             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14473             this.addItem(r.data);
14474             this.tickItems.push(r.data);
14475         }
14476     }
14477     
14478
14479     /** 
14480     * @cfg {Boolean} grow 
14481     * @hide 
14482     */
14483     /** 
14484     * @cfg {Number} growMin 
14485     * @hide 
14486     */
14487     /** 
14488     * @cfg {Number} growMax 
14489     * @hide 
14490     */
14491     /**
14492      * @hide
14493      * @method autoSize
14494      */
14495 });
14496
14497 Roo.apply(Roo.bootstrap.ComboBox,  {
14498     
14499     header : {
14500         tag: 'div',
14501         cls: 'modal-header',
14502         cn: [
14503             {
14504                 tag: 'h4',
14505                 cls: 'modal-title'
14506             }
14507         ]
14508     },
14509     
14510     body : {
14511         tag: 'div',
14512         cls: 'modal-body',
14513         cn: [
14514             {
14515                 tag: 'ul',
14516                 cls: 'list-group'
14517             }
14518         ]
14519     },
14520     
14521     listItemRadio : {
14522         tag: 'li',
14523         cls: 'list-group-item',
14524         cn: [
14525             {
14526                 tag: 'span',
14527                 cls: 'roo-combobox-list-group-item-value'
14528             },
14529             {
14530                 tag: 'div',
14531                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14532                 cn: [
14533                     {
14534                         tag: 'input',
14535                         type: 'radio'
14536                     },
14537                     {
14538                         tag: 'label'
14539                     }
14540                 ]
14541             }
14542         ]
14543     },
14544     
14545     listItemCheckbox : {
14546         tag: 'li',
14547         cls: 'list-group-item',
14548         cn: [
14549             {
14550                 tag: 'span',
14551                 cls: 'roo-combobox-list-group-item-value'
14552             },
14553             {
14554                 tag: 'div',
14555                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14556                 cn: [
14557                     {
14558                         tag: 'input',
14559                         type: 'checkbox'
14560                     },
14561                     {
14562                         tag: 'label'
14563                     }
14564                 ]
14565             }
14566         ]
14567     },
14568     
14569     emptyResult : {
14570         tag: 'div',
14571         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14572     },
14573     
14574     footer : {
14575         tag: 'div',
14576         cls: 'modal-footer',
14577         cn: [
14578             {
14579                 tag: 'div',
14580                 cls: 'row',
14581                 cn: [
14582                     {
14583                         tag: 'div',
14584                         cls: 'col-xs-6 text-left',
14585                         cn: {
14586                             tag: 'button',
14587                             cls: 'btn btn-danger roo-touch-view-cancel',
14588                             html: 'Cancel'
14589                         }
14590                     },
14591                     {
14592                         tag: 'div',
14593                         cls: 'col-xs-6 text-right',
14594                         cn: {
14595                             tag: 'button',
14596                             cls: 'btn btn-success roo-touch-view-ok',
14597                             html: 'OK'
14598                         }
14599                     }
14600                 ]
14601             }
14602         ]
14603         
14604     }
14605 });
14606
14607 Roo.apply(Roo.bootstrap.ComboBox,  {
14608     
14609     touchViewTemplate : {
14610         tag: 'div',
14611         cls: 'modal fade roo-combobox-touch-view',
14612         cn: [
14613             {
14614                 tag: 'div',
14615                 cls: 'modal-dialog',
14616                 style : 'position:fixed', // we have to fix position....
14617                 cn: [
14618                     {
14619                         tag: 'div',
14620                         cls: 'modal-content',
14621                         cn: [
14622                             Roo.bootstrap.ComboBox.header,
14623                             Roo.bootstrap.ComboBox.body,
14624                             Roo.bootstrap.ComboBox.footer
14625                         ]
14626                     }
14627                 ]
14628             }
14629         ]
14630     }
14631 });/*
14632  * Based on:
14633  * Ext JS Library 1.1.1
14634  * Copyright(c) 2006-2007, Ext JS, LLC.
14635  *
14636  * Originally Released Under LGPL - original licence link has changed is not relivant.
14637  *
14638  * Fork - LGPL
14639  * <script type="text/javascript">
14640  */
14641
14642 /**
14643  * @class Roo.View
14644  * @extends Roo.util.Observable
14645  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14646  * This class also supports single and multi selection modes. <br>
14647  * Create a data model bound view:
14648  <pre><code>
14649  var store = new Roo.data.Store(...);
14650
14651  var view = new Roo.View({
14652     el : "my-element",
14653     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14654  
14655     singleSelect: true,
14656     selectedClass: "ydataview-selected",
14657     store: store
14658  });
14659
14660  // listen for node click?
14661  view.on("click", function(vw, index, node, e){
14662  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14663  });
14664
14665  // load XML data
14666  dataModel.load("foobar.xml");
14667  </code></pre>
14668  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14669  * <br><br>
14670  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14671  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14672  * 
14673  * Note: old style constructor is still suported (container, template, config)
14674  * 
14675  * @constructor
14676  * Create a new View
14677  * @param {Object} config The config object
14678  * 
14679  */
14680 Roo.View = function(config, depreciated_tpl, depreciated_config){
14681     
14682     this.parent = false;
14683     
14684     if (typeof(depreciated_tpl) == 'undefined') {
14685         // new way.. - universal constructor.
14686         Roo.apply(this, config);
14687         this.el  = Roo.get(this.el);
14688     } else {
14689         // old format..
14690         this.el  = Roo.get(config);
14691         this.tpl = depreciated_tpl;
14692         Roo.apply(this, depreciated_config);
14693     }
14694     this.wrapEl  = this.el.wrap().wrap();
14695     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14696     
14697     
14698     if(typeof(this.tpl) == "string"){
14699         this.tpl = new Roo.Template(this.tpl);
14700     } else {
14701         // support xtype ctors..
14702         this.tpl = new Roo.factory(this.tpl, Roo);
14703     }
14704     
14705     
14706     this.tpl.compile();
14707     
14708     /** @private */
14709     this.addEvents({
14710         /**
14711          * @event beforeclick
14712          * Fires before a click is processed. Returns false to cancel the default action.
14713          * @param {Roo.View} this
14714          * @param {Number} index The index of the target node
14715          * @param {HTMLElement} node The target node
14716          * @param {Roo.EventObject} e The raw event object
14717          */
14718             "beforeclick" : true,
14719         /**
14720          * @event click
14721          * Fires when a template node is clicked.
14722          * @param {Roo.View} this
14723          * @param {Number} index The index of the target node
14724          * @param {HTMLElement} node The target node
14725          * @param {Roo.EventObject} e The raw event object
14726          */
14727             "click" : true,
14728         /**
14729          * @event dblclick
14730          * Fires when a template node is double clicked.
14731          * @param {Roo.View} this
14732          * @param {Number} index The index of the target node
14733          * @param {HTMLElement} node The target node
14734          * @param {Roo.EventObject} e The raw event object
14735          */
14736             "dblclick" : true,
14737         /**
14738          * @event contextmenu
14739          * Fires when a template node is right clicked.
14740          * @param {Roo.View} this
14741          * @param {Number} index The index of the target node
14742          * @param {HTMLElement} node The target node
14743          * @param {Roo.EventObject} e The raw event object
14744          */
14745             "contextmenu" : true,
14746         /**
14747          * @event selectionchange
14748          * Fires when the selected nodes change.
14749          * @param {Roo.View} this
14750          * @param {Array} selections Array of the selected nodes
14751          */
14752             "selectionchange" : true,
14753     
14754         /**
14755          * @event beforeselect
14756          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14757          * @param {Roo.View} this
14758          * @param {HTMLElement} node The node to be selected
14759          * @param {Array} selections Array of currently selected nodes
14760          */
14761             "beforeselect" : true,
14762         /**
14763          * @event preparedata
14764          * Fires on every row to render, to allow you to change the data.
14765          * @param {Roo.View} this
14766          * @param {Object} data to be rendered (change this)
14767          */
14768           "preparedata" : true
14769           
14770           
14771         });
14772
14773
14774
14775     this.el.on({
14776         "click": this.onClick,
14777         "dblclick": this.onDblClick,
14778         "contextmenu": this.onContextMenu,
14779         scope:this
14780     });
14781
14782     this.selections = [];
14783     this.nodes = [];
14784     this.cmp = new Roo.CompositeElementLite([]);
14785     if(this.store){
14786         this.store = Roo.factory(this.store, Roo.data);
14787         this.setStore(this.store, true);
14788     }
14789     
14790     if ( this.footer && this.footer.xtype) {
14791            
14792          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14793         
14794         this.footer.dataSource = this.store;
14795         this.footer.container = fctr;
14796         this.footer = Roo.factory(this.footer, Roo);
14797         fctr.insertFirst(this.el);
14798         
14799         // this is a bit insane - as the paging toolbar seems to detach the el..
14800 //        dom.parentNode.parentNode.parentNode
14801          // they get detached?
14802     }
14803     
14804     
14805     Roo.View.superclass.constructor.call(this);
14806     
14807     
14808 };
14809
14810 Roo.extend(Roo.View, Roo.util.Observable, {
14811     
14812      /**
14813      * @cfg {Roo.data.Store} store Data store to load data from.
14814      */
14815     store : false,
14816     
14817     /**
14818      * @cfg {String|Roo.Element} el The container element.
14819      */
14820     el : '',
14821     
14822     /**
14823      * @cfg {String|Roo.Template} tpl The template used by this View 
14824      */
14825     tpl : false,
14826     /**
14827      * @cfg {String} dataName the named area of the template to use as the data area
14828      *                          Works with domtemplates roo-name="name"
14829      */
14830     dataName: false,
14831     /**
14832      * @cfg {String} selectedClass The css class to add to selected nodes
14833      */
14834     selectedClass : "x-view-selected",
14835      /**
14836      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14837      */
14838     emptyText : "",
14839     
14840     /**
14841      * @cfg {String} text to display on mask (default Loading)
14842      */
14843     mask : false,
14844     /**
14845      * @cfg {Boolean} multiSelect Allow multiple selection
14846      */
14847     multiSelect : false,
14848     /**
14849      * @cfg {Boolean} singleSelect Allow single selection
14850      */
14851     singleSelect:  false,
14852     
14853     /**
14854      * @cfg {Boolean} toggleSelect - selecting 
14855      */
14856     toggleSelect : false,
14857     
14858     /**
14859      * @cfg {Boolean} tickable - selecting 
14860      */
14861     tickable : false,
14862     
14863     /**
14864      * Returns the element this view is bound to.
14865      * @return {Roo.Element}
14866      */
14867     getEl : function(){
14868         return this.wrapEl;
14869     },
14870     
14871     
14872
14873     /**
14874      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14875      */
14876     refresh : function(){
14877         //Roo.log('refresh');
14878         var t = this.tpl;
14879         
14880         // if we are using something like 'domtemplate', then
14881         // the what gets used is:
14882         // t.applySubtemplate(NAME, data, wrapping data..)
14883         // the outer template then get' applied with
14884         //     the store 'extra data'
14885         // and the body get's added to the
14886         //      roo-name="data" node?
14887         //      <span class='roo-tpl-{name}'></span> ?????
14888         
14889         
14890         
14891         this.clearSelections();
14892         this.el.update("");
14893         var html = [];
14894         var records = this.store.getRange();
14895         if(records.length < 1) {
14896             
14897             // is this valid??  = should it render a template??
14898             
14899             this.el.update(this.emptyText);
14900             return;
14901         }
14902         var el = this.el;
14903         if (this.dataName) {
14904             this.el.update(t.apply(this.store.meta)); //????
14905             el = this.el.child('.roo-tpl-' + this.dataName);
14906         }
14907         
14908         for(var i = 0, len = records.length; i < len; i++){
14909             var data = this.prepareData(records[i].data, i, records[i]);
14910             this.fireEvent("preparedata", this, data, i, records[i]);
14911             
14912             var d = Roo.apply({}, data);
14913             
14914             if(this.tickable){
14915                 Roo.apply(d, {'roo-id' : Roo.id()});
14916                 
14917                 var _this = this;
14918             
14919                 Roo.each(this.parent.item, function(item){
14920                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14921                         return;
14922                     }
14923                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14924                 });
14925             }
14926             
14927             html[html.length] = Roo.util.Format.trim(
14928                 this.dataName ?
14929                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14930                     t.apply(d)
14931             );
14932         }
14933         
14934         
14935         
14936         el.update(html.join(""));
14937         this.nodes = el.dom.childNodes;
14938         this.updateIndexes(0);
14939     },
14940     
14941
14942     /**
14943      * Function to override to reformat the data that is sent to
14944      * the template for each node.
14945      * DEPRICATED - use the preparedata event handler.
14946      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14947      * a JSON object for an UpdateManager bound view).
14948      */
14949     prepareData : function(data, index, record)
14950     {
14951         this.fireEvent("preparedata", this, data, index, record);
14952         return data;
14953     },
14954
14955     onUpdate : function(ds, record){
14956         // Roo.log('on update');   
14957         this.clearSelections();
14958         var index = this.store.indexOf(record);
14959         var n = this.nodes[index];
14960         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14961         n.parentNode.removeChild(n);
14962         this.updateIndexes(index, index);
14963     },
14964
14965     
14966     
14967 // --------- FIXME     
14968     onAdd : function(ds, records, index)
14969     {
14970         //Roo.log(['on Add', ds, records, index] );        
14971         this.clearSelections();
14972         if(this.nodes.length == 0){
14973             this.refresh();
14974             return;
14975         }
14976         var n = this.nodes[index];
14977         for(var i = 0, len = records.length; i < len; i++){
14978             var d = this.prepareData(records[i].data, i, records[i]);
14979             if(n){
14980                 this.tpl.insertBefore(n, d);
14981             }else{
14982                 
14983                 this.tpl.append(this.el, d);
14984             }
14985         }
14986         this.updateIndexes(index);
14987     },
14988
14989     onRemove : function(ds, record, index){
14990        // Roo.log('onRemove');
14991         this.clearSelections();
14992         var el = this.dataName  ?
14993             this.el.child('.roo-tpl-' + this.dataName) :
14994             this.el; 
14995         
14996         el.dom.removeChild(this.nodes[index]);
14997         this.updateIndexes(index);
14998     },
14999
15000     /**
15001      * Refresh an individual node.
15002      * @param {Number} index
15003      */
15004     refreshNode : function(index){
15005         this.onUpdate(this.store, this.store.getAt(index));
15006     },
15007
15008     updateIndexes : function(startIndex, endIndex){
15009         var ns = this.nodes;
15010         startIndex = startIndex || 0;
15011         endIndex = endIndex || ns.length - 1;
15012         for(var i = startIndex; i <= endIndex; i++){
15013             ns[i].nodeIndex = i;
15014         }
15015     },
15016
15017     /**
15018      * Changes the data store this view uses and refresh the view.
15019      * @param {Store} store
15020      */
15021     setStore : function(store, initial){
15022         if(!initial && this.store){
15023             this.store.un("datachanged", this.refresh);
15024             this.store.un("add", this.onAdd);
15025             this.store.un("remove", this.onRemove);
15026             this.store.un("update", this.onUpdate);
15027             this.store.un("clear", this.refresh);
15028             this.store.un("beforeload", this.onBeforeLoad);
15029             this.store.un("load", this.onLoad);
15030             this.store.un("loadexception", this.onLoad);
15031         }
15032         if(store){
15033           
15034             store.on("datachanged", this.refresh, this);
15035             store.on("add", this.onAdd, this);
15036             store.on("remove", this.onRemove, this);
15037             store.on("update", this.onUpdate, this);
15038             store.on("clear", this.refresh, this);
15039             store.on("beforeload", this.onBeforeLoad, this);
15040             store.on("load", this.onLoad, this);
15041             store.on("loadexception", this.onLoad, this);
15042         }
15043         
15044         if(store){
15045             this.refresh();
15046         }
15047     },
15048     /**
15049      * onbeforeLoad - masks the loading area.
15050      *
15051      */
15052     onBeforeLoad : function(store,opts)
15053     {
15054          //Roo.log('onBeforeLoad');   
15055         if (!opts.add) {
15056             this.el.update("");
15057         }
15058         this.el.mask(this.mask ? this.mask : "Loading" ); 
15059     },
15060     onLoad : function ()
15061     {
15062         this.el.unmask();
15063     },
15064     
15065
15066     /**
15067      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15068      * @param {HTMLElement} node
15069      * @return {HTMLElement} The template node
15070      */
15071     findItemFromChild : function(node){
15072         var el = this.dataName  ?
15073             this.el.child('.roo-tpl-' + this.dataName,true) :
15074             this.el.dom; 
15075         
15076         if(!node || node.parentNode == el){
15077                     return node;
15078             }
15079             var p = node.parentNode;
15080             while(p && p != el){
15081             if(p.parentNode == el){
15082                 return p;
15083             }
15084             p = p.parentNode;
15085         }
15086             return null;
15087     },
15088
15089     /** @ignore */
15090     onClick : function(e){
15091         var item = this.findItemFromChild(e.getTarget());
15092         if(item){
15093             var index = this.indexOf(item);
15094             if(this.onItemClick(item, index, e) !== false){
15095                 this.fireEvent("click", this, index, item, e);
15096             }
15097         }else{
15098             this.clearSelections();
15099         }
15100     },
15101
15102     /** @ignore */
15103     onContextMenu : function(e){
15104         var item = this.findItemFromChild(e.getTarget());
15105         if(item){
15106             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15107         }
15108     },
15109
15110     /** @ignore */
15111     onDblClick : function(e){
15112         var item = this.findItemFromChild(e.getTarget());
15113         if(item){
15114             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15115         }
15116     },
15117
15118     onItemClick : function(item, index, e)
15119     {
15120         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15121             return false;
15122         }
15123         if (this.toggleSelect) {
15124             var m = this.isSelected(item) ? 'unselect' : 'select';
15125             //Roo.log(m);
15126             var _t = this;
15127             _t[m](item, true, false);
15128             return true;
15129         }
15130         if(this.multiSelect || this.singleSelect){
15131             if(this.multiSelect && e.shiftKey && this.lastSelection){
15132                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15133             }else{
15134                 this.select(item, this.multiSelect && e.ctrlKey);
15135                 this.lastSelection = item;
15136             }
15137             
15138             if(!this.tickable){
15139                 e.preventDefault();
15140             }
15141             
15142         }
15143         return true;
15144     },
15145
15146     /**
15147      * Get the number of selected nodes.
15148      * @return {Number}
15149      */
15150     getSelectionCount : function(){
15151         return this.selections.length;
15152     },
15153
15154     /**
15155      * Get the currently selected nodes.
15156      * @return {Array} An array of HTMLElements
15157      */
15158     getSelectedNodes : function(){
15159         return this.selections;
15160     },
15161
15162     /**
15163      * Get the indexes of the selected nodes.
15164      * @return {Array}
15165      */
15166     getSelectedIndexes : function(){
15167         var indexes = [], s = this.selections;
15168         for(var i = 0, len = s.length; i < len; i++){
15169             indexes.push(s[i].nodeIndex);
15170         }
15171         return indexes;
15172     },
15173
15174     /**
15175      * Clear all selections
15176      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15177      */
15178     clearSelections : function(suppressEvent){
15179         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15180             this.cmp.elements = this.selections;
15181             this.cmp.removeClass(this.selectedClass);
15182             this.selections = [];
15183             if(!suppressEvent){
15184                 this.fireEvent("selectionchange", this, this.selections);
15185             }
15186         }
15187     },
15188
15189     /**
15190      * Returns true if the passed node is selected
15191      * @param {HTMLElement/Number} node The node or node index
15192      * @return {Boolean}
15193      */
15194     isSelected : function(node){
15195         var s = this.selections;
15196         if(s.length < 1){
15197             return false;
15198         }
15199         node = this.getNode(node);
15200         return s.indexOf(node) !== -1;
15201     },
15202
15203     /**
15204      * Selects nodes.
15205      * @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
15206      * @param {Boolean} keepExisting (optional) true to keep existing selections
15207      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15208      */
15209     select : function(nodeInfo, keepExisting, suppressEvent){
15210         if(nodeInfo instanceof Array){
15211             if(!keepExisting){
15212                 this.clearSelections(true);
15213             }
15214             for(var i = 0, len = nodeInfo.length; i < len; i++){
15215                 this.select(nodeInfo[i], true, true);
15216             }
15217             return;
15218         } 
15219         var node = this.getNode(nodeInfo);
15220         if(!node || this.isSelected(node)){
15221             return; // already selected.
15222         }
15223         if(!keepExisting){
15224             this.clearSelections(true);
15225         }
15226         
15227         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15228             Roo.fly(node).addClass(this.selectedClass);
15229             this.selections.push(node);
15230             if(!suppressEvent){
15231                 this.fireEvent("selectionchange", this, this.selections);
15232             }
15233         }
15234         
15235         
15236     },
15237       /**
15238      * Unselects nodes.
15239      * @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
15240      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15241      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15242      */
15243     unselect : function(nodeInfo, keepExisting, suppressEvent)
15244     {
15245         if(nodeInfo instanceof Array){
15246             Roo.each(this.selections, function(s) {
15247                 this.unselect(s, nodeInfo);
15248             }, this);
15249             return;
15250         }
15251         var node = this.getNode(nodeInfo);
15252         if(!node || !this.isSelected(node)){
15253             //Roo.log("not selected");
15254             return; // not selected.
15255         }
15256         // fireevent???
15257         var ns = [];
15258         Roo.each(this.selections, function(s) {
15259             if (s == node ) {
15260                 Roo.fly(node).removeClass(this.selectedClass);
15261
15262                 return;
15263             }
15264             ns.push(s);
15265         },this);
15266         
15267         this.selections= ns;
15268         this.fireEvent("selectionchange", this, this.selections);
15269     },
15270
15271     /**
15272      * Gets a template node.
15273      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15274      * @return {HTMLElement} The node or null if it wasn't found
15275      */
15276     getNode : function(nodeInfo){
15277         if(typeof nodeInfo == "string"){
15278             return document.getElementById(nodeInfo);
15279         }else if(typeof nodeInfo == "number"){
15280             return this.nodes[nodeInfo];
15281         }
15282         return nodeInfo;
15283     },
15284
15285     /**
15286      * Gets a range template nodes.
15287      * @param {Number} startIndex
15288      * @param {Number} endIndex
15289      * @return {Array} An array of nodes
15290      */
15291     getNodes : function(start, end){
15292         var ns = this.nodes;
15293         start = start || 0;
15294         end = typeof end == "undefined" ? ns.length - 1 : end;
15295         var nodes = [];
15296         if(start <= end){
15297             for(var i = start; i <= end; i++){
15298                 nodes.push(ns[i]);
15299             }
15300         } else{
15301             for(var i = start; i >= end; i--){
15302                 nodes.push(ns[i]);
15303             }
15304         }
15305         return nodes;
15306     },
15307
15308     /**
15309      * Finds the index of the passed node
15310      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15311      * @return {Number} The index of the node or -1
15312      */
15313     indexOf : function(node){
15314         node = this.getNode(node);
15315         if(typeof node.nodeIndex == "number"){
15316             return node.nodeIndex;
15317         }
15318         var ns = this.nodes;
15319         for(var i = 0, len = ns.length; i < len; i++){
15320             if(ns[i] == node){
15321                 return i;
15322             }
15323         }
15324         return -1;
15325     }
15326 });
15327 /*
15328  * - LGPL
15329  *
15330  * based on jquery fullcalendar
15331  * 
15332  */
15333
15334 Roo.bootstrap = Roo.bootstrap || {};
15335 /**
15336  * @class Roo.bootstrap.Calendar
15337  * @extends Roo.bootstrap.Component
15338  * Bootstrap Calendar class
15339  * @cfg {Boolean} loadMask (true|false) default false
15340  * @cfg {Object} header generate the user specific header of the calendar, default false
15341
15342  * @constructor
15343  * Create a new Container
15344  * @param {Object} config The config object
15345  */
15346
15347
15348
15349 Roo.bootstrap.Calendar = function(config){
15350     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15351      this.addEvents({
15352         /**
15353              * @event select
15354              * Fires when a date is selected
15355              * @param {DatePicker} this
15356              * @param {Date} date The selected date
15357              */
15358         'select': true,
15359         /**
15360              * @event monthchange
15361              * Fires when the displayed month changes 
15362              * @param {DatePicker} this
15363              * @param {Date} date The selected month
15364              */
15365         'monthchange': true,
15366         /**
15367              * @event evententer
15368              * Fires when mouse over an event
15369              * @param {Calendar} this
15370              * @param {event} Event
15371              */
15372         'evententer': true,
15373         /**
15374              * @event eventleave
15375              * Fires when the mouse leaves an
15376              * @param {Calendar} this
15377              * @param {event}
15378              */
15379         'eventleave': true,
15380         /**
15381              * @event eventclick
15382              * Fires when the mouse click an
15383              * @param {Calendar} this
15384              * @param {event}
15385              */
15386         'eventclick': true
15387         
15388     });
15389
15390 };
15391
15392 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15393     
15394      /**
15395      * @cfg {Number} startDay
15396      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15397      */
15398     startDay : 0,
15399     
15400     loadMask : false,
15401     
15402     header : false,
15403       
15404     getAutoCreate : function(){
15405         
15406         
15407         var fc_button = function(name, corner, style, content ) {
15408             return Roo.apply({},{
15409                 tag : 'span',
15410                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15411                          (corner.length ?
15412                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15413                             ''
15414                         ),
15415                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15416                 unselectable: 'on'
15417             });
15418         };
15419         
15420         var header = {};
15421         
15422         if(!this.header){
15423             header = {
15424                 tag : 'table',
15425                 cls : 'fc-header',
15426                 style : 'width:100%',
15427                 cn : [
15428                     {
15429                         tag: 'tr',
15430                         cn : [
15431                             {
15432                                 tag : 'td',
15433                                 cls : 'fc-header-left',
15434                                 cn : [
15435                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15436                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15437                                     { tag: 'span', cls: 'fc-header-space' },
15438                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15439
15440
15441                                 ]
15442                             },
15443
15444                             {
15445                                 tag : 'td',
15446                                 cls : 'fc-header-center',
15447                                 cn : [
15448                                     {
15449                                         tag: 'span',
15450                                         cls: 'fc-header-title',
15451                                         cn : {
15452                                             tag: 'H2',
15453                                             html : 'month / year'
15454                                         }
15455                                     }
15456
15457                                 ]
15458                             },
15459                             {
15460                                 tag : 'td',
15461                                 cls : 'fc-header-right',
15462                                 cn : [
15463                               /*      fc_button('month', 'left', '', 'month' ),
15464                                     fc_button('week', '', '', 'week' ),
15465                                     fc_button('day', 'right', '', 'day' )
15466                                 */    
15467
15468                                 ]
15469                             }
15470
15471                         ]
15472                     }
15473                 ]
15474             };
15475         }
15476         
15477         header = this.header;
15478         
15479        
15480         var cal_heads = function() {
15481             var ret = [];
15482             // fixme - handle this.
15483             
15484             for (var i =0; i < Date.dayNames.length; i++) {
15485                 var d = Date.dayNames[i];
15486                 ret.push({
15487                     tag: 'th',
15488                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15489                     html : d.substring(0,3)
15490                 });
15491                 
15492             }
15493             ret[0].cls += ' fc-first';
15494             ret[6].cls += ' fc-last';
15495             return ret;
15496         };
15497         var cal_cell = function(n) {
15498             return  {
15499                 tag: 'td',
15500                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15501                 cn : [
15502                     {
15503                         cn : [
15504                             {
15505                                 cls: 'fc-day-number',
15506                                 html: 'D'
15507                             },
15508                             {
15509                                 cls: 'fc-day-content',
15510                              
15511                                 cn : [
15512                                      {
15513                                         style: 'position: relative;' // height: 17px;
15514                                     }
15515                                 ]
15516                             }
15517                             
15518                             
15519                         ]
15520                     }
15521                 ]
15522                 
15523             }
15524         };
15525         var cal_rows = function() {
15526             
15527             var ret = [];
15528             for (var r = 0; r < 6; r++) {
15529                 var row= {
15530                     tag : 'tr',
15531                     cls : 'fc-week',
15532                     cn : []
15533                 };
15534                 
15535                 for (var i =0; i < Date.dayNames.length; i++) {
15536                     var d = Date.dayNames[i];
15537                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15538
15539                 }
15540                 row.cn[0].cls+=' fc-first';
15541                 row.cn[0].cn[0].style = 'min-height:90px';
15542                 row.cn[6].cls+=' fc-last';
15543                 ret.push(row);
15544                 
15545             }
15546             ret[0].cls += ' fc-first';
15547             ret[4].cls += ' fc-prev-last';
15548             ret[5].cls += ' fc-last';
15549             return ret;
15550             
15551         };
15552         
15553         var cal_table = {
15554             tag: 'table',
15555             cls: 'fc-border-separate',
15556             style : 'width:100%',
15557             cellspacing  : 0,
15558             cn : [
15559                 { 
15560                     tag: 'thead',
15561                     cn : [
15562                         { 
15563                             tag: 'tr',
15564                             cls : 'fc-first fc-last',
15565                             cn : cal_heads()
15566                         }
15567                     ]
15568                 },
15569                 { 
15570                     tag: 'tbody',
15571                     cn : cal_rows()
15572                 }
15573                   
15574             ]
15575         };
15576          
15577          var cfg = {
15578             cls : 'fc fc-ltr',
15579             cn : [
15580                 header,
15581                 {
15582                     cls : 'fc-content',
15583                     style : "position: relative;",
15584                     cn : [
15585                         {
15586                             cls : 'fc-view fc-view-month fc-grid',
15587                             style : 'position: relative',
15588                             unselectable : 'on',
15589                             cn : [
15590                                 {
15591                                     cls : 'fc-event-container',
15592                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15593                                 },
15594                                 cal_table
15595                             ]
15596                         }
15597                     ]
15598     
15599                 }
15600            ] 
15601             
15602         };
15603         
15604          
15605         
15606         return cfg;
15607     },
15608     
15609     
15610     initEvents : function()
15611     {
15612         if(!this.store){
15613             throw "can not find store for calendar";
15614         }
15615         
15616         var mark = {
15617             tag: "div",
15618             cls:"x-dlg-mask",
15619             style: "text-align:center",
15620             cn: [
15621                 {
15622                     tag: "div",
15623                     style: "background-color:white;width:50%;margin:250 auto",
15624                     cn: [
15625                         {
15626                             tag: "img",
15627                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15628                         },
15629                         {
15630                             tag: "span",
15631                             html: "Loading"
15632                         }
15633                         
15634                     ]
15635                 }
15636             ]
15637         };
15638         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15639         
15640         var size = this.el.select('.fc-content', true).first().getSize();
15641         this.maskEl.setSize(size.width, size.height);
15642         this.maskEl.enableDisplayMode("block");
15643         if(!this.loadMask){
15644             this.maskEl.hide();
15645         }
15646         
15647         this.store = Roo.factory(this.store, Roo.data);
15648         this.store.on('load', this.onLoad, this);
15649         this.store.on('beforeload', this.onBeforeLoad, this);
15650         
15651         this.resize();
15652         
15653         this.cells = this.el.select('.fc-day',true);
15654         //Roo.log(this.cells);
15655         this.textNodes = this.el.query('.fc-day-number');
15656         this.cells.addClassOnOver('fc-state-hover');
15657         
15658         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15659         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15660         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15661         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15662         
15663         this.on('monthchange', this.onMonthChange, this);
15664         
15665         this.update(new Date().clearTime());
15666     },
15667     
15668     resize : function() {
15669         var sz  = this.el.getSize();
15670         
15671         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15672         this.el.select('.fc-day-content div',true).setHeight(34);
15673     },
15674     
15675     
15676     // private
15677     showPrevMonth : function(e){
15678         this.update(this.activeDate.add("mo", -1));
15679     },
15680     showToday : function(e){
15681         this.update(new Date().clearTime());
15682     },
15683     // private
15684     showNextMonth : function(e){
15685         this.update(this.activeDate.add("mo", 1));
15686     },
15687
15688     // private
15689     showPrevYear : function(){
15690         this.update(this.activeDate.add("y", -1));
15691     },
15692
15693     // private
15694     showNextYear : function(){
15695         this.update(this.activeDate.add("y", 1));
15696     },
15697
15698     
15699    // private
15700     update : function(date)
15701     {
15702         var vd = this.activeDate;
15703         this.activeDate = date;
15704 //        if(vd && this.el){
15705 //            var t = date.getTime();
15706 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15707 //                Roo.log('using add remove');
15708 //                
15709 //                this.fireEvent('monthchange', this, date);
15710 //                
15711 //                this.cells.removeClass("fc-state-highlight");
15712 //                this.cells.each(function(c){
15713 //                   if(c.dateValue == t){
15714 //                       c.addClass("fc-state-highlight");
15715 //                       setTimeout(function(){
15716 //                            try{c.dom.firstChild.focus();}catch(e){}
15717 //                       }, 50);
15718 //                       return false;
15719 //                   }
15720 //                   return true;
15721 //                });
15722 //                return;
15723 //            }
15724 //        }
15725         
15726         var days = date.getDaysInMonth();
15727         
15728         var firstOfMonth = date.getFirstDateOfMonth();
15729         var startingPos = firstOfMonth.getDay()-this.startDay;
15730         
15731         if(startingPos < this.startDay){
15732             startingPos += 7;
15733         }
15734         
15735         var pm = date.add(Date.MONTH, -1);
15736         var prevStart = pm.getDaysInMonth()-startingPos;
15737 //        
15738         this.cells = this.el.select('.fc-day',true);
15739         this.textNodes = this.el.query('.fc-day-number');
15740         this.cells.addClassOnOver('fc-state-hover');
15741         
15742         var cells = this.cells.elements;
15743         var textEls = this.textNodes;
15744         
15745         Roo.each(cells, function(cell){
15746             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15747         });
15748         
15749         days += startingPos;
15750
15751         // convert everything to numbers so it's fast
15752         var day = 86400000;
15753         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15754         //Roo.log(d);
15755         //Roo.log(pm);
15756         //Roo.log(prevStart);
15757         
15758         var today = new Date().clearTime().getTime();
15759         var sel = date.clearTime().getTime();
15760         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15761         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15762         var ddMatch = this.disabledDatesRE;
15763         var ddText = this.disabledDatesText;
15764         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15765         var ddaysText = this.disabledDaysText;
15766         var format = this.format;
15767         
15768         var setCellClass = function(cal, cell){
15769             cell.row = 0;
15770             cell.events = [];
15771             cell.more = [];
15772             //Roo.log('set Cell Class');
15773             cell.title = "";
15774             var t = d.getTime();
15775             
15776             //Roo.log(d);
15777             
15778             cell.dateValue = t;
15779             if(t == today){
15780                 cell.className += " fc-today";
15781                 cell.className += " fc-state-highlight";
15782                 cell.title = cal.todayText;
15783             }
15784             if(t == sel){
15785                 // disable highlight in other month..
15786                 //cell.className += " fc-state-highlight";
15787                 
15788             }
15789             // disabling
15790             if(t < min) {
15791                 cell.className = " fc-state-disabled";
15792                 cell.title = cal.minText;
15793                 return;
15794             }
15795             if(t > max) {
15796                 cell.className = " fc-state-disabled";
15797                 cell.title = cal.maxText;
15798                 return;
15799             }
15800             if(ddays){
15801                 if(ddays.indexOf(d.getDay()) != -1){
15802                     cell.title = ddaysText;
15803                     cell.className = " fc-state-disabled";
15804                 }
15805             }
15806             if(ddMatch && format){
15807                 var fvalue = d.dateFormat(format);
15808                 if(ddMatch.test(fvalue)){
15809                     cell.title = ddText.replace("%0", fvalue);
15810                     cell.className = " fc-state-disabled";
15811                 }
15812             }
15813             
15814             if (!cell.initialClassName) {
15815                 cell.initialClassName = cell.dom.className;
15816             }
15817             
15818             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15819         };
15820
15821         var i = 0;
15822         
15823         for(; i < startingPos; i++) {
15824             textEls[i].innerHTML = (++prevStart);
15825             d.setDate(d.getDate()+1);
15826             
15827             cells[i].className = "fc-past fc-other-month";
15828             setCellClass(this, cells[i]);
15829         }
15830         
15831         var intDay = 0;
15832         
15833         for(; i < days; i++){
15834             intDay = i - startingPos + 1;
15835             textEls[i].innerHTML = (intDay);
15836             d.setDate(d.getDate()+1);
15837             
15838             cells[i].className = ''; // "x-date-active";
15839             setCellClass(this, cells[i]);
15840         }
15841         var extraDays = 0;
15842         
15843         for(; i < 42; i++) {
15844             textEls[i].innerHTML = (++extraDays);
15845             d.setDate(d.getDate()+1);
15846             
15847             cells[i].className = "fc-future fc-other-month";
15848             setCellClass(this, cells[i]);
15849         }
15850         
15851         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15852         
15853         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15854         
15855         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15856         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15857         
15858         if(totalRows != 6){
15859             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15860             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15861         }
15862         
15863         this.fireEvent('monthchange', this, date);
15864         
15865         
15866         /*
15867         if(!this.internalRender){
15868             var main = this.el.dom.firstChild;
15869             var w = main.offsetWidth;
15870             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15871             Roo.fly(main).setWidth(w);
15872             this.internalRender = true;
15873             // opera does not respect the auto grow header center column
15874             // then, after it gets a width opera refuses to recalculate
15875             // without a second pass
15876             if(Roo.isOpera && !this.secondPass){
15877                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15878                 this.secondPass = true;
15879                 this.update.defer(10, this, [date]);
15880             }
15881         }
15882         */
15883         
15884     },
15885     
15886     findCell : function(dt) {
15887         dt = dt.clearTime().getTime();
15888         var ret = false;
15889         this.cells.each(function(c){
15890             //Roo.log("check " +c.dateValue + '?=' + dt);
15891             if(c.dateValue == dt){
15892                 ret = c;
15893                 return false;
15894             }
15895             return true;
15896         });
15897         
15898         return ret;
15899     },
15900     
15901     findCells : function(ev) {
15902         var s = ev.start.clone().clearTime().getTime();
15903        // Roo.log(s);
15904         var e= ev.end.clone().clearTime().getTime();
15905        // Roo.log(e);
15906         var ret = [];
15907         this.cells.each(function(c){
15908              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15909             
15910             if(c.dateValue > e){
15911                 return ;
15912             }
15913             if(c.dateValue < s){
15914                 return ;
15915             }
15916             ret.push(c);
15917         });
15918         
15919         return ret;    
15920     },
15921     
15922 //    findBestRow: function(cells)
15923 //    {
15924 //        var ret = 0;
15925 //        
15926 //        for (var i =0 ; i < cells.length;i++) {
15927 //            ret  = Math.max(cells[i].rows || 0,ret);
15928 //        }
15929 //        return ret;
15930 //        
15931 //    },
15932     
15933     
15934     addItem : function(ev)
15935     {
15936         // look for vertical location slot in
15937         var cells = this.findCells(ev);
15938         
15939 //        ev.row = this.findBestRow(cells);
15940         
15941         // work out the location.
15942         
15943         var crow = false;
15944         var rows = [];
15945         for(var i =0; i < cells.length; i++) {
15946             
15947             cells[i].row = cells[0].row;
15948             
15949             if(i == 0){
15950                 cells[i].row = cells[i].row + 1;
15951             }
15952             
15953             if (!crow) {
15954                 crow = {
15955                     start : cells[i],
15956                     end :  cells[i]
15957                 };
15958                 continue;
15959             }
15960             if (crow.start.getY() == cells[i].getY()) {
15961                 // on same row.
15962                 crow.end = cells[i];
15963                 continue;
15964             }
15965             // different row.
15966             rows.push(crow);
15967             crow = {
15968                 start: cells[i],
15969                 end : cells[i]
15970             };
15971             
15972         }
15973         
15974         rows.push(crow);
15975         ev.els = [];
15976         ev.rows = rows;
15977         ev.cells = cells;
15978         
15979         cells[0].events.push(ev);
15980         
15981         this.calevents.push(ev);
15982     },
15983     
15984     clearEvents: function() {
15985         
15986         if(!this.calevents){
15987             return;
15988         }
15989         
15990         Roo.each(this.cells.elements, function(c){
15991             c.row = 0;
15992             c.events = [];
15993             c.more = [];
15994         });
15995         
15996         Roo.each(this.calevents, function(e) {
15997             Roo.each(e.els, function(el) {
15998                 el.un('mouseenter' ,this.onEventEnter, this);
15999                 el.un('mouseleave' ,this.onEventLeave, this);
16000                 el.remove();
16001             },this);
16002         },this);
16003         
16004         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16005             e.remove();
16006         });
16007         
16008     },
16009     
16010     renderEvents: function()
16011     {   
16012         var _this = this;
16013         
16014         this.cells.each(function(c) {
16015             
16016             if(c.row < 5){
16017                 return;
16018             }
16019             
16020             var ev = c.events;
16021             
16022             var r = 4;
16023             if(c.row != c.events.length){
16024                 r = 4 - (4 - (c.row - c.events.length));
16025             }
16026             
16027             c.events = ev.slice(0, r);
16028             c.more = ev.slice(r);
16029             
16030             if(c.more.length && c.more.length == 1){
16031                 c.events.push(c.more.pop());
16032             }
16033             
16034             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16035             
16036         });
16037             
16038         this.cells.each(function(c) {
16039             
16040             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16041             
16042             
16043             for (var e = 0; e < c.events.length; e++){
16044                 var ev = c.events[e];
16045                 var rows = ev.rows;
16046                 
16047                 for(var i = 0; i < rows.length; i++) {
16048                 
16049                     // how many rows should it span..
16050
16051                     var  cfg = {
16052                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16053                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16054
16055                         unselectable : "on",
16056                         cn : [
16057                             {
16058                                 cls: 'fc-event-inner',
16059                                 cn : [
16060     //                                {
16061     //                                  tag:'span',
16062     //                                  cls: 'fc-event-time',
16063     //                                  html : cells.length > 1 ? '' : ev.time
16064     //                                },
16065                                     {
16066                                       tag:'span',
16067                                       cls: 'fc-event-title',
16068                                       html : String.format('{0}', ev.title)
16069                                     }
16070
16071
16072                                 ]
16073                             },
16074                             {
16075                                 cls: 'ui-resizable-handle ui-resizable-e',
16076                                 html : '&nbsp;&nbsp;&nbsp'
16077                             }
16078
16079                         ]
16080                     };
16081
16082                     if (i == 0) {
16083                         cfg.cls += ' fc-event-start';
16084                     }
16085                     if ((i+1) == rows.length) {
16086                         cfg.cls += ' fc-event-end';
16087                     }
16088
16089                     var ctr = _this.el.select('.fc-event-container',true).first();
16090                     var cg = ctr.createChild(cfg);
16091
16092                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16093                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16094
16095                     var r = (c.more.length) ? 1 : 0;
16096                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16097                     cg.setWidth(ebox.right - sbox.x -2);
16098
16099                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16100                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16101                     cg.on('click', _this.onEventClick, _this, ev);
16102
16103                     ev.els.push(cg);
16104                     
16105                 }
16106                 
16107             }
16108             
16109             
16110             if(c.more.length){
16111                 var  cfg = {
16112                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16113                     style : 'position: absolute',
16114                     unselectable : "on",
16115                     cn : [
16116                         {
16117                             cls: 'fc-event-inner',
16118                             cn : [
16119                                 {
16120                                   tag:'span',
16121                                   cls: 'fc-event-title',
16122                                   html : 'More'
16123                                 }
16124
16125
16126                             ]
16127                         },
16128                         {
16129                             cls: 'ui-resizable-handle ui-resizable-e',
16130                             html : '&nbsp;&nbsp;&nbsp'
16131                         }
16132
16133                     ]
16134                 };
16135
16136                 var ctr = _this.el.select('.fc-event-container',true).first();
16137                 var cg = ctr.createChild(cfg);
16138
16139                 var sbox = c.select('.fc-day-content',true).first().getBox();
16140                 var ebox = c.select('.fc-day-content',true).first().getBox();
16141                 //Roo.log(cg);
16142                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16143                 cg.setWidth(ebox.right - sbox.x -2);
16144
16145                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16146                 
16147             }
16148             
16149         });
16150         
16151         
16152         
16153     },
16154     
16155     onEventEnter: function (e, el,event,d) {
16156         this.fireEvent('evententer', this, el, event);
16157     },
16158     
16159     onEventLeave: function (e, el,event,d) {
16160         this.fireEvent('eventleave', this, el, event);
16161     },
16162     
16163     onEventClick: function (e, el,event,d) {
16164         this.fireEvent('eventclick', this, el, event);
16165     },
16166     
16167     onMonthChange: function () {
16168         this.store.load();
16169     },
16170     
16171     onMoreEventClick: function(e, el, more)
16172     {
16173         var _this = this;
16174         
16175         this.calpopover.placement = 'right';
16176         this.calpopover.setTitle('More');
16177         
16178         this.calpopover.setContent('');
16179         
16180         var ctr = this.calpopover.el.select('.popover-content', true).first();
16181         
16182         Roo.each(more, function(m){
16183             var cfg = {
16184                 cls : 'fc-event-hori fc-event-draggable',
16185                 html : m.title
16186             };
16187             var cg = ctr.createChild(cfg);
16188             
16189             cg.on('click', _this.onEventClick, _this, m);
16190         });
16191         
16192         this.calpopover.show(el);
16193         
16194         
16195     },
16196     
16197     onLoad: function () 
16198     {   
16199         this.calevents = [];
16200         var cal = this;
16201         
16202         if(this.store.getCount() > 0){
16203             this.store.data.each(function(d){
16204                cal.addItem({
16205                     id : d.data.id,
16206                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16207                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16208                     time : d.data.start_time,
16209                     title : d.data.title,
16210                     description : d.data.description,
16211                     venue : d.data.venue
16212                 });
16213             });
16214         }
16215         
16216         this.renderEvents();
16217         
16218         if(this.calevents.length && this.loadMask){
16219             this.maskEl.hide();
16220         }
16221     },
16222     
16223     onBeforeLoad: function()
16224     {
16225         this.clearEvents();
16226         if(this.loadMask){
16227             this.maskEl.show();
16228         }
16229     }
16230 });
16231
16232  
16233  /*
16234  * - LGPL
16235  *
16236  * element
16237  * 
16238  */
16239
16240 /**
16241  * @class Roo.bootstrap.Popover
16242  * @extends Roo.bootstrap.Component
16243  * Bootstrap Popover class
16244  * @cfg {String} html contents of the popover   (or false to use children..)
16245  * @cfg {String} title of popover (or false to hide)
16246  * @cfg {String} placement how it is placed
16247  * @cfg {String} trigger click || hover (or false to trigger manually)
16248  * @cfg {String} over what (parent or false to trigger manually.)
16249  * @cfg {Number} delay - delay before showing
16250  
16251  * @constructor
16252  * Create a new Popover
16253  * @param {Object} config The config object
16254  */
16255
16256 Roo.bootstrap.Popover = function(config){
16257     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16258     
16259     this.addEvents({
16260         // raw events
16261          /**
16262          * @event show
16263          * After the popover show
16264          * 
16265          * @param {Roo.bootstrap.Popover} this
16266          */
16267         "show" : true,
16268         /**
16269          * @event hide
16270          * After the popover hide
16271          * 
16272          * @param {Roo.bootstrap.Popover} this
16273          */
16274         "hide" : true
16275     });
16276 };
16277
16278 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16279     
16280     title: 'Fill in a title',
16281     html: false,
16282     
16283     placement : 'right',
16284     trigger : 'hover', // hover
16285     
16286     delay : 0,
16287     
16288     over: 'parent',
16289     
16290     can_build_overlaid : false,
16291     
16292     getChildContainer : function()
16293     {
16294         return this.el.select('.popover-content',true).first();
16295     },
16296     
16297     getAutoCreate : function(){
16298          
16299         var cfg = {
16300            cls : 'popover roo-dynamic',
16301            style: 'display:block',
16302            cn : [
16303                 {
16304                     cls : 'arrow'
16305                 },
16306                 {
16307                     cls : 'popover-inner',
16308                     cn : [
16309                         {
16310                             tag: 'h3',
16311                             cls: 'popover-title',
16312                             html : this.title
16313                         },
16314                         {
16315                             cls : 'popover-content',
16316                             html : this.html
16317                         }
16318                     ]
16319                     
16320                 }
16321            ]
16322         };
16323         
16324         return cfg;
16325     },
16326     setTitle: function(str)
16327     {
16328         this.title = str;
16329         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16330     },
16331     setContent: function(str)
16332     {
16333         this.html = str;
16334         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16335     },
16336     // as it get's added to the bottom of the page.
16337     onRender : function(ct, position)
16338     {
16339         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16340         if(!this.el){
16341             var cfg = Roo.apply({},  this.getAutoCreate());
16342             cfg.id = Roo.id();
16343             
16344             if (this.cls) {
16345                 cfg.cls += ' ' + this.cls;
16346             }
16347             if (this.style) {
16348                 cfg.style = this.style;
16349             }
16350             //Roo.log("adding to ");
16351             this.el = Roo.get(document.body).createChild(cfg, position);
16352 //            Roo.log(this.el);
16353         }
16354         this.initEvents();
16355     },
16356     
16357     initEvents : function()
16358     {
16359         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16360         this.el.enableDisplayMode('block');
16361         this.el.hide();
16362         if (this.over === false) {
16363             return; 
16364         }
16365         if (this.triggers === false) {
16366             return;
16367         }
16368         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16369         var triggers = this.trigger ? this.trigger.split(' ') : [];
16370         Roo.each(triggers, function(trigger) {
16371         
16372             if (trigger == 'click') {
16373                 on_el.on('click', this.toggle, this);
16374             } else if (trigger != 'manual') {
16375                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16376                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16377       
16378                 on_el.on(eventIn  ,this.enter, this);
16379                 on_el.on(eventOut, this.leave, this);
16380             }
16381         }, this);
16382         
16383     },
16384     
16385     
16386     // private
16387     timeout : null,
16388     hoverState : null,
16389     
16390     toggle : function () {
16391         this.hoverState == 'in' ? this.leave() : this.enter();
16392     },
16393     
16394     enter : function () {
16395         
16396         clearTimeout(this.timeout);
16397     
16398         this.hoverState = 'in';
16399     
16400         if (!this.delay || !this.delay.show) {
16401             this.show();
16402             return;
16403         }
16404         var _t = this;
16405         this.timeout = setTimeout(function () {
16406             if (_t.hoverState == 'in') {
16407                 _t.show();
16408             }
16409         }, this.delay.show)
16410     },
16411     
16412     leave : function() {
16413         clearTimeout(this.timeout);
16414     
16415         this.hoverState = 'out';
16416     
16417         if (!this.delay || !this.delay.hide) {
16418             this.hide();
16419             return;
16420         }
16421         var _t = this;
16422         this.timeout = setTimeout(function () {
16423             if (_t.hoverState == 'out') {
16424                 _t.hide();
16425             }
16426         }, this.delay.hide)
16427     },
16428     
16429     show : function (on_el)
16430     {
16431         if (!on_el) {
16432             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16433         }
16434         
16435         // set content.
16436         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16437         if (this.html !== false) {
16438             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16439         }
16440         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16441         if (!this.title.length) {
16442             this.el.select('.popover-title',true).hide();
16443         }
16444         
16445         var placement = typeof this.placement == 'function' ?
16446             this.placement.call(this, this.el, on_el) :
16447             this.placement;
16448             
16449         var autoToken = /\s?auto?\s?/i;
16450         var autoPlace = autoToken.test(placement);
16451         if (autoPlace) {
16452             placement = placement.replace(autoToken, '') || 'top';
16453         }
16454         
16455         //this.el.detach()
16456         //this.el.setXY([0,0]);
16457         this.el.show();
16458         this.el.dom.style.display='block';
16459         this.el.addClass(placement);
16460         
16461         //this.el.appendTo(on_el);
16462         
16463         var p = this.getPosition();
16464         var box = this.el.getBox();
16465         
16466         if (autoPlace) {
16467             // fixme..
16468         }
16469         var align = Roo.bootstrap.Popover.alignment[placement];
16470         this.el.alignTo(on_el, align[0],align[1]);
16471         //var arrow = this.el.select('.arrow',true).first();
16472         //arrow.set(align[2], 
16473         
16474         this.el.addClass('in');
16475         
16476         
16477         if (this.el.hasClass('fade')) {
16478             // fade it?
16479         }
16480         
16481         this.hoverState = 'in';
16482         
16483         this.fireEvent('show', this);
16484         
16485     },
16486     hide : function()
16487     {
16488         this.el.setXY([0,0]);
16489         this.el.removeClass('in');
16490         this.el.hide();
16491         this.hoverState = null;
16492         
16493         this.fireEvent('hide', this);
16494     }
16495     
16496 });
16497
16498 Roo.bootstrap.Popover.alignment = {
16499     'left' : ['r-l', [-10,0], 'right'],
16500     'right' : ['l-r', [10,0], 'left'],
16501     'bottom' : ['t-b', [0,10], 'top'],
16502     'top' : [ 'b-t', [0,-10], 'bottom']
16503 };
16504
16505  /*
16506  * - LGPL
16507  *
16508  * Progress
16509  * 
16510  */
16511
16512 /**
16513  * @class Roo.bootstrap.Progress
16514  * @extends Roo.bootstrap.Component
16515  * Bootstrap Progress class
16516  * @cfg {Boolean} striped striped of the progress bar
16517  * @cfg {Boolean} active animated of the progress bar
16518  * 
16519  * 
16520  * @constructor
16521  * Create a new Progress
16522  * @param {Object} config The config object
16523  */
16524
16525 Roo.bootstrap.Progress = function(config){
16526     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16527 };
16528
16529 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16530     
16531     striped : false,
16532     active: false,
16533     
16534     getAutoCreate : function(){
16535         var cfg = {
16536             tag: 'div',
16537             cls: 'progress'
16538         };
16539         
16540         
16541         if(this.striped){
16542             cfg.cls += ' progress-striped';
16543         }
16544       
16545         if(this.active){
16546             cfg.cls += ' active';
16547         }
16548         
16549         
16550         return cfg;
16551     }
16552    
16553 });
16554
16555  
16556
16557  /*
16558  * - LGPL
16559  *
16560  * ProgressBar
16561  * 
16562  */
16563
16564 /**
16565  * @class Roo.bootstrap.ProgressBar
16566  * @extends Roo.bootstrap.Component
16567  * Bootstrap ProgressBar class
16568  * @cfg {Number} aria_valuenow aria-value now
16569  * @cfg {Number} aria_valuemin aria-value min
16570  * @cfg {Number} aria_valuemax aria-value max
16571  * @cfg {String} label label for the progress bar
16572  * @cfg {String} panel (success | info | warning | danger )
16573  * @cfg {String} role role of the progress bar
16574  * @cfg {String} sr_only text
16575  * 
16576  * 
16577  * @constructor
16578  * Create a new ProgressBar
16579  * @param {Object} config The config object
16580  */
16581
16582 Roo.bootstrap.ProgressBar = function(config){
16583     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16584 };
16585
16586 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16587     
16588     aria_valuenow : 0,
16589     aria_valuemin : 0,
16590     aria_valuemax : 100,
16591     label : false,
16592     panel : false,
16593     role : false,
16594     sr_only: false,
16595     
16596     getAutoCreate : function()
16597     {
16598         
16599         var cfg = {
16600             tag: 'div',
16601             cls: 'progress-bar',
16602             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16603         };
16604         
16605         if(this.sr_only){
16606             cfg.cn = {
16607                 tag: 'span',
16608                 cls: 'sr-only',
16609                 html: this.sr_only
16610             }
16611         }
16612         
16613         if(this.role){
16614             cfg.role = this.role;
16615         }
16616         
16617         if(this.aria_valuenow){
16618             cfg['aria-valuenow'] = this.aria_valuenow;
16619         }
16620         
16621         if(this.aria_valuemin){
16622             cfg['aria-valuemin'] = this.aria_valuemin;
16623         }
16624         
16625         if(this.aria_valuemax){
16626             cfg['aria-valuemax'] = this.aria_valuemax;
16627         }
16628         
16629         if(this.label && !this.sr_only){
16630             cfg.html = this.label;
16631         }
16632         
16633         if(this.panel){
16634             cfg.cls += ' progress-bar-' + this.panel;
16635         }
16636         
16637         return cfg;
16638     },
16639     
16640     update : function(aria_valuenow)
16641     {
16642         this.aria_valuenow = aria_valuenow;
16643         
16644         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16645     }
16646    
16647 });
16648
16649  
16650
16651  /*
16652  * - LGPL
16653  *
16654  * column
16655  * 
16656  */
16657
16658 /**
16659  * @class Roo.bootstrap.TabGroup
16660  * @extends Roo.bootstrap.Column
16661  * Bootstrap Column class
16662  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16663  * @cfg {Boolean} carousel true to make the group behave like a carousel
16664  * @cfg {Boolean} bullets show bullets for the panels
16665  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16666  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16667  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16668  * @cfg {Boolean} showarrow (true|false) show arrow default true
16669  * 
16670  * @constructor
16671  * Create a new TabGroup
16672  * @param {Object} config The config object
16673  */
16674
16675 Roo.bootstrap.TabGroup = function(config){
16676     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16677     if (!this.navId) {
16678         this.navId = Roo.id();
16679     }
16680     this.tabs = [];
16681     Roo.bootstrap.TabGroup.register(this);
16682     
16683 };
16684
16685 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16686     
16687     carousel : false,
16688     transition : false,
16689     bullets : 0,
16690     timer : 0,
16691     autoslide : false,
16692     slideFn : false,
16693     slideOnTouch : false,
16694     showarrow : true,
16695     
16696     getAutoCreate : function()
16697     {
16698         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16699         
16700         cfg.cls += ' tab-content';
16701         
16702         if (this.carousel) {
16703             cfg.cls += ' carousel slide';
16704             
16705             cfg.cn = [{
16706                cls : 'carousel-inner',
16707                cn : []
16708             }];
16709         
16710             if(this.bullets  && !Roo.isTouch){
16711                 
16712                 var bullets = {
16713                     cls : 'carousel-bullets',
16714                     cn : []
16715                 };
16716                
16717                 if(this.bullets_cls){
16718                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16719                 }
16720                 
16721                 bullets.cn.push({
16722                     cls : 'clear'
16723                 });
16724                 
16725                 cfg.cn[0].cn.push(bullets);
16726             }
16727             
16728             if(this.showarrow){
16729                 cfg.cn[0].cn.push({
16730                     tag : 'div',
16731                     class : 'carousel-arrow',
16732                     cn : [
16733                         {
16734                             tag : 'div',
16735                             class : 'carousel-prev',
16736                             cn : [
16737                                 {
16738                                     tag : 'i',
16739                                     class : 'fa fa-chevron-left'
16740                                 }
16741                             ]
16742                         },
16743                         {
16744                             tag : 'div',
16745                             class : 'carousel-next',
16746                             cn : [
16747                                 {
16748                                     tag : 'i',
16749                                     class : 'fa fa-chevron-right'
16750                                 }
16751                             ]
16752                         }
16753                     ]
16754                 });
16755             }
16756             
16757         }
16758         
16759         return cfg;
16760     },
16761     
16762     initEvents:  function()
16763     {
16764         if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16765             this.el.on("touchstart", this.onTouchStart, this);
16766         }
16767         
16768         if(this.autoslide){
16769             var _this = this;
16770             
16771             this.slideFn = window.setInterval(function() {
16772                 _this.showPanelNext();
16773             }, this.timer);
16774         }
16775         
16776         if(this.showarrow){
16777             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16778             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16779         }
16780         
16781         
16782     },
16783     
16784     onTouchStart : function(e, el, o)
16785     {
16786         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16787             return;
16788         }
16789         
16790         this.showPanelNext();
16791     },
16792     
16793     getChildContainer : function()
16794     {
16795         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16796     },
16797     
16798     /**
16799     * register a Navigation item
16800     * @param {Roo.bootstrap.NavItem} the navitem to add
16801     */
16802     register : function(item)
16803     {
16804         this.tabs.push( item);
16805         item.navId = this.navId; // not really needed..
16806         this.addBullet();
16807     
16808     },
16809     
16810     getActivePanel : function()
16811     {
16812         var r = false;
16813         Roo.each(this.tabs, function(t) {
16814             if (t.active) {
16815                 r = t;
16816                 return false;
16817             }
16818             return null;
16819         });
16820         return r;
16821         
16822     },
16823     getPanelByName : function(n)
16824     {
16825         var r = false;
16826         Roo.each(this.tabs, function(t) {
16827             if (t.tabId == n) {
16828                 r = t;
16829                 return false;
16830             }
16831             return null;
16832         });
16833         return r;
16834     },
16835     indexOfPanel : function(p)
16836     {
16837         var r = false;
16838         Roo.each(this.tabs, function(t,i) {
16839             if (t.tabId == p.tabId) {
16840                 r = i;
16841                 return false;
16842             }
16843             return null;
16844         });
16845         return r;
16846     },
16847     /**
16848      * show a specific panel
16849      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16850      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16851      */
16852     showPanel : function (pan)
16853     {
16854         if(this.transition || typeof(pan) == 'undefined'){
16855             Roo.log("waiting for the transitionend");
16856             return;
16857         }
16858         
16859         if (typeof(pan) == 'number') {
16860             pan = this.tabs[pan];
16861         }
16862         
16863         if (typeof(pan) == 'string') {
16864             pan = this.getPanelByName(pan);
16865         }
16866         
16867         var cur = this.getActivePanel();
16868         
16869         if(!pan || !cur){
16870             Roo.log('pan or acitve pan is undefined');
16871             return false;
16872         }
16873         
16874         if (pan.tabId == this.getActivePanel().tabId) {
16875             return true;
16876         }
16877         
16878         if (false === cur.fireEvent('beforedeactivate')) {
16879             return false;
16880         }
16881         
16882         if(this.bullets > 0 && !Roo.isTouch){
16883             this.setActiveBullet(this.indexOfPanel(pan));
16884         }
16885         
16886         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16887             
16888             this.transition = true;
16889             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16890             var lr = dir == 'next' ? 'left' : 'right';
16891             pan.el.addClass(dir); // or prev
16892             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16893             cur.el.addClass(lr); // or right
16894             pan.el.addClass(lr);
16895             
16896             var _this = this;
16897             cur.el.on('transitionend', function() {
16898                 Roo.log("trans end?");
16899                 
16900                 pan.el.removeClass([lr,dir]);
16901                 pan.setActive(true);
16902                 
16903                 cur.el.removeClass([lr]);
16904                 cur.setActive(false);
16905                 
16906                 _this.transition = false;
16907                 
16908             }, this, { single:  true } );
16909             
16910             return true;
16911         }
16912         
16913         cur.setActive(false);
16914         pan.setActive(true);
16915         
16916         return true;
16917         
16918     },
16919     showPanelNext : function()
16920     {
16921         var i = this.indexOfPanel(this.getActivePanel());
16922         
16923         if (i >= this.tabs.length - 1 && !this.autoslide) {
16924             return;
16925         }
16926         
16927         if (i >= this.tabs.length - 1 && this.autoslide) {
16928             i = -1;
16929         }
16930         
16931         this.showPanel(this.tabs[i+1]);
16932     },
16933     
16934     showPanelPrev : function()
16935     {
16936         var i = this.indexOfPanel(this.getActivePanel());
16937         
16938         if (i  < 1 && !this.autoslide) {
16939             return;
16940         }
16941         
16942         if (i < 1 && this.autoslide) {
16943             i = this.tabs.length;
16944         }
16945         
16946         this.showPanel(this.tabs[i-1]);
16947     },
16948     
16949     
16950     addBullet: function()
16951     {
16952         if(!this.bullets || Roo.isTouch){
16953             return;
16954         }
16955         var ctr = this.el.select('.carousel-bullets',true).first();
16956         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16957         var bullet = ctr.createChild({
16958             cls : 'bullet bullet-' + i
16959         },ctr.dom.lastChild);
16960         
16961         
16962         var _this = this;
16963         
16964         bullet.on('click', (function(e, el, o, ii, t){
16965
16966             e.preventDefault();
16967
16968             this.showPanel(ii);
16969
16970             if(this.autoslide && this.slideFn){
16971                 clearInterval(this.slideFn);
16972                 this.slideFn = window.setInterval(function() {
16973                     _this.showPanelNext();
16974                 }, this.timer);
16975             }
16976
16977         }).createDelegate(this, [i, bullet], true));
16978                 
16979         
16980     },
16981      
16982     setActiveBullet : function(i)
16983     {
16984         if(Roo.isTouch){
16985             return;
16986         }
16987         
16988         Roo.each(this.el.select('.bullet', true).elements, function(el){
16989             el.removeClass('selected');
16990         });
16991
16992         var bullet = this.el.select('.bullet-' + i, true).first();
16993         
16994         if(!bullet){
16995             return;
16996         }
16997         
16998         bullet.addClass('selected');
16999     }
17000     
17001     
17002   
17003 });
17004
17005  
17006
17007  
17008  
17009 Roo.apply(Roo.bootstrap.TabGroup, {
17010     
17011     groups: {},
17012      /**
17013     * register a Navigation Group
17014     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17015     */
17016     register : function(navgrp)
17017     {
17018         this.groups[navgrp.navId] = navgrp;
17019         
17020     },
17021     /**
17022     * fetch a Navigation Group based on the navigation ID
17023     * if one does not exist , it will get created.
17024     * @param {string} the navgroup to add
17025     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17026     */
17027     get: function(navId) {
17028         if (typeof(this.groups[navId]) == 'undefined') {
17029             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17030         }
17031         return this.groups[navId] ;
17032     }
17033     
17034     
17035     
17036 });
17037
17038  /*
17039  * - LGPL
17040  *
17041  * TabPanel
17042  * 
17043  */
17044
17045 /**
17046  * @class Roo.bootstrap.TabPanel
17047  * @extends Roo.bootstrap.Component
17048  * Bootstrap TabPanel class
17049  * @cfg {Boolean} active panel active
17050  * @cfg {String} html panel content
17051  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17052  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17053  * @cfg {String} href click to link..
17054  * 
17055  * 
17056  * @constructor
17057  * Create a new TabPanel
17058  * @param {Object} config The config object
17059  */
17060
17061 Roo.bootstrap.TabPanel = function(config){
17062     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17063     this.addEvents({
17064         /**
17065              * @event changed
17066              * Fires when the active status changes
17067              * @param {Roo.bootstrap.TabPanel} this
17068              * @param {Boolean} state the new state
17069             
17070          */
17071         'changed': true,
17072         /**
17073              * @event beforedeactivate
17074              * Fires before a tab is de-activated - can be used to do validation on a form.
17075              * @param {Roo.bootstrap.TabPanel} this
17076              * @return {Boolean} false if there is an error
17077             
17078          */
17079         'beforedeactivate': true
17080      });
17081     
17082     this.tabId = this.tabId || Roo.id();
17083   
17084 };
17085
17086 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17087     
17088     active: false,
17089     html: false,
17090     tabId: false,
17091     navId : false,
17092     href : '',
17093     
17094     getAutoCreate : function(){
17095         var cfg = {
17096             tag: 'div',
17097             // item is needed for carousel - not sure if it has any effect otherwise
17098             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17099             html: this.html || ''
17100         };
17101         
17102         if(this.active){
17103             cfg.cls += ' active';
17104         }
17105         
17106         if(this.tabId){
17107             cfg.tabId = this.tabId;
17108         }
17109         
17110         
17111         return cfg;
17112     },
17113     
17114     initEvents:  function()
17115     {
17116         var p = this.parent();
17117         this.navId = this.navId || p.navId;
17118         
17119         if (typeof(this.navId) != 'undefined') {
17120             // not really needed.. but just in case.. parent should be a NavGroup.
17121             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17122             
17123             tg.register(this);
17124             
17125             var i = tg.tabs.length - 1;
17126             
17127             if(this.active && tg.bullets > 0 && i < tg.bullets){
17128                 tg.setActiveBullet(i);
17129             }
17130         }
17131         
17132         if(this.href.length){
17133             this.el.on('click', this.onClick, this);
17134         }
17135         
17136     },
17137     
17138     onRender : function(ct, position)
17139     {
17140        // Roo.log("Call onRender: " + this.xtype);
17141         
17142         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17143         
17144         
17145         
17146         
17147         
17148     },
17149     
17150     setActive: function(state)
17151     {
17152         Roo.log("panel - set active " + this.tabId + "=" + state);
17153         
17154         this.active = state;
17155         if (!state) {
17156             this.el.removeClass('active');
17157             
17158         } else  if (!this.el.hasClass('active')) {
17159             this.el.addClass('active');
17160         }
17161         
17162         this.fireEvent('changed', this, state);
17163     },
17164     
17165     onClick: function(e)
17166     {
17167         e.preventDefault();
17168         
17169         window.location.href = this.href;
17170     }
17171     
17172     
17173 });
17174  
17175
17176  
17177
17178  /*
17179  * - LGPL
17180  *
17181  * DateField
17182  * 
17183  */
17184
17185 /**
17186  * @class Roo.bootstrap.DateField
17187  * @extends Roo.bootstrap.Input
17188  * Bootstrap DateField class
17189  * @cfg {Number} weekStart default 0
17190  * @cfg {String} viewMode default empty, (months|years)
17191  * @cfg {String} minViewMode default empty, (months|years)
17192  * @cfg {Number} startDate default -Infinity
17193  * @cfg {Number} endDate default Infinity
17194  * @cfg {Boolean} todayHighlight default false
17195  * @cfg {Boolean} todayBtn default false
17196  * @cfg {Boolean} calendarWeeks default false
17197  * @cfg {Object} daysOfWeekDisabled default empty
17198  * @cfg {Boolean} singleMode default false (true | false)
17199  * 
17200  * @cfg {Boolean} keyboardNavigation default true
17201  * @cfg {String} language default en
17202  * 
17203  * @constructor
17204  * Create a new DateField
17205  * @param {Object} config The config object
17206  */
17207
17208 Roo.bootstrap.DateField = function(config){
17209     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17210      this.addEvents({
17211             /**
17212              * @event show
17213              * Fires when this field show.
17214              * @param {Roo.bootstrap.DateField} this
17215              * @param {Mixed} date The date value
17216              */
17217             show : true,
17218             /**
17219              * @event show
17220              * Fires when this field hide.
17221              * @param {Roo.bootstrap.DateField} this
17222              * @param {Mixed} date The date value
17223              */
17224             hide : true,
17225             /**
17226              * @event select
17227              * Fires when select a date.
17228              * @param {Roo.bootstrap.DateField} this
17229              * @param {Mixed} date The date value
17230              */
17231             select : true,
17232             /**
17233              * @event beforeselect
17234              * Fires when before select a date.
17235              * @param {Roo.bootstrap.DateField} this
17236              * @param {Mixed} date The date value
17237              */
17238             beforeselect : true
17239         });
17240 };
17241
17242 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17243     
17244     /**
17245      * @cfg {String} format
17246      * The default date format string which can be overriden for localization support.  The format must be
17247      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17248      */
17249     format : "m/d/y",
17250     /**
17251      * @cfg {String} altFormats
17252      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17253      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17254      */
17255     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17256     
17257     weekStart : 0,
17258     
17259     viewMode : '',
17260     
17261     minViewMode : '',
17262     
17263     todayHighlight : false,
17264     
17265     todayBtn: false,
17266     
17267     language: 'en',
17268     
17269     keyboardNavigation: true,
17270     
17271     calendarWeeks: false,
17272     
17273     startDate: -Infinity,
17274     
17275     endDate: Infinity,
17276     
17277     daysOfWeekDisabled: [],
17278     
17279     _events: [],
17280     
17281     singleMode : false,
17282     
17283     UTCDate: function()
17284     {
17285         return new Date(Date.UTC.apply(Date, arguments));
17286     },
17287     
17288     UTCToday: function()
17289     {
17290         var today = new Date();
17291         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17292     },
17293     
17294     getDate: function() {
17295             var d = this.getUTCDate();
17296             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17297     },
17298     
17299     getUTCDate: function() {
17300             return this.date;
17301     },
17302     
17303     setDate: function(d) {
17304             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17305     },
17306     
17307     setUTCDate: function(d) {
17308             this.date = d;
17309             this.setValue(this.formatDate(this.date));
17310     },
17311         
17312     onRender: function(ct, position)
17313     {
17314         
17315         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17316         
17317         this.language = this.language || 'en';
17318         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17319         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17320         
17321         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17322         this.format = this.format || 'm/d/y';
17323         this.isInline = false;
17324         this.isInput = true;
17325         this.component = this.el.select('.add-on', true).first() || false;
17326         this.component = (this.component && this.component.length === 0) ? false : this.component;
17327         this.hasInput = this.component && this.inputEL().length;
17328         
17329         if (typeof(this.minViewMode === 'string')) {
17330             switch (this.minViewMode) {
17331                 case 'months':
17332                     this.minViewMode = 1;
17333                     break;
17334                 case 'years':
17335                     this.minViewMode = 2;
17336                     break;
17337                 default:
17338                     this.minViewMode = 0;
17339                     break;
17340             }
17341         }
17342         
17343         if (typeof(this.viewMode === 'string')) {
17344             switch (this.viewMode) {
17345                 case 'months':
17346                     this.viewMode = 1;
17347                     break;
17348                 case 'years':
17349                     this.viewMode = 2;
17350                     break;
17351                 default:
17352                     this.viewMode = 0;
17353                     break;
17354             }
17355         }
17356                 
17357         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17358         
17359 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17360         
17361         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17362         
17363         this.picker().on('mousedown', this.onMousedown, this);
17364         this.picker().on('click', this.onClick, this);
17365         
17366         this.picker().addClass('datepicker-dropdown');
17367         
17368         this.startViewMode = this.viewMode;
17369         
17370         if(this.singleMode){
17371             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17372                 v.setVisibilityMode(Roo.Element.DISPLAY);
17373                 v.hide();
17374             });
17375             
17376             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17377                 v.setStyle('width', '189px');
17378             });
17379         }
17380         
17381         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17382             if(!this.calendarWeeks){
17383                 v.remove();
17384                 return;
17385             }
17386             
17387             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17388             v.attr('colspan', function(i, val){
17389                 return parseInt(val) + 1;
17390             });
17391         });
17392                         
17393         
17394         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17395         
17396         this.setStartDate(this.startDate);
17397         this.setEndDate(this.endDate);
17398         
17399         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17400         
17401         this.fillDow();
17402         this.fillMonths();
17403         this.update();
17404         this.showMode();
17405         
17406         if(this.isInline) {
17407             this.show();
17408         }
17409     },
17410     
17411     picker : function()
17412     {
17413         return this.pickerEl;
17414 //        return this.el.select('.datepicker', true).first();
17415     },
17416     
17417     fillDow: function()
17418     {
17419         var dowCnt = this.weekStart;
17420         
17421         var dow = {
17422             tag: 'tr',
17423             cn: [
17424                 
17425             ]
17426         };
17427         
17428         if(this.calendarWeeks){
17429             dow.cn.push({
17430                 tag: 'th',
17431                 cls: 'cw',
17432                 html: '&nbsp;'
17433             })
17434         }
17435         
17436         while (dowCnt < this.weekStart + 7) {
17437             dow.cn.push({
17438                 tag: 'th',
17439                 cls: 'dow',
17440                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17441             });
17442         }
17443         
17444         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17445     },
17446     
17447     fillMonths: function()
17448     {    
17449         var i = 0;
17450         var months = this.picker().select('>.datepicker-months td', true).first();
17451         
17452         months.dom.innerHTML = '';
17453         
17454         while (i < 12) {
17455             var month = {
17456                 tag: 'span',
17457                 cls: 'month',
17458                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17459             };
17460             
17461             months.createChild(month);
17462         }
17463         
17464     },
17465     
17466     update: function()
17467     {
17468         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;
17469         
17470         if (this.date < this.startDate) {
17471             this.viewDate = new Date(this.startDate);
17472         } else if (this.date > this.endDate) {
17473             this.viewDate = new Date(this.endDate);
17474         } else {
17475             this.viewDate = new Date(this.date);
17476         }
17477         
17478         this.fill();
17479     },
17480     
17481     fill: function() 
17482     {
17483         var d = new Date(this.viewDate),
17484                 year = d.getUTCFullYear(),
17485                 month = d.getUTCMonth(),
17486                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17487                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17488                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17489                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17490                 currentDate = this.date && this.date.valueOf(),
17491                 today = this.UTCToday();
17492         
17493         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17494         
17495 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17496         
17497 //        this.picker.select('>tfoot th.today').
17498 //                                              .text(dates[this.language].today)
17499 //                                              .toggle(this.todayBtn !== false);
17500     
17501         this.updateNavArrows();
17502         this.fillMonths();
17503                                                 
17504         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17505         
17506         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17507          
17508         prevMonth.setUTCDate(day);
17509         
17510         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17511         
17512         var nextMonth = new Date(prevMonth);
17513         
17514         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17515         
17516         nextMonth = nextMonth.valueOf();
17517         
17518         var fillMonths = false;
17519         
17520         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17521         
17522         while(prevMonth.valueOf() < nextMonth) {
17523             var clsName = '';
17524             
17525             if (prevMonth.getUTCDay() === this.weekStart) {
17526                 if(fillMonths){
17527                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17528                 }
17529                     
17530                 fillMonths = {
17531                     tag: 'tr',
17532                     cn: []
17533                 };
17534                 
17535                 if(this.calendarWeeks){
17536                     // ISO 8601: First week contains first thursday.
17537                     // ISO also states week starts on Monday, but we can be more abstract here.
17538                     var
17539                     // Start of current week: based on weekstart/current date
17540                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17541                     // Thursday of this week
17542                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17543                     // First Thursday of year, year from thursday
17544                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17545                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17546                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17547                     
17548                     fillMonths.cn.push({
17549                         tag: 'td',
17550                         cls: 'cw',
17551                         html: calWeek
17552                     });
17553                 }
17554             }
17555             
17556             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17557                 clsName += ' old';
17558             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17559                 clsName += ' new';
17560             }
17561             if (this.todayHighlight &&
17562                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17563                 prevMonth.getUTCMonth() == today.getMonth() &&
17564                 prevMonth.getUTCDate() == today.getDate()) {
17565                 clsName += ' today';
17566             }
17567             
17568             if (currentDate && prevMonth.valueOf() === currentDate) {
17569                 clsName += ' active';
17570             }
17571             
17572             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17573                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17574                     clsName += ' disabled';
17575             }
17576             
17577             fillMonths.cn.push({
17578                 tag: 'td',
17579                 cls: 'day ' + clsName,
17580                 html: prevMonth.getDate()
17581             });
17582             
17583             prevMonth.setDate(prevMonth.getDate()+1);
17584         }
17585           
17586         var currentYear = this.date && this.date.getUTCFullYear();
17587         var currentMonth = this.date && this.date.getUTCMonth();
17588         
17589         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17590         
17591         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17592             v.removeClass('active');
17593             
17594             if(currentYear === year && k === currentMonth){
17595                 v.addClass('active');
17596             }
17597             
17598             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17599                 v.addClass('disabled');
17600             }
17601             
17602         });
17603         
17604         
17605         year = parseInt(year/10, 10) * 10;
17606         
17607         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17608         
17609         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17610         
17611         year -= 1;
17612         for (var i = -1; i < 11; i++) {
17613             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17614                 tag: 'span',
17615                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17616                 html: year
17617             });
17618             
17619             year += 1;
17620         }
17621     },
17622     
17623     showMode: function(dir) 
17624     {
17625         if (dir) {
17626             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17627         }
17628         
17629         Roo.each(this.picker().select('>div',true).elements, function(v){
17630             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17631             v.hide();
17632         });
17633         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17634     },
17635     
17636     place: function()
17637     {
17638         if(this.isInline) {
17639             return;
17640         }
17641         
17642         this.picker().removeClass(['bottom', 'top']);
17643         
17644         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17645             /*
17646              * place to the top of element!
17647              *
17648              */
17649             
17650             this.picker().addClass('top');
17651             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17652             
17653             return;
17654         }
17655         
17656         this.picker().addClass('bottom');
17657         
17658         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17659     },
17660     
17661     parseDate : function(value)
17662     {
17663         if(!value || value instanceof Date){
17664             return value;
17665         }
17666         var v = Date.parseDate(value, this.format);
17667         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17668             v = Date.parseDate(value, 'Y-m-d');
17669         }
17670         if(!v && this.altFormats){
17671             if(!this.altFormatsArray){
17672                 this.altFormatsArray = this.altFormats.split("|");
17673             }
17674             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17675                 v = Date.parseDate(value, this.altFormatsArray[i]);
17676             }
17677         }
17678         return v;
17679     },
17680     
17681     formatDate : function(date, fmt)
17682     {   
17683         return (!date || !(date instanceof Date)) ?
17684         date : date.dateFormat(fmt || this.format);
17685     },
17686     
17687     onFocus : function()
17688     {
17689         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17690         this.show();
17691     },
17692     
17693     onBlur : function()
17694     {
17695         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17696         
17697         var d = this.inputEl().getValue();
17698         
17699         this.setValue(d);
17700                 
17701         this.hide();
17702     },
17703     
17704     show : function()
17705     {
17706         this.picker().show();
17707         this.update();
17708         this.place();
17709         
17710         this.fireEvent('show', this, this.date);
17711     },
17712     
17713     hide : function()
17714     {
17715         if(this.isInline) {
17716             return;
17717         }
17718         this.picker().hide();
17719         this.viewMode = this.startViewMode;
17720         this.showMode();
17721         
17722         this.fireEvent('hide', this, this.date);
17723         
17724     },
17725     
17726     onMousedown: function(e)
17727     {
17728         e.stopPropagation();
17729         e.preventDefault();
17730     },
17731     
17732     keyup: function(e)
17733     {
17734         Roo.bootstrap.DateField.superclass.keyup.call(this);
17735         this.update();
17736     },
17737
17738     setValue: function(v)
17739     {
17740         if(this.fireEvent('beforeselect', this, v) !== false){
17741             var d = new Date(this.parseDate(v) ).clearTime();
17742         
17743             if(isNaN(d.getTime())){
17744                 this.date = this.viewDate = '';
17745                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17746                 return;
17747             }
17748
17749             v = this.formatDate(d);
17750
17751             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17752
17753             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17754
17755             this.update();
17756
17757             this.fireEvent('select', this, this.date);
17758         }
17759     },
17760     
17761     getValue: function()
17762     {
17763         return this.formatDate(this.date);
17764     },
17765     
17766     fireKey: function(e)
17767     {
17768         if (!this.picker().isVisible()){
17769             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17770                 this.show();
17771             }
17772             return;
17773         }
17774         
17775         var dateChanged = false,
17776         dir, day, month,
17777         newDate, newViewDate;
17778         
17779         switch(e.keyCode){
17780             case 27: // escape
17781                 this.hide();
17782                 e.preventDefault();
17783                 break;
17784             case 37: // left
17785             case 39: // right
17786                 if (!this.keyboardNavigation) {
17787                     break;
17788                 }
17789                 dir = e.keyCode == 37 ? -1 : 1;
17790                 
17791                 if (e.ctrlKey){
17792                     newDate = this.moveYear(this.date, dir);
17793                     newViewDate = this.moveYear(this.viewDate, dir);
17794                 } else if (e.shiftKey){
17795                     newDate = this.moveMonth(this.date, dir);
17796                     newViewDate = this.moveMonth(this.viewDate, dir);
17797                 } else {
17798                     newDate = new Date(this.date);
17799                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17800                     newViewDate = new Date(this.viewDate);
17801                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17802                 }
17803                 if (this.dateWithinRange(newDate)){
17804                     this.date = newDate;
17805                     this.viewDate = newViewDate;
17806                     this.setValue(this.formatDate(this.date));
17807 //                    this.update();
17808                     e.preventDefault();
17809                     dateChanged = true;
17810                 }
17811                 break;
17812             case 38: // up
17813             case 40: // down
17814                 if (!this.keyboardNavigation) {
17815                     break;
17816                 }
17817                 dir = e.keyCode == 38 ? -1 : 1;
17818                 if (e.ctrlKey){
17819                     newDate = this.moveYear(this.date, dir);
17820                     newViewDate = this.moveYear(this.viewDate, dir);
17821                 } else if (e.shiftKey){
17822                     newDate = this.moveMonth(this.date, dir);
17823                     newViewDate = this.moveMonth(this.viewDate, dir);
17824                 } else {
17825                     newDate = new Date(this.date);
17826                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17827                     newViewDate = new Date(this.viewDate);
17828                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17829                 }
17830                 if (this.dateWithinRange(newDate)){
17831                     this.date = newDate;
17832                     this.viewDate = newViewDate;
17833                     this.setValue(this.formatDate(this.date));
17834 //                    this.update();
17835                     e.preventDefault();
17836                     dateChanged = true;
17837                 }
17838                 break;
17839             case 13: // enter
17840                 this.setValue(this.formatDate(this.date));
17841                 this.hide();
17842                 e.preventDefault();
17843                 break;
17844             case 9: // tab
17845                 this.setValue(this.formatDate(this.date));
17846                 this.hide();
17847                 break;
17848             case 16: // shift
17849             case 17: // ctrl
17850             case 18: // alt
17851                 break;
17852             default :
17853                 this.hide();
17854                 
17855         }
17856     },
17857     
17858     
17859     onClick: function(e) 
17860     {
17861         e.stopPropagation();
17862         e.preventDefault();
17863         
17864         var target = e.getTarget();
17865         
17866         if(target.nodeName.toLowerCase() === 'i'){
17867             target = Roo.get(target).dom.parentNode;
17868         }
17869         
17870         var nodeName = target.nodeName;
17871         var className = target.className;
17872         var html = target.innerHTML;
17873         //Roo.log(nodeName);
17874         
17875         switch(nodeName.toLowerCase()) {
17876             case 'th':
17877                 switch(className) {
17878                     case 'switch':
17879                         this.showMode(1);
17880                         break;
17881                     case 'prev':
17882                     case 'next':
17883                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17884                         switch(this.viewMode){
17885                                 case 0:
17886                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17887                                         break;
17888                                 case 1:
17889                                 case 2:
17890                                         this.viewDate = this.moveYear(this.viewDate, dir);
17891                                         break;
17892                         }
17893                         this.fill();
17894                         break;
17895                     case 'today':
17896                         var date = new Date();
17897                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17898 //                        this.fill()
17899                         this.setValue(this.formatDate(this.date));
17900                         
17901                         this.hide();
17902                         break;
17903                 }
17904                 break;
17905             case 'span':
17906                 if (className.indexOf('disabled') < 0) {
17907                     this.viewDate.setUTCDate(1);
17908                     if (className.indexOf('month') > -1) {
17909                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17910                     } else {
17911                         var year = parseInt(html, 10) || 0;
17912                         this.viewDate.setUTCFullYear(year);
17913                         
17914                     }
17915                     
17916                     if(this.singleMode){
17917                         this.setValue(this.formatDate(this.viewDate));
17918                         this.hide();
17919                         return;
17920                     }
17921                     
17922                     this.showMode(-1);
17923                     this.fill();
17924                 }
17925                 break;
17926                 
17927             case 'td':
17928                 //Roo.log(className);
17929                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17930                     var day = parseInt(html, 10) || 1;
17931                     var year = this.viewDate.getUTCFullYear(),
17932                         month = this.viewDate.getUTCMonth();
17933
17934                     if (className.indexOf('old') > -1) {
17935                         if(month === 0 ){
17936                             month = 11;
17937                             year -= 1;
17938                         }else{
17939                             month -= 1;
17940                         }
17941                     } else if (className.indexOf('new') > -1) {
17942                         if (month == 11) {
17943                             month = 0;
17944                             year += 1;
17945                         } else {
17946                             month += 1;
17947                         }
17948                     }
17949                     //Roo.log([year,month,day]);
17950                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17951                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17952 //                    this.fill();
17953                     //Roo.log(this.formatDate(this.date));
17954                     this.setValue(this.formatDate(this.date));
17955                     this.hide();
17956                 }
17957                 break;
17958         }
17959     },
17960     
17961     setStartDate: function(startDate)
17962     {
17963         this.startDate = startDate || -Infinity;
17964         if (this.startDate !== -Infinity) {
17965             this.startDate = this.parseDate(this.startDate);
17966         }
17967         this.update();
17968         this.updateNavArrows();
17969     },
17970
17971     setEndDate: function(endDate)
17972     {
17973         this.endDate = endDate || Infinity;
17974         if (this.endDate !== Infinity) {
17975             this.endDate = this.parseDate(this.endDate);
17976         }
17977         this.update();
17978         this.updateNavArrows();
17979     },
17980     
17981     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17982     {
17983         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17984         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17985             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17986         }
17987         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17988             return parseInt(d, 10);
17989         });
17990         this.update();
17991         this.updateNavArrows();
17992     },
17993     
17994     updateNavArrows: function() 
17995     {
17996         if(this.singleMode){
17997             return;
17998         }
17999         
18000         var d = new Date(this.viewDate),
18001         year = d.getUTCFullYear(),
18002         month = d.getUTCMonth();
18003         
18004         Roo.each(this.picker().select('.prev', true).elements, function(v){
18005             v.show();
18006             switch (this.viewMode) {
18007                 case 0:
18008
18009                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18010                         v.hide();
18011                     }
18012                     break;
18013                 case 1:
18014                 case 2:
18015                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18016                         v.hide();
18017                     }
18018                     break;
18019             }
18020         });
18021         
18022         Roo.each(this.picker().select('.next', true).elements, function(v){
18023             v.show();
18024             switch (this.viewMode) {
18025                 case 0:
18026
18027                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18028                         v.hide();
18029                     }
18030                     break;
18031                 case 1:
18032                 case 2:
18033                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18034                         v.hide();
18035                     }
18036                     break;
18037             }
18038         })
18039     },
18040     
18041     moveMonth: function(date, dir)
18042     {
18043         if (!dir) {
18044             return date;
18045         }
18046         var new_date = new Date(date.valueOf()),
18047         day = new_date.getUTCDate(),
18048         month = new_date.getUTCMonth(),
18049         mag = Math.abs(dir),
18050         new_month, test;
18051         dir = dir > 0 ? 1 : -1;
18052         if (mag == 1){
18053             test = dir == -1
18054             // If going back one month, make sure month is not current month
18055             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18056             ? function(){
18057                 return new_date.getUTCMonth() == month;
18058             }
18059             // If going forward one month, make sure month is as expected
18060             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18061             : function(){
18062                 return new_date.getUTCMonth() != new_month;
18063             };
18064             new_month = month + dir;
18065             new_date.setUTCMonth(new_month);
18066             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18067             if (new_month < 0 || new_month > 11) {
18068                 new_month = (new_month + 12) % 12;
18069             }
18070         } else {
18071             // For magnitudes >1, move one month at a time...
18072             for (var i=0; i<mag; i++) {
18073                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18074                 new_date = this.moveMonth(new_date, dir);
18075             }
18076             // ...then reset the day, keeping it in the new month
18077             new_month = new_date.getUTCMonth();
18078             new_date.setUTCDate(day);
18079             test = function(){
18080                 return new_month != new_date.getUTCMonth();
18081             };
18082         }
18083         // Common date-resetting loop -- if date is beyond end of month, make it
18084         // end of month
18085         while (test()){
18086             new_date.setUTCDate(--day);
18087             new_date.setUTCMonth(new_month);
18088         }
18089         return new_date;
18090     },
18091
18092     moveYear: function(date, dir)
18093     {
18094         return this.moveMonth(date, dir*12);
18095     },
18096
18097     dateWithinRange: function(date)
18098     {
18099         return date >= this.startDate && date <= this.endDate;
18100     },
18101
18102     
18103     remove: function() 
18104     {
18105         this.picker().remove();
18106     }
18107    
18108 });
18109
18110 Roo.apply(Roo.bootstrap.DateField,  {
18111     
18112     head : {
18113         tag: 'thead',
18114         cn: [
18115         {
18116             tag: 'tr',
18117             cn: [
18118             {
18119                 tag: 'th',
18120                 cls: 'prev',
18121                 html: '<i class="fa fa-arrow-left"/>'
18122             },
18123             {
18124                 tag: 'th',
18125                 cls: 'switch',
18126                 colspan: '5'
18127             },
18128             {
18129                 tag: 'th',
18130                 cls: 'next',
18131                 html: '<i class="fa fa-arrow-right"/>'
18132             }
18133
18134             ]
18135         }
18136         ]
18137     },
18138     
18139     content : {
18140         tag: 'tbody',
18141         cn: [
18142         {
18143             tag: 'tr',
18144             cn: [
18145             {
18146                 tag: 'td',
18147                 colspan: '7'
18148             }
18149             ]
18150         }
18151         ]
18152     },
18153     
18154     footer : {
18155         tag: 'tfoot',
18156         cn: [
18157         {
18158             tag: 'tr',
18159             cn: [
18160             {
18161                 tag: 'th',
18162                 colspan: '7',
18163                 cls: 'today'
18164             }
18165                     
18166             ]
18167         }
18168         ]
18169     },
18170     
18171     dates:{
18172         en: {
18173             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18174             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18175             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18176             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18177             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18178             today: "Today"
18179         }
18180     },
18181     
18182     modes: [
18183     {
18184         clsName: 'days',
18185         navFnc: 'Month',
18186         navStep: 1
18187     },
18188     {
18189         clsName: 'months',
18190         navFnc: 'FullYear',
18191         navStep: 1
18192     },
18193     {
18194         clsName: 'years',
18195         navFnc: 'FullYear',
18196         navStep: 10
18197     }]
18198 });
18199
18200 Roo.apply(Roo.bootstrap.DateField,  {
18201   
18202     template : {
18203         tag: 'div',
18204         cls: 'datepicker dropdown-menu roo-dynamic',
18205         cn: [
18206         {
18207             tag: 'div',
18208             cls: 'datepicker-days',
18209             cn: [
18210             {
18211                 tag: 'table',
18212                 cls: 'table-condensed',
18213                 cn:[
18214                 Roo.bootstrap.DateField.head,
18215                 {
18216                     tag: 'tbody'
18217                 },
18218                 Roo.bootstrap.DateField.footer
18219                 ]
18220             }
18221             ]
18222         },
18223         {
18224             tag: 'div',
18225             cls: 'datepicker-months',
18226             cn: [
18227             {
18228                 tag: 'table',
18229                 cls: 'table-condensed',
18230                 cn:[
18231                 Roo.bootstrap.DateField.head,
18232                 Roo.bootstrap.DateField.content,
18233                 Roo.bootstrap.DateField.footer
18234                 ]
18235             }
18236             ]
18237         },
18238         {
18239             tag: 'div',
18240             cls: 'datepicker-years',
18241             cn: [
18242             {
18243                 tag: 'table',
18244                 cls: 'table-condensed',
18245                 cn:[
18246                 Roo.bootstrap.DateField.head,
18247                 Roo.bootstrap.DateField.content,
18248                 Roo.bootstrap.DateField.footer
18249                 ]
18250             }
18251             ]
18252         }
18253         ]
18254     }
18255 });
18256
18257  
18258
18259  /*
18260  * - LGPL
18261  *
18262  * TimeField
18263  * 
18264  */
18265
18266 /**
18267  * @class Roo.bootstrap.TimeField
18268  * @extends Roo.bootstrap.Input
18269  * Bootstrap DateField class
18270  * 
18271  * 
18272  * @constructor
18273  * Create a new TimeField
18274  * @param {Object} config The config object
18275  */
18276
18277 Roo.bootstrap.TimeField = function(config){
18278     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18279     this.addEvents({
18280             /**
18281              * @event show
18282              * Fires when this field show.
18283              * @param {Roo.bootstrap.DateField} thisthis
18284              * @param {Mixed} date The date value
18285              */
18286             show : true,
18287             /**
18288              * @event show
18289              * Fires when this field hide.
18290              * @param {Roo.bootstrap.DateField} this
18291              * @param {Mixed} date The date value
18292              */
18293             hide : true,
18294             /**
18295              * @event select
18296              * Fires when select a date.
18297              * @param {Roo.bootstrap.DateField} this
18298              * @param {Mixed} date The date value
18299              */
18300             select : true
18301         });
18302 };
18303
18304 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18305     
18306     /**
18307      * @cfg {String} format
18308      * The default time format string which can be overriden for localization support.  The format must be
18309      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18310      */
18311     format : "H:i",
18312        
18313     onRender: function(ct, position)
18314     {
18315         
18316         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18317                 
18318         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18319         
18320         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18321         
18322         this.pop = this.picker().select('>.datepicker-time',true).first();
18323         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18324         
18325         this.picker().on('mousedown', this.onMousedown, this);
18326         this.picker().on('click', this.onClick, this);
18327         
18328         this.picker().addClass('datepicker-dropdown');
18329     
18330         this.fillTime();
18331         this.update();
18332             
18333         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18334         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18335         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18336         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18337         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18338         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18339
18340     },
18341     
18342     fireKey: function(e){
18343         if (!this.picker().isVisible()){
18344             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18345                 this.show();
18346             }
18347             return;
18348         }
18349
18350         e.preventDefault();
18351         
18352         switch(e.keyCode){
18353             case 27: // escape
18354                 this.hide();
18355                 break;
18356             case 37: // left
18357             case 39: // right
18358                 this.onTogglePeriod();
18359                 break;
18360             case 38: // up
18361                 this.onIncrementMinutes();
18362                 break;
18363             case 40: // down
18364                 this.onDecrementMinutes();
18365                 break;
18366             case 13: // enter
18367             case 9: // tab
18368                 this.setTime();
18369                 break;
18370         }
18371     },
18372     
18373     onClick: function(e) {
18374         e.stopPropagation();
18375         e.preventDefault();
18376     },
18377     
18378     picker : function()
18379     {
18380         return this.el.select('.datepicker', true).first();
18381     },
18382     
18383     fillTime: function()
18384     {    
18385         var time = this.pop.select('tbody', true).first();
18386         
18387         time.dom.innerHTML = '';
18388         
18389         time.createChild({
18390             tag: 'tr',
18391             cn: [
18392                 {
18393                     tag: 'td',
18394                     cn: [
18395                         {
18396                             tag: 'a',
18397                             href: '#',
18398                             cls: 'btn',
18399                             cn: [
18400                                 {
18401                                     tag: 'span',
18402                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18403                                 }
18404                             ]
18405                         } 
18406                     ]
18407                 },
18408                 {
18409                     tag: 'td',
18410                     cls: 'separator'
18411                 },
18412                 {
18413                     tag: 'td',
18414                     cn: [
18415                         {
18416                             tag: 'a',
18417                             href: '#',
18418                             cls: 'btn',
18419                             cn: [
18420                                 {
18421                                     tag: 'span',
18422                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18423                                 }
18424                             ]
18425                         }
18426                     ]
18427                 },
18428                 {
18429                     tag: 'td',
18430                     cls: 'separator'
18431                 }
18432             ]
18433         });
18434         
18435         time.createChild({
18436             tag: 'tr',
18437             cn: [
18438                 {
18439                     tag: 'td',
18440                     cn: [
18441                         {
18442                             tag: 'span',
18443                             cls: 'timepicker-hour',
18444                             html: '00'
18445                         }  
18446                     ]
18447                 },
18448                 {
18449                     tag: 'td',
18450                     cls: 'separator',
18451                     html: ':'
18452                 },
18453                 {
18454                     tag: 'td',
18455                     cn: [
18456                         {
18457                             tag: 'span',
18458                             cls: 'timepicker-minute',
18459                             html: '00'
18460                         }  
18461                     ]
18462                 },
18463                 {
18464                     tag: 'td',
18465                     cls: 'separator'
18466                 },
18467                 {
18468                     tag: 'td',
18469                     cn: [
18470                         {
18471                             tag: 'button',
18472                             type: 'button',
18473                             cls: 'btn btn-primary period',
18474                             html: 'AM'
18475                             
18476                         }
18477                     ]
18478                 }
18479             ]
18480         });
18481         
18482         time.createChild({
18483             tag: 'tr',
18484             cn: [
18485                 {
18486                     tag: 'td',
18487                     cn: [
18488                         {
18489                             tag: 'a',
18490                             href: '#',
18491                             cls: 'btn',
18492                             cn: [
18493                                 {
18494                                     tag: 'span',
18495                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18496                                 }
18497                             ]
18498                         }
18499                     ]
18500                 },
18501                 {
18502                     tag: 'td',
18503                     cls: 'separator'
18504                 },
18505                 {
18506                     tag: 'td',
18507                     cn: [
18508                         {
18509                             tag: 'a',
18510                             href: '#',
18511                             cls: 'btn',
18512                             cn: [
18513                                 {
18514                                     tag: 'span',
18515                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18516                                 }
18517                             ]
18518                         }
18519                     ]
18520                 },
18521                 {
18522                     tag: 'td',
18523                     cls: 'separator'
18524                 }
18525             ]
18526         });
18527         
18528     },
18529     
18530     update: function()
18531     {
18532         
18533         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18534         
18535         this.fill();
18536     },
18537     
18538     fill: function() 
18539     {
18540         var hours = this.time.getHours();
18541         var minutes = this.time.getMinutes();
18542         var period = 'AM';
18543         
18544         if(hours > 11){
18545             period = 'PM';
18546         }
18547         
18548         if(hours == 0){
18549             hours = 12;
18550         }
18551         
18552         
18553         if(hours > 12){
18554             hours = hours - 12;
18555         }
18556         
18557         if(hours < 10){
18558             hours = '0' + hours;
18559         }
18560         
18561         if(minutes < 10){
18562             minutes = '0' + minutes;
18563         }
18564         
18565         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18566         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18567         this.pop.select('button', true).first().dom.innerHTML = period;
18568         
18569     },
18570     
18571     place: function()
18572     {   
18573         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18574         
18575         var cls = ['bottom'];
18576         
18577         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18578             cls.pop();
18579             cls.push('top');
18580         }
18581         
18582         cls.push('right');
18583         
18584         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18585             cls.pop();
18586             cls.push('left');
18587         }
18588         
18589         this.picker().addClass(cls.join('-'));
18590         
18591         var _this = this;
18592         
18593         Roo.each(cls, function(c){
18594             if(c == 'bottom'){
18595                 _this.picker().setTop(_this.inputEl().getHeight());
18596                 return;
18597             }
18598             if(c == 'top'){
18599                 _this.picker().setTop(0 - _this.picker().getHeight());
18600                 return;
18601             }
18602             
18603             if(c == 'left'){
18604                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18605                 return;
18606             }
18607             if(c == 'right'){
18608                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18609                 return;
18610             }
18611         });
18612         
18613     },
18614   
18615     onFocus : function()
18616     {
18617         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18618         this.show();
18619     },
18620     
18621     onBlur : function()
18622     {
18623         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18624         this.hide();
18625     },
18626     
18627     show : function()
18628     {
18629         this.picker().show();
18630         this.pop.show();
18631         this.update();
18632         this.place();
18633         
18634         this.fireEvent('show', this, this.date);
18635     },
18636     
18637     hide : function()
18638     {
18639         this.picker().hide();
18640         this.pop.hide();
18641         
18642         this.fireEvent('hide', this, this.date);
18643     },
18644     
18645     setTime : function()
18646     {
18647         this.hide();
18648         this.setValue(this.time.format(this.format));
18649         
18650         this.fireEvent('select', this, this.date);
18651         
18652         
18653     },
18654     
18655     onMousedown: function(e){
18656         e.stopPropagation();
18657         e.preventDefault();
18658     },
18659     
18660     onIncrementHours: function()
18661     {
18662         Roo.log('onIncrementHours');
18663         this.time = this.time.add(Date.HOUR, 1);
18664         this.update();
18665         
18666     },
18667     
18668     onDecrementHours: function()
18669     {
18670         Roo.log('onDecrementHours');
18671         this.time = this.time.add(Date.HOUR, -1);
18672         this.update();
18673     },
18674     
18675     onIncrementMinutes: function()
18676     {
18677         Roo.log('onIncrementMinutes');
18678         this.time = this.time.add(Date.MINUTE, 1);
18679         this.update();
18680     },
18681     
18682     onDecrementMinutes: function()
18683     {
18684         Roo.log('onDecrementMinutes');
18685         this.time = this.time.add(Date.MINUTE, -1);
18686         this.update();
18687     },
18688     
18689     onTogglePeriod: function()
18690     {
18691         Roo.log('onTogglePeriod');
18692         this.time = this.time.add(Date.HOUR, 12);
18693         this.update();
18694     }
18695     
18696    
18697 });
18698
18699 Roo.apply(Roo.bootstrap.TimeField,  {
18700     
18701     content : {
18702         tag: 'tbody',
18703         cn: [
18704             {
18705                 tag: 'tr',
18706                 cn: [
18707                 {
18708                     tag: 'td',
18709                     colspan: '7'
18710                 }
18711                 ]
18712             }
18713         ]
18714     },
18715     
18716     footer : {
18717         tag: 'tfoot',
18718         cn: [
18719             {
18720                 tag: 'tr',
18721                 cn: [
18722                 {
18723                     tag: 'th',
18724                     colspan: '7',
18725                     cls: '',
18726                     cn: [
18727                         {
18728                             tag: 'button',
18729                             cls: 'btn btn-info ok',
18730                             html: 'OK'
18731                         }
18732                     ]
18733                 }
18734
18735                 ]
18736             }
18737         ]
18738     }
18739 });
18740
18741 Roo.apply(Roo.bootstrap.TimeField,  {
18742   
18743     template : {
18744         tag: 'div',
18745         cls: 'datepicker dropdown-menu',
18746         cn: [
18747             {
18748                 tag: 'div',
18749                 cls: 'datepicker-time',
18750                 cn: [
18751                 {
18752                     tag: 'table',
18753                     cls: 'table-condensed',
18754                     cn:[
18755                     Roo.bootstrap.TimeField.content,
18756                     Roo.bootstrap.TimeField.footer
18757                     ]
18758                 }
18759                 ]
18760             }
18761         ]
18762     }
18763 });
18764
18765  
18766
18767  /*
18768  * - LGPL
18769  *
18770  * MonthField
18771  * 
18772  */
18773
18774 /**
18775  * @class Roo.bootstrap.MonthField
18776  * @extends Roo.bootstrap.Input
18777  * Bootstrap MonthField class
18778  * 
18779  * @cfg {String} language default en
18780  * 
18781  * @constructor
18782  * Create a new MonthField
18783  * @param {Object} config The config object
18784  */
18785
18786 Roo.bootstrap.MonthField = function(config){
18787     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18788     
18789     this.addEvents({
18790         /**
18791          * @event show
18792          * Fires when this field show.
18793          * @param {Roo.bootstrap.MonthField} this
18794          * @param {Mixed} date The date value
18795          */
18796         show : true,
18797         /**
18798          * @event show
18799          * Fires when this field hide.
18800          * @param {Roo.bootstrap.MonthField} this
18801          * @param {Mixed} date The date value
18802          */
18803         hide : true,
18804         /**
18805          * @event select
18806          * Fires when select a date.
18807          * @param {Roo.bootstrap.MonthField} this
18808          * @param {String} oldvalue The old value
18809          * @param {String} newvalue The new value
18810          */
18811         select : true
18812     });
18813 };
18814
18815 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18816     
18817     onRender: function(ct, position)
18818     {
18819         
18820         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18821         
18822         this.language = this.language || 'en';
18823         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18824         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18825         
18826         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18827         this.isInline = false;
18828         this.isInput = true;
18829         this.component = this.el.select('.add-on', true).first() || false;
18830         this.component = (this.component && this.component.length === 0) ? false : this.component;
18831         this.hasInput = this.component && this.inputEL().length;
18832         
18833         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18834         
18835         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18836         
18837         this.picker().on('mousedown', this.onMousedown, this);
18838         this.picker().on('click', this.onClick, this);
18839         
18840         this.picker().addClass('datepicker-dropdown');
18841         
18842         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18843             v.setStyle('width', '189px');
18844         });
18845         
18846         this.fillMonths();
18847         
18848         this.update();
18849         
18850         if(this.isInline) {
18851             this.show();
18852         }
18853         
18854     },
18855     
18856     setValue: function(v, suppressEvent)
18857     {   
18858         var o = this.getValue();
18859         
18860         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18861         
18862         this.update();
18863
18864         if(suppressEvent !== true){
18865             this.fireEvent('select', this, o, v);
18866         }
18867         
18868     },
18869     
18870     getValue: function()
18871     {
18872         return this.value;
18873     },
18874     
18875     onClick: function(e) 
18876     {
18877         e.stopPropagation();
18878         e.preventDefault();
18879         
18880         var target = e.getTarget();
18881         
18882         if(target.nodeName.toLowerCase() === 'i'){
18883             target = Roo.get(target).dom.parentNode;
18884         }
18885         
18886         var nodeName = target.nodeName;
18887         var className = target.className;
18888         var html = target.innerHTML;
18889         
18890         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18891             return;
18892         }
18893         
18894         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18895         
18896         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18897         
18898         this.hide();
18899                         
18900     },
18901     
18902     picker : function()
18903     {
18904         return this.pickerEl;
18905     },
18906     
18907     fillMonths: function()
18908     {    
18909         var i = 0;
18910         var months = this.picker().select('>.datepicker-months td', true).first();
18911         
18912         months.dom.innerHTML = '';
18913         
18914         while (i < 12) {
18915             var month = {
18916                 tag: 'span',
18917                 cls: 'month',
18918                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18919             };
18920             
18921             months.createChild(month);
18922         }
18923         
18924     },
18925     
18926     update: function()
18927     {
18928         var _this = this;
18929         
18930         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18931             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18932         }
18933         
18934         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18935             e.removeClass('active');
18936             
18937             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18938                 e.addClass('active');
18939             }
18940         })
18941     },
18942     
18943     place: function()
18944     {
18945         if(this.isInline) {
18946             return;
18947         }
18948         
18949         this.picker().removeClass(['bottom', 'top']);
18950         
18951         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18952             /*
18953              * place to the top of element!
18954              *
18955              */
18956             
18957             this.picker().addClass('top');
18958             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18959             
18960             return;
18961         }
18962         
18963         this.picker().addClass('bottom');
18964         
18965         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18966     },
18967     
18968     onFocus : function()
18969     {
18970         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18971         this.show();
18972     },
18973     
18974     onBlur : function()
18975     {
18976         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18977         
18978         var d = this.inputEl().getValue();
18979         
18980         this.setValue(d);
18981                 
18982         this.hide();
18983     },
18984     
18985     show : function()
18986     {
18987         this.picker().show();
18988         this.picker().select('>.datepicker-months', true).first().show();
18989         this.update();
18990         this.place();
18991         
18992         this.fireEvent('show', this, this.date);
18993     },
18994     
18995     hide : function()
18996     {
18997         if(this.isInline) {
18998             return;
18999         }
19000         this.picker().hide();
19001         this.fireEvent('hide', this, this.date);
19002         
19003     },
19004     
19005     onMousedown: function(e)
19006     {
19007         e.stopPropagation();
19008         e.preventDefault();
19009     },
19010     
19011     keyup: function(e)
19012     {
19013         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19014         this.update();
19015     },
19016
19017     fireKey: function(e)
19018     {
19019         if (!this.picker().isVisible()){
19020             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19021                 this.show();
19022             }
19023             return;
19024         }
19025         
19026         var dir;
19027         
19028         switch(e.keyCode){
19029             case 27: // escape
19030                 this.hide();
19031                 e.preventDefault();
19032                 break;
19033             case 37: // left
19034             case 39: // right
19035                 dir = e.keyCode == 37 ? -1 : 1;
19036                 
19037                 this.vIndex = this.vIndex + dir;
19038                 
19039                 if(this.vIndex < 0){
19040                     this.vIndex = 0;
19041                 }
19042                 
19043                 if(this.vIndex > 11){
19044                     this.vIndex = 11;
19045                 }
19046                 
19047                 if(isNaN(this.vIndex)){
19048                     this.vIndex = 0;
19049                 }
19050                 
19051                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19052                 
19053                 break;
19054             case 38: // up
19055             case 40: // down
19056                 
19057                 dir = e.keyCode == 38 ? -1 : 1;
19058                 
19059                 this.vIndex = this.vIndex + dir * 4;
19060                 
19061                 if(this.vIndex < 0){
19062                     this.vIndex = 0;
19063                 }
19064                 
19065                 if(this.vIndex > 11){
19066                     this.vIndex = 11;
19067                 }
19068                 
19069                 if(isNaN(this.vIndex)){
19070                     this.vIndex = 0;
19071                 }
19072                 
19073                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19074                 break;
19075                 
19076             case 13: // enter
19077                 
19078                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19079                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19080                 }
19081                 
19082                 this.hide();
19083                 e.preventDefault();
19084                 break;
19085             case 9: // tab
19086                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19087                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19088                 }
19089                 this.hide();
19090                 break;
19091             case 16: // shift
19092             case 17: // ctrl
19093             case 18: // alt
19094                 break;
19095             default :
19096                 this.hide();
19097                 
19098         }
19099     },
19100     
19101     remove: function() 
19102     {
19103         this.picker().remove();
19104     }
19105    
19106 });
19107
19108 Roo.apply(Roo.bootstrap.MonthField,  {
19109     
19110     content : {
19111         tag: 'tbody',
19112         cn: [
19113         {
19114             tag: 'tr',
19115             cn: [
19116             {
19117                 tag: 'td',
19118                 colspan: '7'
19119             }
19120             ]
19121         }
19122         ]
19123     },
19124     
19125     dates:{
19126         en: {
19127             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19128             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19129         }
19130     }
19131 });
19132
19133 Roo.apply(Roo.bootstrap.MonthField,  {
19134   
19135     template : {
19136         tag: 'div',
19137         cls: 'datepicker dropdown-menu roo-dynamic',
19138         cn: [
19139             {
19140                 tag: 'div',
19141                 cls: 'datepicker-months',
19142                 cn: [
19143                 {
19144                     tag: 'table',
19145                     cls: 'table-condensed',
19146                     cn:[
19147                         Roo.bootstrap.DateField.content
19148                     ]
19149                 }
19150                 ]
19151             }
19152         ]
19153     }
19154 });
19155
19156  
19157
19158  
19159  /*
19160  * - LGPL
19161  *
19162  * CheckBox
19163  * 
19164  */
19165
19166 /**
19167  * @class Roo.bootstrap.CheckBox
19168  * @extends Roo.bootstrap.Input
19169  * Bootstrap CheckBox class
19170  * 
19171  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19172  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19173  * @cfg {String} boxLabel The text that appears beside the checkbox
19174  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19175  * @cfg {Boolean} checked initnal the element
19176  * @cfg {Boolean} inline inline the element (default false)
19177  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19178  * 
19179  * @constructor
19180  * Create a new CheckBox
19181  * @param {Object} config The config object
19182  */
19183
19184 Roo.bootstrap.CheckBox = function(config){
19185     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19186    
19187     this.addEvents({
19188         /**
19189         * @event check
19190         * Fires when the element is checked or unchecked.
19191         * @param {Roo.bootstrap.CheckBox} this This input
19192         * @param {Boolean} checked The new checked value
19193         */
19194        check : true
19195     });
19196     
19197 };
19198
19199 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19200   
19201     inputType: 'checkbox',
19202     inputValue: 1,
19203     valueOff: 0,
19204     boxLabel: false,
19205     checked: false,
19206     weight : false,
19207     inline: false,
19208     
19209     getAutoCreate : function()
19210     {
19211         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19212         
19213         var id = Roo.id();
19214         
19215         var cfg = {};
19216         
19217         cfg.cls = 'form-group ' + this.inputType; //input-group
19218         
19219         if(this.inline){
19220             cfg.cls += ' ' + this.inputType + '-inline';
19221         }
19222         
19223         var input =  {
19224             tag: 'input',
19225             id : id,
19226             type : this.inputType,
19227             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19228             cls : 'roo-' + this.inputType, //'form-box',
19229             placeholder : this.placeholder || ''
19230             
19231         };
19232         
19233         if (this.weight) { // Validity check?
19234             cfg.cls += " " + this.inputType + "-" + this.weight;
19235         }
19236         
19237         if (this.disabled) {
19238             input.disabled=true;
19239         }
19240         
19241         if(this.checked){
19242             input.checked = this.checked;
19243         }
19244         
19245         if (this.name) {
19246             input.name = this.name;
19247         }
19248         
19249         if (this.size) {
19250             input.cls += ' input-' + this.size;
19251         }
19252         
19253         var settings=this;
19254         
19255         ['xs','sm','md','lg'].map(function(size){
19256             if (settings[size]) {
19257                 cfg.cls += ' col-' + size + '-' + settings[size];
19258             }
19259         });
19260         
19261         var inputblock = input;
19262          
19263         if (this.before || this.after) {
19264             
19265             inputblock = {
19266                 cls : 'input-group',
19267                 cn :  [] 
19268             };
19269             
19270             if (this.before) {
19271                 inputblock.cn.push({
19272                     tag :'span',
19273                     cls : 'input-group-addon',
19274                     html : this.before
19275                 });
19276             }
19277             
19278             inputblock.cn.push(input);
19279             
19280             if (this.after) {
19281                 inputblock.cn.push({
19282                     tag :'span',
19283                     cls : 'input-group-addon',
19284                     html : this.after
19285                 });
19286             }
19287             
19288         }
19289         
19290         if (align ==='left' && this.fieldLabel.length) {
19291 //                Roo.log("left and has label");
19292                 cfg.cn = [
19293                     
19294                     {
19295                         tag: 'label',
19296                         'for' :  id,
19297                         cls : 'control-label col-md-' + this.labelWidth,
19298                         html : this.fieldLabel
19299                         
19300                     },
19301                     {
19302                         cls : "col-md-" + (12 - this.labelWidth), 
19303                         cn: [
19304                             inputblock
19305                         ]
19306                     }
19307                     
19308                 ];
19309         } else if ( this.fieldLabel.length) {
19310 //                Roo.log(" label");
19311                 cfg.cn = [
19312                    
19313                     {
19314                         tag: this.boxLabel ? 'span' : 'label',
19315                         'for': id,
19316                         cls: 'control-label box-input-label',
19317                         //cls : 'input-group-addon',
19318                         html : this.fieldLabel
19319                         
19320                     },
19321                     
19322                     inputblock
19323                     
19324                 ];
19325
19326         } else {
19327             
19328 //                Roo.log(" no label && no align");
19329                 cfg.cn = [  inputblock ] ;
19330                 
19331                 
19332         }
19333         
19334         if(this.boxLabel){
19335              var boxLabelCfg = {
19336                 tag: 'label',
19337                 //'for': id, // box label is handled by onclick - so no for...
19338                 cls: 'box-label',
19339                 html: this.boxLabel
19340             };
19341             
19342             if(this.tooltip){
19343                 boxLabelCfg.tooltip = this.tooltip;
19344             }
19345              
19346             cfg.cn.push(boxLabelCfg);
19347         }
19348         
19349         
19350        
19351         return cfg;
19352         
19353     },
19354     
19355     /**
19356      * return the real input element.
19357      */
19358     inputEl: function ()
19359     {
19360         return this.el.select('input.roo-' + this.inputType,true).first();
19361     },
19362     
19363     labelEl: function()
19364     {
19365         return this.el.select('label.control-label',true).first();
19366     },
19367     /* depricated... */
19368     
19369     label: function()
19370     {
19371         return this.labelEl();
19372     },
19373     
19374     boxLabelEl: function()
19375     {
19376         return this.el.select('label.box-label',true).first();
19377     },
19378     
19379     initEvents : function()
19380     {
19381 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19382         
19383         this.inputEl().on('click', this.onClick,  this);
19384         
19385         if (this.boxLabel) { 
19386             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19387         }
19388         
19389         this.startValue = this.getValue();
19390         
19391         if(this.groupId){
19392             Roo.bootstrap.CheckBox.register(this);
19393         }
19394     },
19395     
19396     onClick : function()
19397     {   
19398         this.setChecked(!this.checked);
19399     },
19400     
19401     setChecked : function(state,suppressEvent)
19402     {
19403         this.startValue = this.getValue();
19404         
19405         if(this.inputType == 'radio'){
19406             
19407             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19408                 e.dom.checked = false;
19409             });
19410             
19411             this.inputEl().dom.checked = true;
19412             
19413             this.inputEl().dom.value = this.inputValue;
19414             
19415             if(suppressEvent !== true){
19416                 this.fireEvent('check', this, true);
19417             }
19418             
19419             this.validate();
19420             
19421             return;
19422         }
19423         
19424         this.checked = state;
19425         
19426         this.inputEl().dom.checked = state;
19427         
19428         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19429         
19430         if(suppressEvent !== true){
19431             this.fireEvent('check', this, state);
19432         }
19433         
19434         this.validate();
19435     },
19436     
19437     getValue : function()
19438     {
19439         if(this.inputType == 'radio'){
19440             return this.getGroupValue();
19441         }
19442         
19443         return this.inputEl().getValue();
19444         
19445     },
19446     
19447     getGroupValue : function()
19448     {
19449         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19450             return '';
19451         }
19452         
19453         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19454     },
19455     
19456     setValue : function(v,suppressEvent)
19457     {
19458         if(this.inputType == 'radio'){
19459             this.setGroupValue(v, suppressEvent);
19460             return;
19461         }
19462         
19463         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19464         
19465         this.validate();
19466     },
19467     
19468     setGroupValue : function(v, suppressEvent)
19469     {
19470         this.startValue = this.getValue();
19471         
19472         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19473             e.dom.checked = false;
19474             
19475             if(e.dom.value == v){
19476                 e.dom.checked = true;
19477             }
19478         });
19479         
19480         if(suppressEvent !== true){
19481             this.fireEvent('check', this, true);
19482         }
19483
19484         this.validate();
19485         
19486         return;
19487     },
19488     
19489     validate : function()
19490     {
19491         if(
19492                 this.disabled || 
19493                 (this.inputType == 'radio' && this.validateRadio()) ||
19494                 (this.inputType == 'checkbox' && this.validateCheckbox())
19495         ){
19496             this.markValid();
19497             return true;
19498         }
19499         
19500         this.markInvalid();
19501         return false;
19502     },
19503     
19504     validateRadio : function()
19505     {
19506         var valid = false;
19507         
19508         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19509             if(!e.dom.checked){
19510                 return;
19511             }
19512             
19513             valid = true;
19514             
19515             return false;
19516         });
19517         
19518         return valid;
19519     },
19520     
19521     validateCheckbox : function()
19522     {
19523         if(!this.groupId){
19524             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19525         }
19526         
19527         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19528         
19529         if(!group){
19530             return false;
19531         }
19532         
19533         var r = false;
19534         
19535         for(var i in group){
19536             if(r){
19537                 break;
19538             }
19539             
19540             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19541         }
19542         
19543         return r;
19544     },
19545     
19546     /**
19547      * Mark this field as valid
19548      */
19549     markValid : function()
19550     {
19551         if(this.allowBlank){
19552             return;
19553         }
19554         
19555         var _this = this;
19556         
19557         this.fireEvent('valid', this);
19558         
19559         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19560         
19561         if(this.groupId){
19562             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19563         }
19564         
19565         if(label){
19566             label.markValid();
19567         }
19568         
19569         if(this.inputType == 'radio'){
19570             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19571                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19572                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19573             });
19574             
19575             return;
19576         }
19577         
19578         if(!this.groupId){
19579             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19580             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19581             return;
19582         }
19583         
19584         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19585             
19586         if(!group){
19587             return;
19588         }
19589         
19590         for(var i in group){
19591             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19592             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19593         }
19594     },
19595     
19596      /**
19597      * Mark this field as invalid
19598      * @param {String} msg The validation message
19599      */
19600     markInvalid : function(msg)
19601     {
19602         if(this.allowBlank){
19603             return;
19604         }
19605         
19606         var _this = this;
19607         
19608         this.fireEvent('invalid', this, msg);
19609         
19610         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19611         
19612         if(this.groupId){
19613             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19614         }
19615         
19616         if(label){
19617             label.markInvalid();
19618         }
19619             
19620         if(this.inputType == 'radio'){
19621             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19622                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19623                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19624             });
19625             
19626             return;
19627         }
19628         
19629         if(!this.groupId){
19630             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19631             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19632             return;
19633         }
19634         
19635         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19636         
19637         if(!group){
19638             return;
19639         }
19640         
19641         for(var i in group){
19642             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19643             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19644         }
19645         
19646     }
19647     
19648 });
19649
19650 Roo.apply(Roo.bootstrap.CheckBox, {
19651     
19652     groups: {},
19653     
19654      /**
19655     * register a CheckBox Group
19656     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19657     */
19658     register : function(checkbox)
19659     {
19660         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19661             this.groups[checkbox.groupId] = {};
19662         }
19663         
19664         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19665             return;
19666         }
19667         
19668         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19669         
19670     },
19671     /**
19672     * fetch a CheckBox Group based on the group ID
19673     * @param {string} the group ID
19674     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19675     */
19676     get: function(groupId) {
19677         if (typeof(this.groups[groupId]) == 'undefined') {
19678             return false;
19679         }
19680         
19681         return this.groups[groupId] ;
19682     }
19683     
19684     
19685 });
19686 /*
19687  * - LGPL
19688  *
19689  * Radio
19690  *
19691  *
19692  * not inline
19693  *<div class="radio">
19694   <label>
19695     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19696     Option one is this and that&mdash;be sure to include why it's great
19697   </label>
19698 </div>
19699  *
19700  *
19701  *inline
19702  *<span>
19703  *<label class="radio-inline">fieldLabel</label>
19704  *<label class="radio-inline">
19705   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19706 </label>
19707 <span>
19708  * 
19709  * 
19710  */
19711
19712 /**
19713  * @class Roo.bootstrap.Radio
19714  * @extends Roo.bootstrap.CheckBox
19715  * Bootstrap Radio class
19716
19717  * @constructor
19718  * Create a new Radio
19719  * @param {Object} config The config object
19720  */
19721
19722 Roo.bootstrap.Radio = function(config){
19723     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19724    
19725 };
19726
19727 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19728     
19729     inputType: 'radio',
19730     inputValue: '',
19731     valueOff: '',
19732     
19733     getAutoCreate : function()
19734     {
19735         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19736         align = align || 'left'; // default...
19737         
19738         
19739         
19740         var id = Roo.id();
19741         
19742         var cfg = {
19743                 tag : this.inline ? 'span' : 'div',
19744                 cls : '',
19745                 cn : []
19746         };
19747         
19748         var inline = this.inline ? ' radio-inline' : '';
19749         
19750         var lbl = {
19751                 tag: 'label' ,
19752                 // does not need for, as we wrap the input with it..
19753                 'for' : id,
19754                 cls : 'control-label box-label' + inline,
19755                 cn : []
19756         };
19757         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19758         
19759         var fieldLabel = {
19760             tag: 'label' ,
19761             //cls : 'control-label' + inline,
19762             html : this.fieldLabel,
19763             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19764         };
19765         
19766  
19767         
19768         
19769         var input =  {
19770             tag: 'input',
19771             id : id,
19772             type : this.inputType,
19773             //value : (!this.checked) ? this.valueOff : this.inputValue,
19774             value : this.inputValue,
19775             cls : 'roo-radio',
19776             placeholder : this.placeholder || '' // ?? needed????
19777             
19778         };
19779         if (this.weight) { // Validity check?
19780             input.cls += " radio-" + this.weight;
19781         }
19782         if (this.disabled) {
19783             input.disabled=true;
19784         }
19785         
19786         if(this.checked){
19787             input.checked = this.checked;
19788         }
19789         
19790         if (this.name) {
19791             input.name = this.name;
19792         }
19793         
19794         if (this.size) {
19795             input.cls += ' input-' + this.size;
19796         }
19797         
19798         //?? can span's inline have a width??
19799         
19800         var settings=this;
19801         ['xs','sm','md','lg'].map(function(size){
19802             if (settings[size]) {
19803                 cfg.cls += ' col-' + size + '-' + settings[size];
19804             }
19805         });
19806         
19807         var inputblock = input;
19808         
19809         if (this.before || this.after) {
19810             
19811             inputblock = {
19812                 cls : 'input-group',
19813                 tag : 'span',
19814                 cn :  [] 
19815             };
19816             if (this.before) {
19817                 inputblock.cn.push({
19818                     tag :'span',
19819                     cls : 'input-group-addon',
19820                     html : this.before
19821                 });
19822             }
19823             inputblock.cn.push(input);
19824             if (this.after) {
19825                 inputblock.cn.push({
19826                     tag :'span',
19827                     cls : 'input-group-addon',
19828                     html : this.after
19829                 });
19830             }
19831             
19832         };
19833         
19834         
19835         if (this.fieldLabel && this.fieldLabel.length) {
19836             cfg.cn.push(fieldLabel);
19837         }
19838        
19839         // normal bootstrap puts the input inside the label.
19840         // however with our styled version - it has to go after the input.
19841        
19842         //lbl.cn.push(inputblock);
19843         
19844         var lblwrap =  {
19845             tag: 'span',
19846             cls: 'radio' + inline,
19847             cn: [
19848                 inputblock,
19849                 lbl
19850             ]
19851         };
19852         
19853         cfg.cn.push( lblwrap);
19854         
19855         if(this.boxLabel){
19856             lbl.cn.push({
19857                 tag: 'span',
19858                 html: this.boxLabel
19859             })
19860         }
19861          
19862         
19863         return cfg;
19864         
19865     },
19866     
19867     initEvents : function()
19868     {
19869 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19870         
19871         this.inputEl().on('click', this.onClick,  this);
19872         if (this.boxLabel) {
19873             //Roo.log('find label');
19874             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19875         }
19876         
19877     },
19878     
19879     inputEl: function ()
19880     {
19881         return this.el.select('input.roo-radio',true).first();
19882     },
19883     onClick : function()
19884     {   
19885         Roo.log("click");
19886         this.setChecked(true);
19887     },
19888     
19889     setChecked : function(state,suppressEvent)
19890     {
19891         if(state){
19892             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19893                 v.dom.checked = false;
19894             });
19895         }
19896         Roo.log(this.inputEl().dom);
19897         this.checked = state;
19898         this.inputEl().dom.checked = state;
19899         
19900         if(suppressEvent !== true){
19901             this.fireEvent('check', this, state);
19902         }
19903         
19904         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19905         
19906     },
19907     
19908     getGroupValue : function()
19909     {
19910         var value = '';
19911         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19912             if(v.dom.checked == true){
19913                 value = v.dom.value;
19914             }
19915         });
19916         
19917         return value;
19918     },
19919     
19920     /**
19921      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19922      * @return {Mixed} value The field value
19923      */
19924     getValue : function(){
19925         return this.getGroupValue();
19926     }
19927     
19928 });
19929
19930  
19931 //<script type="text/javascript">
19932
19933 /*
19934  * Based  Ext JS Library 1.1.1
19935  * Copyright(c) 2006-2007, Ext JS, LLC.
19936  * LGPL
19937  *
19938  */
19939  
19940 /**
19941  * @class Roo.HtmlEditorCore
19942  * @extends Roo.Component
19943  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19944  *
19945  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19946  */
19947
19948 Roo.HtmlEditorCore = function(config){
19949     
19950     
19951     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19952     
19953     
19954     this.addEvents({
19955         /**
19956          * @event initialize
19957          * Fires when the editor is fully initialized (including the iframe)
19958          * @param {Roo.HtmlEditorCore} this
19959          */
19960         initialize: true,
19961         /**
19962          * @event activate
19963          * Fires when the editor is first receives the focus. Any insertion must wait
19964          * until after this event.
19965          * @param {Roo.HtmlEditorCore} this
19966          */
19967         activate: true,
19968          /**
19969          * @event beforesync
19970          * Fires before the textarea is updated with content from the editor iframe. Return false
19971          * to cancel the sync.
19972          * @param {Roo.HtmlEditorCore} this
19973          * @param {String} html
19974          */
19975         beforesync: true,
19976          /**
19977          * @event beforepush
19978          * Fires before the iframe editor is updated with content from the textarea. Return false
19979          * to cancel the push.
19980          * @param {Roo.HtmlEditorCore} this
19981          * @param {String} html
19982          */
19983         beforepush: true,
19984          /**
19985          * @event sync
19986          * Fires when the textarea is updated with content from the editor iframe.
19987          * @param {Roo.HtmlEditorCore} this
19988          * @param {String} html
19989          */
19990         sync: true,
19991          /**
19992          * @event push
19993          * Fires when the iframe editor is updated with content from the textarea.
19994          * @param {Roo.HtmlEditorCore} this
19995          * @param {String} html
19996          */
19997         push: true,
19998         
19999         /**
20000          * @event editorevent
20001          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20002          * @param {Roo.HtmlEditorCore} this
20003          */
20004         editorevent: true
20005         
20006     });
20007     
20008     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20009     
20010     // defaults : white / black...
20011     this.applyBlacklists();
20012     
20013     
20014     
20015 };
20016
20017
20018 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20019
20020
20021      /**
20022      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20023      */
20024     
20025     owner : false,
20026     
20027      /**
20028      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20029      *                        Roo.resizable.
20030      */
20031     resizable : false,
20032      /**
20033      * @cfg {Number} height (in pixels)
20034      */   
20035     height: 300,
20036    /**
20037      * @cfg {Number} width (in pixels)
20038      */   
20039     width: 500,
20040     
20041     /**
20042      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20043      * 
20044      */
20045     stylesheets: false,
20046     
20047     // id of frame..
20048     frameId: false,
20049     
20050     // private properties
20051     validationEvent : false,
20052     deferHeight: true,
20053     initialized : false,
20054     activated : false,
20055     sourceEditMode : false,
20056     onFocus : Roo.emptyFn,
20057     iframePad:3,
20058     hideMode:'offsets',
20059     
20060     clearUp: true,
20061     
20062     // blacklist + whitelisted elements..
20063     black: false,
20064     white: false,
20065      
20066     
20067
20068     /**
20069      * Protected method that will not generally be called directly. It
20070      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20071      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20072      */
20073     getDocMarkup : function(){
20074         // body styles..
20075         var st = '';
20076         
20077         // inherit styels from page...?? 
20078         if (this.stylesheets === false) {
20079             
20080             Roo.get(document.head).select('style').each(function(node) {
20081                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20082             });
20083             
20084             Roo.get(document.head).select('link').each(function(node) { 
20085                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20086             });
20087             
20088         } else if (!this.stylesheets.length) {
20089                 // simple..
20090                 st = '<style type="text/css">' +
20091                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20092                    '</style>';
20093         } else { 
20094             
20095         }
20096         
20097         st +=  '<style type="text/css">' +
20098             'IMG { cursor: pointer } ' +
20099         '</style>';
20100
20101         
20102         return '<html><head>' + st  +
20103             //<style type="text/css">' +
20104             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20105             //'</style>' +
20106             ' </head><body class="roo-htmleditor-body"></body></html>';
20107     },
20108
20109     // private
20110     onRender : function(ct, position)
20111     {
20112         var _t = this;
20113         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20114         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20115         
20116         
20117         this.el.dom.style.border = '0 none';
20118         this.el.dom.setAttribute('tabIndex', -1);
20119         this.el.addClass('x-hidden hide');
20120         
20121         
20122         
20123         if(Roo.isIE){ // fix IE 1px bogus margin
20124             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20125         }
20126        
20127         
20128         this.frameId = Roo.id();
20129         
20130          
20131         
20132         var iframe = this.owner.wrap.createChild({
20133             tag: 'iframe',
20134             cls: 'form-control', // bootstrap..
20135             id: this.frameId,
20136             name: this.frameId,
20137             frameBorder : 'no',
20138             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20139         }, this.el
20140         );
20141         
20142         
20143         this.iframe = iframe.dom;
20144
20145          this.assignDocWin();
20146         
20147         this.doc.designMode = 'on';
20148        
20149         this.doc.open();
20150         this.doc.write(this.getDocMarkup());
20151         this.doc.close();
20152
20153         
20154         var task = { // must defer to wait for browser to be ready
20155             run : function(){
20156                 //console.log("run task?" + this.doc.readyState);
20157                 this.assignDocWin();
20158                 if(this.doc.body || this.doc.readyState == 'complete'){
20159                     try {
20160                         this.doc.designMode="on";
20161                     } catch (e) {
20162                         return;
20163                     }
20164                     Roo.TaskMgr.stop(task);
20165                     this.initEditor.defer(10, this);
20166                 }
20167             },
20168             interval : 10,
20169             duration: 10000,
20170             scope: this
20171         };
20172         Roo.TaskMgr.start(task);
20173
20174     },
20175
20176     // private
20177     onResize : function(w, h)
20178     {
20179          Roo.log('resize: ' +w + ',' + h );
20180         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20181         if(!this.iframe){
20182             return;
20183         }
20184         if(typeof w == 'number'){
20185             
20186             this.iframe.style.width = w + 'px';
20187         }
20188         if(typeof h == 'number'){
20189             
20190             this.iframe.style.height = h + 'px';
20191             if(this.doc){
20192                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20193             }
20194         }
20195         
20196     },
20197
20198     /**
20199      * Toggles the editor between standard and source edit mode.
20200      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20201      */
20202     toggleSourceEdit : function(sourceEditMode){
20203         
20204         this.sourceEditMode = sourceEditMode === true;
20205         
20206         if(this.sourceEditMode){
20207  
20208             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20209             
20210         }else{
20211             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20212             //this.iframe.className = '';
20213             this.deferFocus();
20214         }
20215         //this.setSize(this.owner.wrap.getSize());
20216         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20217     },
20218
20219     
20220   
20221
20222     /**
20223      * Protected method that will not generally be called directly. If you need/want
20224      * custom HTML cleanup, this is the method you should override.
20225      * @param {String} html The HTML to be cleaned
20226      * return {String} The cleaned HTML
20227      */
20228     cleanHtml : function(html){
20229         html = String(html);
20230         if(html.length > 5){
20231             if(Roo.isSafari){ // strip safari nonsense
20232                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20233             }
20234         }
20235         if(html == '&nbsp;'){
20236             html = '';
20237         }
20238         return html;
20239     },
20240
20241     /**
20242      * HTML Editor -> Textarea
20243      * Protected method that will not generally be called directly. Syncs the contents
20244      * of the editor iframe with the textarea.
20245      */
20246     syncValue : function(){
20247         if(this.initialized){
20248             var bd = (this.doc.body || this.doc.documentElement);
20249             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20250             var html = bd.innerHTML;
20251             if(Roo.isSafari){
20252                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20253                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20254                 if(m && m[1]){
20255                     html = '<div style="'+m[0]+'">' + html + '</div>';
20256                 }
20257             }
20258             html = this.cleanHtml(html);
20259             // fix up the special chars.. normaly like back quotes in word...
20260             // however we do not want to do this with chinese..
20261             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20262                 var cc = b.charCodeAt();
20263                 if (
20264                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20265                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20266                     (cc >= 0xf900 && cc < 0xfb00 )
20267                 ) {
20268                         return b;
20269                 }
20270                 return "&#"+cc+";" 
20271             });
20272             if(this.owner.fireEvent('beforesync', this, html) !== false){
20273                 this.el.dom.value = html;
20274                 this.owner.fireEvent('sync', this, html);
20275             }
20276         }
20277     },
20278
20279     /**
20280      * Protected method that will not generally be called directly. Pushes the value of the textarea
20281      * into the iframe editor.
20282      */
20283     pushValue : function(){
20284         if(this.initialized){
20285             var v = this.el.dom.value.trim();
20286             
20287 //            if(v.length < 1){
20288 //                v = '&#160;';
20289 //            }
20290             
20291             if(this.owner.fireEvent('beforepush', this, v) !== false){
20292                 var d = (this.doc.body || this.doc.documentElement);
20293                 d.innerHTML = v;
20294                 this.cleanUpPaste();
20295                 this.el.dom.value = d.innerHTML;
20296                 this.owner.fireEvent('push', this, v);
20297             }
20298         }
20299     },
20300
20301     // private
20302     deferFocus : function(){
20303         this.focus.defer(10, this);
20304     },
20305
20306     // doc'ed in Field
20307     focus : function(){
20308         if(this.win && !this.sourceEditMode){
20309             this.win.focus();
20310         }else{
20311             this.el.focus();
20312         }
20313     },
20314     
20315     assignDocWin: function()
20316     {
20317         var iframe = this.iframe;
20318         
20319          if(Roo.isIE){
20320             this.doc = iframe.contentWindow.document;
20321             this.win = iframe.contentWindow;
20322         } else {
20323 //            if (!Roo.get(this.frameId)) {
20324 //                return;
20325 //            }
20326 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20327 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20328             
20329             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20330                 return;
20331             }
20332             
20333             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20334             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20335         }
20336     },
20337     
20338     // private
20339     initEditor : function(){
20340         //console.log("INIT EDITOR");
20341         this.assignDocWin();
20342         
20343         
20344         
20345         this.doc.designMode="on";
20346         this.doc.open();
20347         this.doc.write(this.getDocMarkup());
20348         this.doc.close();
20349         
20350         var dbody = (this.doc.body || this.doc.documentElement);
20351         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20352         // this copies styles from the containing element into thsi one..
20353         // not sure why we need all of this..
20354         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20355         
20356         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20357         //ss['background-attachment'] = 'fixed'; // w3c
20358         dbody.bgProperties = 'fixed'; // ie
20359         //Roo.DomHelper.applyStyles(dbody, ss);
20360         Roo.EventManager.on(this.doc, {
20361             //'mousedown': this.onEditorEvent,
20362             'mouseup': this.onEditorEvent,
20363             'dblclick': this.onEditorEvent,
20364             'click': this.onEditorEvent,
20365             'keyup': this.onEditorEvent,
20366             buffer:100,
20367             scope: this
20368         });
20369         if(Roo.isGecko){
20370             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20371         }
20372         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20373             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20374         }
20375         this.initialized = true;
20376
20377         this.owner.fireEvent('initialize', this);
20378         this.pushValue();
20379     },
20380
20381     // private
20382     onDestroy : function(){
20383         
20384         
20385         
20386         if(this.rendered){
20387             
20388             //for (var i =0; i < this.toolbars.length;i++) {
20389             //    // fixme - ask toolbars for heights?
20390             //    this.toolbars[i].onDestroy();
20391            // }
20392             
20393             //this.wrap.dom.innerHTML = '';
20394             //this.wrap.remove();
20395         }
20396     },
20397
20398     // private
20399     onFirstFocus : function(){
20400         
20401         this.assignDocWin();
20402         
20403         
20404         this.activated = true;
20405          
20406     
20407         if(Roo.isGecko){ // prevent silly gecko errors
20408             this.win.focus();
20409             var s = this.win.getSelection();
20410             if(!s.focusNode || s.focusNode.nodeType != 3){
20411                 var r = s.getRangeAt(0);
20412                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20413                 r.collapse(true);
20414                 this.deferFocus();
20415             }
20416             try{
20417                 this.execCmd('useCSS', true);
20418                 this.execCmd('styleWithCSS', false);
20419             }catch(e){}
20420         }
20421         this.owner.fireEvent('activate', this);
20422     },
20423
20424     // private
20425     adjustFont: function(btn){
20426         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20427         //if(Roo.isSafari){ // safari
20428         //    adjust *= 2;
20429        // }
20430         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20431         if(Roo.isSafari){ // safari
20432             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20433             v =  (v < 10) ? 10 : v;
20434             v =  (v > 48) ? 48 : v;
20435             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20436             
20437         }
20438         
20439         
20440         v = Math.max(1, v+adjust);
20441         
20442         this.execCmd('FontSize', v  );
20443     },
20444
20445     onEditorEvent : function(e)
20446     {
20447         this.owner.fireEvent('editorevent', this, e);
20448       //  this.updateToolbar();
20449         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20450     },
20451
20452     insertTag : function(tg)
20453     {
20454         // could be a bit smarter... -> wrap the current selected tRoo..
20455         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20456             
20457             range = this.createRange(this.getSelection());
20458             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20459             wrappingNode.appendChild(range.extractContents());
20460             range.insertNode(wrappingNode);
20461
20462             return;
20463             
20464             
20465             
20466         }
20467         this.execCmd("formatblock",   tg);
20468         
20469     },
20470     
20471     insertText : function(txt)
20472     {
20473         
20474         
20475         var range = this.createRange();
20476         range.deleteContents();
20477                //alert(Sender.getAttribute('label'));
20478                
20479         range.insertNode(this.doc.createTextNode(txt));
20480     } ,
20481     
20482      
20483
20484     /**
20485      * Executes a Midas editor command on the editor document and performs necessary focus and
20486      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20487      * @param {String} cmd The Midas command
20488      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20489      */
20490     relayCmd : function(cmd, value){
20491         this.win.focus();
20492         this.execCmd(cmd, value);
20493         this.owner.fireEvent('editorevent', this);
20494         //this.updateToolbar();
20495         this.owner.deferFocus();
20496     },
20497
20498     /**
20499      * Executes a Midas editor command directly on the editor document.
20500      * For visual commands, you should use {@link #relayCmd} instead.
20501      * <b>This should only be called after the editor is initialized.</b>
20502      * @param {String} cmd The Midas command
20503      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20504      */
20505     execCmd : function(cmd, value){
20506         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20507         this.syncValue();
20508     },
20509  
20510  
20511    
20512     /**
20513      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20514      * to insert tRoo.
20515      * @param {String} text | dom node.. 
20516      */
20517     insertAtCursor : function(text)
20518     {
20519         
20520         
20521         
20522         if(!this.activated){
20523             return;
20524         }
20525         /*
20526         if(Roo.isIE){
20527             this.win.focus();
20528             var r = this.doc.selection.createRange();
20529             if(r){
20530                 r.collapse(true);
20531                 r.pasteHTML(text);
20532                 this.syncValue();
20533                 this.deferFocus();
20534             
20535             }
20536             return;
20537         }
20538         */
20539         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20540             this.win.focus();
20541             
20542             
20543             // from jquery ui (MIT licenced)
20544             var range, node;
20545             var win = this.win;
20546             
20547             if (win.getSelection && win.getSelection().getRangeAt) {
20548                 range = win.getSelection().getRangeAt(0);
20549                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20550                 range.insertNode(node);
20551             } else if (win.document.selection && win.document.selection.createRange) {
20552                 // no firefox support
20553                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20554                 win.document.selection.createRange().pasteHTML(txt);
20555             } else {
20556                 // no firefox support
20557                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20558                 this.execCmd('InsertHTML', txt);
20559             } 
20560             
20561             this.syncValue();
20562             
20563             this.deferFocus();
20564         }
20565     },
20566  // private
20567     mozKeyPress : function(e){
20568         if(e.ctrlKey){
20569             var c = e.getCharCode(), cmd;
20570           
20571             if(c > 0){
20572                 c = String.fromCharCode(c).toLowerCase();
20573                 switch(c){
20574                     case 'b':
20575                         cmd = 'bold';
20576                         break;
20577                     case 'i':
20578                         cmd = 'italic';
20579                         break;
20580                     
20581                     case 'u':
20582                         cmd = 'underline';
20583                         break;
20584                     
20585                     case 'v':
20586                         this.cleanUpPaste.defer(100, this);
20587                         return;
20588                         
20589                 }
20590                 if(cmd){
20591                     this.win.focus();
20592                     this.execCmd(cmd);
20593                     this.deferFocus();
20594                     e.preventDefault();
20595                 }
20596                 
20597             }
20598         }
20599     },
20600
20601     // private
20602     fixKeys : function(){ // load time branching for fastest keydown performance
20603         if(Roo.isIE){
20604             return function(e){
20605                 var k = e.getKey(), r;
20606                 if(k == e.TAB){
20607                     e.stopEvent();
20608                     r = this.doc.selection.createRange();
20609                     if(r){
20610                         r.collapse(true);
20611                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20612                         this.deferFocus();
20613                     }
20614                     return;
20615                 }
20616                 
20617                 if(k == e.ENTER){
20618                     r = this.doc.selection.createRange();
20619                     if(r){
20620                         var target = r.parentElement();
20621                         if(!target || target.tagName.toLowerCase() != 'li'){
20622                             e.stopEvent();
20623                             r.pasteHTML('<br />');
20624                             r.collapse(false);
20625                             r.select();
20626                         }
20627                     }
20628                 }
20629                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20630                     this.cleanUpPaste.defer(100, this);
20631                     return;
20632                 }
20633                 
20634                 
20635             };
20636         }else if(Roo.isOpera){
20637             return function(e){
20638                 var k = e.getKey();
20639                 if(k == e.TAB){
20640                     e.stopEvent();
20641                     this.win.focus();
20642                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20643                     this.deferFocus();
20644                 }
20645                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20646                     this.cleanUpPaste.defer(100, this);
20647                     return;
20648                 }
20649                 
20650             };
20651         }else if(Roo.isSafari){
20652             return function(e){
20653                 var k = e.getKey();
20654                 
20655                 if(k == e.TAB){
20656                     e.stopEvent();
20657                     this.execCmd('InsertText','\t');
20658                     this.deferFocus();
20659                     return;
20660                 }
20661                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20662                     this.cleanUpPaste.defer(100, this);
20663                     return;
20664                 }
20665                 
20666              };
20667         }
20668     }(),
20669     
20670     getAllAncestors: function()
20671     {
20672         var p = this.getSelectedNode();
20673         var a = [];
20674         if (!p) {
20675             a.push(p); // push blank onto stack..
20676             p = this.getParentElement();
20677         }
20678         
20679         
20680         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20681             a.push(p);
20682             p = p.parentNode;
20683         }
20684         a.push(this.doc.body);
20685         return a;
20686     },
20687     lastSel : false,
20688     lastSelNode : false,
20689     
20690     
20691     getSelection : function() 
20692     {
20693         this.assignDocWin();
20694         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20695     },
20696     
20697     getSelectedNode: function() 
20698     {
20699         // this may only work on Gecko!!!
20700         
20701         // should we cache this!!!!
20702         
20703         
20704         
20705          
20706         var range = this.createRange(this.getSelection()).cloneRange();
20707         
20708         if (Roo.isIE) {
20709             var parent = range.parentElement();
20710             while (true) {
20711                 var testRange = range.duplicate();
20712                 testRange.moveToElementText(parent);
20713                 if (testRange.inRange(range)) {
20714                     break;
20715                 }
20716                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20717                     break;
20718                 }
20719                 parent = parent.parentElement;
20720             }
20721             return parent;
20722         }
20723         
20724         // is ancestor a text element.
20725         var ac =  range.commonAncestorContainer;
20726         if (ac.nodeType == 3) {
20727             ac = ac.parentNode;
20728         }
20729         
20730         var ar = ac.childNodes;
20731          
20732         var nodes = [];
20733         var other_nodes = [];
20734         var has_other_nodes = false;
20735         for (var i=0;i<ar.length;i++) {
20736             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20737                 continue;
20738             }
20739             // fullly contained node.
20740             
20741             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20742                 nodes.push(ar[i]);
20743                 continue;
20744             }
20745             
20746             // probably selected..
20747             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20748                 other_nodes.push(ar[i]);
20749                 continue;
20750             }
20751             // outer..
20752             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20753                 continue;
20754             }
20755             
20756             
20757             has_other_nodes = true;
20758         }
20759         if (!nodes.length && other_nodes.length) {
20760             nodes= other_nodes;
20761         }
20762         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20763             return false;
20764         }
20765         
20766         return nodes[0];
20767     },
20768     createRange: function(sel)
20769     {
20770         // this has strange effects when using with 
20771         // top toolbar - not sure if it's a great idea.
20772         //this.editor.contentWindow.focus();
20773         if (typeof sel != "undefined") {
20774             try {
20775                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20776             } catch(e) {
20777                 return this.doc.createRange();
20778             }
20779         } else {
20780             return this.doc.createRange();
20781         }
20782     },
20783     getParentElement: function()
20784     {
20785         
20786         this.assignDocWin();
20787         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20788         
20789         var range = this.createRange(sel);
20790          
20791         try {
20792             var p = range.commonAncestorContainer;
20793             while (p.nodeType == 3) { // text node
20794                 p = p.parentNode;
20795             }
20796             return p;
20797         } catch (e) {
20798             return null;
20799         }
20800     
20801     },
20802     /***
20803      *
20804      * Range intersection.. the hard stuff...
20805      *  '-1' = before
20806      *  '0' = hits..
20807      *  '1' = after.
20808      *         [ -- selected range --- ]
20809      *   [fail]                        [fail]
20810      *
20811      *    basically..
20812      *      if end is before start or  hits it. fail.
20813      *      if start is after end or hits it fail.
20814      *
20815      *   if either hits (but other is outside. - then it's not 
20816      *   
20817      *    
20818      **/
20819     
20820     
20821     // @see http://www.thismuchiknow.co.uk/?p=64.
20822     rangeIntersectsNode : function(range, node)
20823     {
20824         var nodeRange = node.ownerDocument.createRange();
20825         try {
20826             nodeRange.selectNode(node);
20827         } catch (e) {
20828             nodeRange.selectNodeContents(node);
20829         }
20830     
20831         var rangeStartRange = range.cloneRange();
20832         rangeStartRange.collapse(true);
20833     
20834         var rangeEndRange = range.cloneRange();
20835         rangeEndRange.collapse(false);
20836     
20837         var nodeStartRange = nodeRange.cloneRange();
20838         nodeStartRange.collapse(true);
20839     
20840         var nodeEndRange = nodeRange.cloneRange();
20841         nodeEndRange.collapse(false);
20842     
20843         return rangeStartRange.compareBoundaryPoints(
20844                  Range.START_TO_START, nodeEndRange) == -1 &&
20845                rangeEndRange.compareBoundaryPoints(
20846                  Range.START_TO_START, nodeStartRange) == 1;
20847         
20848          
20849     },
20850     rangeCompareNode : function(range, node)
20851     {
20852         var nodeRange = node.ownerDocument.createRange();
20853         try {
20854             nodeRange.selectNode(node);
20855         } catch (e) {
20856             nodeRange.selectNodeContents(node);
20857         }
20858         
20859         
20860         range.collapse(true);
20861     
20862         nodeRange.collapse(true);
20863      
20864         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20865         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20866          
20867         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20868         
20869         var nodeIsBefore   =  ss == 1;
20870         var nodeIsAfter    = ee == -1;
20871         
20872         if (nodeIsBefore && nodeIsAfter) {
20873             return 0; // outer
20874         }
20875         if (!nodeIsBefore && nodeIsAfter) {
20876             return 1; //right trailed.
20877         }
20878         
20879         if (nodeIsBefore && !nodeIsAfter) {
20880             return 2;  // left trailed.
20881         }
20882         // fully contined.
20883         return 3;
20884     },
20885
20886     // private? - in a new class?
20887     cleanUpPaste :  function()
20888     {
20889         // cleans up the whole document..
20890         Roo.log('cleanuppaste');
20891         
20892         this.cleanUpChildren(this.doc.body);
20893         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20894         if (clean != this.doc.body.innerHTML) {
20895             this.doc.body.innerHTML = clean;
20896         }
20897         
20898     },
20899     
20900     cleanWordChars : function(input) {// change the chars to hex code
20901         var he = Roo.HtmlEditorCore;
20902         
20903         var output = input;
20904         Roo.each(he.swapCodes, function(sw) { 
20905             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20906             
20907             output = output.replace(swapper, sw[1]);
20908         });
20909         
20910         return output;
20911     },
20912     
20913     
20914     cleanUpChildren : function (n)
20915     {
20916         if (!n.childNodes.length) {
20917             return;
20918         }
20919         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20920            this.cleanUpChild(n.childNodes[i]);
20921         }
20922     },
20923     
20924     
20925         
20926     
20927     cleanUpChild : function (node)
20928     {
20929         var ed = this;
20930         //console.log(node);
20931         if (node.nodeName == "#text") {
20932             // clean up silly Windows -- stuff?
20933             return; 
20934         }
20935         if (node.nodeName == "#comment") {
20936             node.parentNode.removeChild(node);
20937             // clean up silly Windows -- stuff?
20938             return; 
20939         }
20940         var lcname = node.tagName.toLowerCase();
20941         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20942         // whitelist of tags..
20943         
20944         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20945             // remove node.
20946             node.parentNode.removeChild(node);
20947             return;
20948             
20949         }
20950         
20951         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20952         
20953         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20954         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20955         
20956         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20957         //    remove_keep_children = true;
20958         //}
20959         
20960         if (remove_keep_children) {
20961             this.cleanUpChildren(node);
20962             // inserts everything just before this node...
20963             while (node.childNodes.length) {
20964                 var cn = node.childNodes[0];
20965                 node.removeChild(cn);
20966                 node.parentNode.insertBefore(cn, node);
20967             }
20968             node.parentNode.removeChild(node);
20969             return;
20970         }
20971         
20972         if (!node.attributes || !node.attributes.length) {
20973             this.cleanUpChildren(node);
20974             return;
20975         }
20976         
20977         function cleanAttr(n,v)
20978         {
20979             
20980             if (v.match(/^\./) || v.match(/^\//)) {
20981                 return;
20982             }
20983             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20984                 return;
20985             }
20986             if (v.match(/^#/)) {
20987                 return;
20988             }
20989 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20990             node.removeAttribute(n);
20991             
20992         }
20993         
20994         var cwhite = this.cwhite;
20995         var cblack = this.cblack;
20996             
20997         function cleanStyle(n,v)
20998         {
20999             if (v.match(/expression/)) { //XSS?? should we even bother..
21000                 node.removeAttribute(n);
21001                 return;
21002             }
21003             
21004             var parts = v.split(/;/);
21005             var clean = [];
21006             
21007             Roo.each(parts, function(p) {
21008                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21009                 if (!p.length) {
21010                     return true;
21011                 }
21012                 var l = p.split(':').shift().replace(/\s+/g,'');
21013                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21014                 
21015                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21016 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21017                     //node.removeAttribute(n);
21018                     return true;
21019                 }
21020                 //Roo.log()
21021                 // only allow 'c whitelisted system attributes'
21022                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21023 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21024                     //node.removeAttribute(n);
21025                     return true;
21026                 }
21027                 
21028                 
21029                  
21030                 
21031                 clean.push(p);
21032                 return true;
21033             });
21034             if (clean.length) { 
21035                 node.setAttribute(n, clean.join(';'));
21036             } else {
21037                 node.removeAttribute(n);
21038             }
21039             
21040         }
21041         
21042         
21043         for (var i = node.attributes.length-1; i > -1 ; i--) {
21044             var a = node.attributes[i];
21045             //console.log(a);
21046             
21047             if (a.name.toLowerCase().substr(0,2)=='on')  {
21048                 node.removeAttribute(a.name);
21049                 continue;
21050             }
21051             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21052                 node.removeAttribute(a.name);
21053                 continue;
21054             }
21055             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21056                 cleanAttr(a.name,a.value); // fixme..
21057                 continue;
21058             }
21059             if (a.name == 'style') {
21060                 cleanStyle(a.name,a.value);
21061                 continue;
21062             }
21063             /// clean up MS crap..
21064             // tecnically this should be a list of valid class'es..
21065             
21066             
21067             if (a.name == 'class') {
21068                 if (a.value.match(/^Mso/)) {
21069                     node.className = '';
21070                 }
21071                 
21072                 if (a.value.match(/body/)) {
21073                     node.className = '';
21074                 }
21075                 continue;
21076             }
21077             
21078             // style cleanup!?
21079             // class cleanup?
21080             
21081         }
21082         
21083         
21084         this.cleanUpChildren(node);
21085         
21086         
21087     },
21088     
21089     /**
21090      * Clean up MS wordisms...
21091      */
21092     cleanWord : function(node)
21093     {
21094         
21095         
21096         if (!node) {
21097             this.cleanWord(this.doc.body);
21098             return;
21099         }
21100         if (node.nodeName == "#text") {
21101             // clean up silly Windows -- stuff?
21102             return; 
21103         }
21104         if (node.nodeName == "#comment") {
21105             node.parentNode.removeChild(node);
21106             // clean up silly Windows -- stuff?
21107             return; 
21108         }
21109         
21110         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21111             node.parentNode.removeChild(node);
21112             return;
21113         }
21114         
21115         // remove - but keep children..
21116         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21117             while (node.childNodes.length) {
21118                 var cn = node.childNodes[0];
21119                 node.removeChild(cn);
21120                 node.parentNode.insertBefore(cn, node);
21121             }
21122             node.parentNode.removeChild(node);
21123             this.iterateChildren(node, this.cleanWord);
21124             return;
21125         }
21126         // clean styles
21127         if (node.className.length) {
21128             
21129             var cn = node.className.split(/\W+/);
21130             var cna = [];
21131             Roo.each(cn, function(cls) {
21132                 if (cls.match(/Mso[a-zA-Z]+/)) {
21133                     return;
21134                 }
21135                 cna.push(cls);
21136             });
21137             node.className = cna.length ? cna.join(' ') : '';
21138             if (!cna.length) {
21139                 node.removeAttribute("class");
21140             }
21141         }
21142         
21143         if (node.hasAttribute("lang")) {
21144             node.removeAttribute("lang");
21145         }
21146         
21147         if (node.hasAttribute("style")) {
21148             
21149             var styles = node.getAttribute("style").split(";");
21150             var nstyle = [];
21151             Roo.each(styles, function(s) {
21152                 if (!s.match(/:/)) {
21153                     return;
21154                 }
21155                 var kv = s.split(":");
21156                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21157                     return;
21158                 }
21159                 // what ever is left... we allow.
21160                 nstyle.push(s);
21161             });
21162             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21163             if (!nstyle.length) {
21164                 node.removeAttribute('style');
21165             }
21166         }
21167         this.iterateChildren(node, this.cleanWord);
21168         
21169         
21170         
21171     },
21172     /**
21173      * iterateChildren of a Node, calling fn each time, using this as the scole..
21174      * @param {DomNode} node node to iterate children of.
21175      * @param {Function} fn method of this class to call on each item.
21176      */
21177     iterateChildren : function(node, fn)
21178     {
21179         if (!node.childNodes.length) {
21180                 return;
21181         }
21182         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21183            fn.call(this, node.childNodes[i])
21184         }
21185     },
21186     
21187     
21188     /**
21189      * cleanTableWidths.
21190      *
21191      * Quite often pasting from word etc.. results in tables with column and widths.
21192      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21193      *
21194      */
21195     cleanTableWidths : function(node)
21196     {
21197          
21198          
21199         if (!node) {
21200             this.cleanTableWidths(this.doc.body);
21201             return;
21202         }
21203         
21204         // ignore list...
21205         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21206             return; 
21207         }
21208         Roo.log(node.tagName);
21209         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21210             this.iterateChildren(node, this.cleanTableWidths);
21211             return;
21212         }
21213         if (node.hasAttribute('width')) {
21214             node.removeAttribute('width');
21215         }
21216         
21217          
21218         if (node.hasAttribute("style")) {
21219             // pretty basic...
21220             
21221             var styles = node.getAttribute("style").split(";");
21222             var nstyle = [];
21223             Roo.each(styles, function(s) {
21224                 if (!s.match(/:/)) {
21225                     return;
21226                 }
21227                 var kv = s.split(":");
21228                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21229                     return;
21230                 }
21231                 // what ever is left... we allow.
21232                 nstyle.push(s);
21233             });
21234             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21235             if (!nstyle.length) {
21236                 node.removeAttribute('style');
21237             }
21238         }
21239         
21240         this.iterateChildren(node, this.cleanTableWidths);
21241         
21242         
21243     },
21244     
21245     
21246     
21247     
21248     domToHTML : function(currentElement, depth, nopadtext) {
21249         
21250         depth = depth || 0;
21251         nopadtext = nopadtext || false;
21252     
21253         if (!currentElement) {
21254             return this.domToHTML(this.doc.body);
21255         }
21256         
21257         //Roo.log(currentElement);
21258         var j;
21259         var allText = false;
21260         var nodeName = currentElement.nodeName;
21261         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21262         
21263         if  (nodeName == '#text') {
21264             
21265             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21266         }
21267         
21268         
21269         var ret = '';
21270         if (nodeName != 'BODY') {
21271              
21272             var i = 0;
21273             // Prints the node tagName, such as <A>, <IMG>, etc
21274             if (tagName) {
21275                 var attr = [];
21276                 for(i = 0; i < currentElement.attributes.length;i++) {
21277                     // quoting?
21278                     var aname = currentElement.attributes.item(i).name;
21279                     if (!currentElement.attributes.item(i).value.length) {
21280                         continue;
21281                     }
21282                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21283                 }
21284                 
21285                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21286             } 
21287             else {
21288                 
21289                 // eack
21290             }
21291         } else {
21292             tagName = false;
21293         }
21294         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21295             return ret;
21296         }
21297         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21298             nopadtext = true;
21299         }
21300         
21301         
21302         // Traverse the tree
21303         i = 0;
21304         var currentElementChild = currentElement.childNodes.item(i);
21305         var allText = true;
21306         var innerHTML  = '';
21307         lastnode = '';
21308         while (currentElementChild) {
21309             // Formatting code (indent the tree so it looks nice on the screen)
21310             var nopad = nopadtext;
21311             if (lastnode == 'SPAN') {
21312                 nopad  = true;
21313             }
21314             // text
21315             if  (currentElementChild.nodeName == '#text') {
21316                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21317                 toadd = nopadtext ? toadd : toadd.trim();
21318                 if (!nopad && toadd.length > 80) {
21319                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21320                 }
21321                 innerHTML  += toadd;
21322                 
21323                 i++;
21324                 currentElementChild = currentElement.childNodes.item(i);
21325                 lastNode = '';
21326                 continue;
21327             }
21328             allText = false;
21329             
21330             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21331                 
21332             // Recursively traverse the tree structure of the child node
21333             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21334             lastnode = currentElementChild.nodeName;
21335             i++;
21336             currentElementChild=currentElement.childNodes.item(i);
21337         }
21338         
21339         ret += innerHTML;
21340         
21341         if (!allText) {
21342                 // The remaining code is mostly for formatting the tree
21343             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21344         }
21345         
21346         
21347         if (tagName) {
21348             ret+= "</"+tagName+">";
21349         }
21350         return ret;
21351         
21352     },
21353         
21354     applyBlacklists : function()
21355     {
21356         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21357         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21358         
21359         this.white = [];
21360         this.black = [];
21361         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21362             if (b.indexOf(tag) > -1) {
21363                 return;
21364             }
21365             this.white.push(tag);
21366             
21367         }, this);
21368         
21369         Roo.each(w, function(tag) {
21370             if (b.indexOf(tag) > -1) {
21371                 return;
21372             }
21373             if (this.white.indexOf(tag) > -1) {
21374                 return;
21375             }
21376             this.white.push(tag);
21377             
21378         }, this);
21379         
21380         
21381         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21382             if (w.indexOf(tag) > -1) {
21383                 return;
21384             }
21385             this.black.push(tag);
21386             
21387         }, this);
21388         
21389         Roo.each(b, function(tag) {
21390             if (w.indexOf(tag) > -1) {
21391                 return;
21392             }
21393             if (this.black.indexOf(tag) > -1) {
21394                 return;
21395             }
21396             this.black.push(tag);
21397             
21398         }, this);
21399         
21400         
21401         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21402         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21403         
21404         this.cwhite = [];
21405         this.cblack = [];
21406         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21407             if (b.indexOf(tag) > -1) {
21408                 return;
21409             }
21410             this.cwhite.push(tag);
21411             
21412         }, this);
21413         
21414         Roo.each(w, function(tag) {
21415             if (b.indexOf(tag) > -1) {
21416                 return;
21417             }
21418             if (this.cwhite.indexOf(tag) > -1) {
21419                 return;
21420             }
21421             this.cwhite.push(tag);
21422             
21423         }, this);
21424         
21425         
21426         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21427             if (w.indexOf(tag) > -1) {
21428                 return;
21429             }
21430             this.cblack.push(tag);
21431             
21432         }, this);
21433         
21434         Roo.each(b, function(tag) {
21435             if (w.indexOf(tag) > -1) {
21436                 return;
21437             }
21438             if (this.cblack.indexOf(tag) > -1) {
21439                 return;
21440             }
21441             this.cblack.push(tag);
21442             
21443         }, this);
21444     },
21445     
21446     setStylesheets : function(stylesheets)
21447     {
21448         if(typeof(stylesheets) == 'string'){
21449             Roo.get(this.iframe.contentDocument.head).createChild({
21450                 tag : 'link',
21451                 rel : 'stylesheet',
21452                 type : 'text/css',
21453                 href : stylesheets
21454             });
21455             
21456             return;
21457         }
21458         var _this = this;
21459      
21460         Roo.each(stylesheets, function(s) {
21461             if(!s.length){
21462                 return;
21463             }
21464             
21465             Roo.get(_this.iframe.contentDocument.head).createChild({
21466                 tag : 'link',
21467                 rel : 'stylesheet',
21468                 type : 'text/css',
21469                 href : s
21470             });
21471         });
21472
21473         
21474     },
21475     
21476     removeStylesheets : function()
21477     {
21478         var _this = this;
21479         
21480         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21481             s.remove();
21482         });
21483     }
21484     
21485     // hide stuff that is not compatible
21486     /**
21487      * @event blur
21488      * @hide
21489      */
21490     /**
21491      * @event change
21492      * @hide
21493      */
21494     /**
21495      * @event focus
21496      * @hide
21497      */
21498     /**
21499      * @event specialkey
21500      * @hide
21501      */
21502     /**
21503      * @cfg {String} fieldClass @hide
21504      */
21505     /**
21506      * @cfg {String} focusClass @hide
21507      */
21508     /**
21509      * @cfg {String} autoCreate @hide
21510      */
21511     /**
21512      * @cfg {String} inputType @hide
21513      */
21514     /**
21515      * @cfg {String} invalidClass @hide
21516      */
21517     /**
21518      * @cfg {String} invalidText @hide
21519      */
21520     /**
21521      * @cfg {String} msgFx @hide
21522      */
21523     /**
21524      * @cfg {String} validateOnBlur @hide
21525      */
21526 });
21527
21528 Roo.HtmlEditorCore.white = [
21529         'area', 'br', 'img', 'input', 'hr', 'wbr',
21530         
21531        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21532        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21533        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21534        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21535        'table',   'ul',         'xmp', 
21536        
21537        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21538       'thead',   'tr', 
21539      
21540       'dir', 'menu', 'ol', 'ul', 'dl',
21541        
21542       'embed',  'object'
21543 ];
21544
21545
21546 Roo.HtmlEditorCore.black = [
21547     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21548         'applet', // 
21549         'base',   'basefont', 'bgsound', 'blink',  'body', 
21550         'frame',  'frameset', 'head',    'html',   'ilayer', 
21551         'iframe', 'layer',  'link',     'meta',    'object',   
21552         'script', 'style' ,'title',  'xml' // clean later..
21553 ];
21554 Roo.HtmlEditorCore.clean = [
21555     'script', 'style', 'title', 'xml'
21556 ];
21557 Roo.HtmlEditorCore.remove = [
21558     'font'
21559 ];
21560 // attributes..
21561
21562 Roo.HtmlEditorCore.ablack = [
21563     'on'
21564 ];
21565     
21566 Roo.HtmlEditorCore.aclean = [ 
21567     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21568 ];
21569
21570 // protocols..
21571 Roo.HtmlEditorCore.pwhite= [
21572         'http',  'https',  'mailto'
21573 ];
21574
21575 // white listed style attributes.
21576 Roo.HtmlEditorCore.cwhite= [
21577       //  'text-align', /// default is to allow most things..
21578       
21579          
21580 //        'font-size'//??
21581 ];
21582
21583 // black listed style attributes.
21584 Roo.HtmlEditorCore.cblack= [
21585       //  'font-size' -- this can be set by the project 
21586 ];
21587
21588
21589 Roo.HtmlEditorCore.swapCodes   =[ 
21590     [    8211, "--" ], 
21591     [    8212, "--" ], 
21592     [    8216,  "'" ],  
21593     [    8217, "'" ],  
21594     [    8220, '"' ],  
21595     [    8221, '"' ],  
21596     [    8226, "*" ],  
21597     [    8230, "..." ]
21598 ]; 
21599
21600     /*
21601  * - LGPL
21602  *
21603  * HtmlEditor
21604  * 
21605  */
21606
21607 /**
21608  * @class Roo.bootstrap.HtmlEditor
21609  * @extends Roo.bootstrap.TextArea
21610  * Bootstrap HtmlEditor class
21611
21612  * @constructor
21613  * Create a new HtmlEditor
21614  * @param {Object} config The config object
21615  */
21616
21617 Roo.bootstrap.HtmlEditor = function(config){
21618     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21619     if (!this.toolbars) {
21620         this.toolbars = [];
21621     }
21622     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21623     this.addEvents({
21624             /**
21625              * @event initialize
21626              * Fires when the editor is fully initialized (including the iframe)
21627              * @param {HtmlEditor} this
21628              */
21629             initialize: true,
21630             /**
21631              * @event activate
21632              * Fires when the editor is first receives the focus. Any insertion must wait
21633              * until after this event.
21634              * @param {HtmlEditor} this
21635              */
21636             activate: true,
21637              /**
21638              * @event beforesync
21639              * Fires before the textarea is updated with content from the editor iframe. Return false
21640              * to cancel the sync.
21641              * @param {HtmlEditor} this
21642              * @param {String} html
21643              */
21644             beforesync: true,
21645              /**
21646              * @event beforepush
21647              * Fires before the iframe editor is updated with content from the textarea. Return false
21648              * to cancel the push.
21649              * @param {HtmlEditor} this
21650              * @param {String} html
21651              */
21652             beforepush: true,
21653              /**
21654              * @event sync
21655              * Fires when the textarea is updated with content from the editor iframe.
21656              * @param {HtmlEditor} this
21657              * @param {String} html
21658              */
21659             sync: true,
21660              /**
21661              * @event push
21662              * Fires when the iframe editor is updated with content from the textarea.
21663              * @param {HtmlEditor} this
21664              * @param {String} html
21665              */
21666             push: true,
21667              /**
21668              * @event editmodechange
21669              * Fires when the editor switches edit modes
21670              * @param {HtmlEditor} this
21671              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21672              */
21673             editmodechange: true,
21674             /**
21675              * @event editorevent
21676              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21677              * @param {HtmlEditor} this
21678              */
21679             editorevent: true,
21680             /**
21681              * @event firstfocus
21682              * Fires when on first focus - needed by toolbars..
21683              * @param {HtmlEditor} this
21684              */
21685             firstfocus: true,
21686             /**
21687              * @event autosave
21688              * Auto save the htmlEditor value as a file into Events
21689              * @param {HtmlEditor} this
21690              */
21691             autosave: true,
21692             /**
21693              * @event savedpreview
21694              * preview the saved version of htmlEditor
21695              * @param {HtmlEditor} this
21696              */
21697             savedpreview: true
21698         });
21699 };
21700
21701
21702 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21703     
21704     
21705       /**
21706      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21707      */
21708     toolbars : false,
21709    
21710      /**
21711      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21712      *                        Roo.resizable.
21713      */
21714     resizable : false,
21715      /**
21716      * @cfg {Number} height (in pixels)
21717      */   
21718     height: 300,
21719    /**
21720      * @cfg {Number} width (in pixels)
21721      */   
21722     width: false,
21723     
21724     /**
21725      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21726      * 
21727      */
21728     stylesheets: false,
21729     
21730     // id of frame..
21731     frameId: false,
21732     
21733     // private properties
21734     validationEvent : false,
21735     deferHeight: true,
21736     initialized : false,
21737     activated : false,
21738     
21739     onFocus : Roo.emptyFn,
21740     iframePad:3,
21741     hideMode:'offsets',
21742     
21743     
21744     tbContainer : false,
21745     
21746     toolbarContainer :function() {
21747         return this.wrap.select('.x-html-editor-tb',true).first();
21748     },
21749
21750     /**
21751      * Protected method that will not generally be called directly. It
21752      * is called when the editor creates its toolbar. Override this method if you need to
21753      * add custom toolbar buttons.
21754      * @param {HtmlEditor} editor
21755      */
21756     createToolbar : function(){
21757         
21758         Roo.log("create toolbars");
21759         
21760         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21761         this.toolbars[0].render(this.toolbarContainer());
21762         
21763         return;
21764         
21765 //        if (!editor.toolbars || !editor.toolbars.length) {
21766 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21767 //        }
21768 //        
21769 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21770 //            editor.toolbars[i] = Roo.factory(
21771 //                    typeof(editor.toolbars[i]) == 'string' ?
21772 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21773 //                Roo.bootstrap.HtmlEditor);
21774 //            editor.toolbars[i].init(editor);
21775 //        }
21776     },
21777
21778      
21779     // private
21780     onRender : function(ct, position)
21781     {
21782        // Roo.log("Call onRender: " + this.xtype);
21783         var _t = this;
21784         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21785       
21786         this.wrap = this.inputEl().wrap({
21787             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21788         });
21789         
21790         this.editorcore.onRender(ct, position);
21791          
21792         if (this.resizable) {
21793             this.resizeEl = new Roo.Resizable(this.wrap, {
21794                 pinned : true,
21795                 wrap: true,
21796                 dynamic : true,
21797                 minHeight : this.height,
21798                 height: this.height,
21799                 handles : this.resizable,
21800                 width: this.width,
21801                 listeners : {
21802                     resize : function(r, w, h) {
21803                         _t.onResize(w,h); // -something
21804                     }
21805                 }
21806             });
21807             
21808         }
21809         this.createToolbar(this);
21810        
21811         
21812         if(!this.width && this.resizable){
21813             this.setSize(this.wrap.getSize());
21814         }
21815         if (this.resizeEl) {
21816             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21817             // should trigger onReize..
21818         }
21819         
21820     },
21821
21822     // private
21823     onResize : function(w, h)
21824     {
21825         Roo.log('resize: ' +w + ',' + h );
21826         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21827         var ew = false;
21828         var eh = false;
21829         
21830         if(this.inputEl() ){
21831             if(typeof w == 'number'){
21832                 var aw = w - this.wrap.getFrameWidth('lr');
21833                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21834                 ew = aw;
21835             }
21836             if(typeof h == 'number'){
21837                  var tbh = -11;  // fixme it needs to tool bar size!
21838                 for (var i =0; i < this.toolbars.length;i++) {
21839                     // fixme - ask toolbars for heights?
21840                     tbh += this.toolbars[i].el.getHeight();
21841                     //if (this.toolbars[i].footer) {
21842                     //    tbh += this.toolbars[i].footer.el.getHeight();
21843                     //}
21844                 }
21845               
21846                 
21847                 
21848                 
21849                 
21850                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21851                 ah -= 5; // knock a few pixes off for look..
21852                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21853                 var eh = ah;
21854             }
21855         }
21856         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21857         this.editorcore.onResize(ew,eh);
21858         
21859     },
21860
21861     /**
21862      * Toggles the editor between standard and source edit mode.
21863      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21864      */
21865     toggleSourceEdit : function(sourceEditMode)
21866     {
21867         this.editorcore.toggleSourceEdit(sourceEditMode);
21868         
21869         if(this.editorcore.sourceEditMode){
21870             Roo.log('editor - showing textarea');
21871             
21872 //            Roo.log('in');
21873 //            Roo.log(this.syncValue());
21874             this.syncValue();
21875             this.inputEl().removeClass(['hide', 'x-hidden']);
21876             this.inputEl().dom.removeAttribute('tabIndex');
21877             this.inputEl().focus();
21878         }else{
21879             Roo.log('editor - hiding textarea');
21880 //            Roo.log('out')
21881 //            Roo.log(this.pushValue()); 
21882             this.pushValue();
21883             
21884             this.inputEl().addClass(['hide', 'x-hidden']);
21885             this.inputEl().dom.setAttribute('tabIndex', -1);
21886             //this.deferFocus();
21887         }
21888          
21889         if(this.resizable){
21890             this.setSize(this.wrap.getSize());
21891         }
21892         
21893         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21894     },
21895  
21896     // private (for BoxComponent)
21897     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21898
21899     // private (for BoxComponent)
21900     getResizeEl : function(){
21901         return this.wrap;
21902     },
21903
21904     // private (for BoxComponent)
21905     getPositionEl : function(){
21906         return this.wrap;
21907     },
21908
21909     // private
21910     initEvents : function(){
21911         this.originalValue = this.getValue();
21912     },
21913
21914 //    /**
21915 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21916 //     * @method
21917 //     */
21918 //    markInvalid : Roo.emptyFn,
21919 //    /**
21920 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21921 //     * @method
21922 //     */
21923 //    clearInvalid : Roo.emptyFn,
21924
21925     setValue : function(v){
21926         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21927         this.editorcore.pushValue();
21928     },
21929
21930      
21931     // private
21932     deferFocus : function(){
21933         this.focus.defer(10, this);
21934     },
21935
21936     // doc'ed in Field
21937     focus : function(){
21938         this.editorcore.focus();
21939         
21940     },
21941       
21942
21943     // private
21944     onDestroy : function(){
21945         
21946         
21947         
21948         if(this.rendered){
21949             
21950             for (var i =0; i < this.toolbars.length;i++) {
21951                 // fixme - ask toolbars for heights?
21952                 this.toolbars[i].onDestroy();
21953             }
21954             
21955             this.wrap.dom.innerHTML = '';
21956             this.wrap.remove();
21957         }
21958     },
21959
21960     // private
21961     onFirstFocus : function(){
21962         //Roo.log("onFirstFocus");
21963         this.editorcore.onFirstFocus();
21964          for (var i =0; i < this.toolbars.length;i++) {
21965             this.toolbars[i].onFirstFocus();
21966         }
21967         
21968     },
21969     
21970     // private
21971     syncValue : function()
21972     {   
21973         this.editorcore.syncValue();
21974     },
21975     
21976     pushValue : function()
21977     {   
21978         this.editorcore.pushValue();
21979     }
21980      
21981     
21982     // hide stuff that is not compatible
21983     /**
21984      * @event blur
21985      * @hide
21986      */
21987     /**
21988      * @event change
21989      * @hide
21990      */
21991     /**
21992      * @event focus
21993      * @hide
21994      */
21995     /**
21996      * @event specialkey
21997      * @hide
21998      */
21999     /**
22000      * @cfg {String} fieldClass @hide
22001      */
22002     /**
22003      * @cfg {String} focusClass @hide
22004      */
22005     /**
22006      * @cfg {String} autoCreate @hide
22007      */
22008     /**
22009      * @cfg {String} inputType @hide
22010      */
22011     /**
22012      * @cfg {String} invalidClass @hide
22013      */
22014     /**
22015      * @cfg {String} invalidText @hide
22016      */
22017     /**
22018      * @cfg {String} msgFx @hide
22019      */
22020     /**
22021      * @cfg {String} validateOnBlur @hide
22022      */
22023 });
22024  
22025     
22026    
22027    
22028    
22029       
22030 Roo.namespace('Roo.bootstrap.htmleditor');
22031 /**
22032  * @class Roo.bootstrap.HtmlEditorToolbar1
22033  * Basic Toolbar
22034  * 
22035  * Usage:
22036  *
22037  new Roo.bootstrap.HtmlEditor({
22038     ....
22039     toolbars : [
22040         new Roo.bootstrap.HtmlEditorToolbar1({
22041             disable : { fonts: 1 , format: 1, ..., ... , ...],
22042             btns : [ .... ]
22043         })
22044     }
22045      
22046  * 
22047  * @cfg {Object} disable List of elements to disable..
22048  * @cfg {Array} btns List of additional buttons.
22049  * 
22050  * 
22051  * NEEDS Extra CSS? 
22052  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22053  */
22054  
22055 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22056 {
22057     
22058     Roo.apply(this, config);
22059     
22060     // default disabled, based on 'good practice'..
22061     this.disable = this.disable || {};
22062     Roo.applyIf(this.disable, {
22063         fontSize : true,
22064         colors : true,
22065         specialElements : true
22066     });
22067     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22068     
22069     this.editor = config.editor;
22070     this.editorcore = config.editor.editorcore;
22071     
22072     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22073     
22074     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22075     // dont call parent... till later.
22076 }
22077 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22078      
22079     bar : true,
22080     
22081     editor : false,
22082     editorcore : false,
22083     
22084     
22085     formats : [
22086         "p" ,  
22087         "h1","h2","h3","h4","h5","h6", 
22088         "pre", "code", 
22089         "abbr", "acronym", "address", "cite", "samp", "var",
22090         'div','span'
22091     ],
22092     
22093     onRender : function(ct, position)
22094     {
22095        // Roo.log("Call onRender: " + this.xtype);
22096         
22097        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22098        Roo.log(this.el);
22099        this.el.dom.style.marginBottom = '0';
22100        var _this = this;
22101        var editorcore = this.editorcore;
22102        var editor= this.editor;
22103        
22104        var children = [];
22105        var btn = function(id,cmd , toggle, handler){
22106        
22107             var  event = toggle ? 'toggle' : 'click';
22108        
22109             var a = {
22110                 size : 'sm',
22111                 xtype: 'Button',
22112                 xns: Roo.bootstrap,
22113                 glyphicon : id,
22114                 cmd : id || cmd,
22115                 enableToggle:toggle !== false,
22116                 //html : 'submit'
22117                 pressed : toggle ? false : null,
22118                 listeners : {}
22119             };
22120             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22121                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22122             };
22123             children.push(a);
22124             return a;
22125        }
22126         
22127         var style = {
22128                 xtype: 'Button',
22129                 size : 'sm',
22130                 xns: Roo.bootstrap,
22131                 glyphicon : 'font',
22132                 //html : 'submit'
22133                 menu : {
22134                     xtype: 'Menu',
22135                     xns: Roo.bootstrap,
22136                     items:  []
22137                 }
22138         };
22139         Roo.each(this.formats, function(f) {
22140             style.menu.items.push({
22141                 xtype :'MenuItem',
22142                 xns: Roo.bootstrap,
22143                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22144                 tagname : f,
22145                 listeners : {
22146                     click : function()
22147                     {
22148                         editorcore.insertTag(this.tagname);
22149                         editor.focus();
22150                     }
22151                 }
22152                 
22153             });
22154         });
22155          children.push(style);   
22156             
22157             
22158         btn('bold',false,true);
22159         btn('italic',false,true);
22160         btn('align-left', 'justifyleft',true);
22161         btn('align-center', 'justifycenter',true);
22162         btn('align-right' , 'justifyright',true);
22163         btn('link', false, false, function(btn) {
22164             //Roo.log("create link?");
22165             var url = prompt(this.createLinkText, this.defaultLinkValue);
22166             if(url && url != 'http:/'+'/'){
22167                 this.editorcore.relayCmd('createlink', url);
22168             }
22169         }),
22170         btn('list','insertunorderedlist',true);
22171         btn('pencil', false,true, function(btn){
22172                 Roo.log(this);
22173                 
22174                 this.toggleSourceEdit(btn.pressed);
22175         });
22176         /*
22177         var cog = {
22178                 xtype: 'Button',
22179                 size : 'sm',
22180                 xns: Roo.bootstrap,
22181                 glyphicon : 'cog',
22182                 //html : 'submit'
22183                 menu : {
22184                     xtype: 'Menu',
22185                     xns: Roo.bootstrap,
22186                     items:  []
22187                 }
22188         };
22189         
22190         cog.menu.items.push({
22191             xtype :'MenuItem',
22192             xns: Roo.bootstrap,
22193             html : Clean styles,
22194             tagname : f,
22195             listeners : {
22196                 click : function()
22197                 {
22198                     editorcore.insertTag(this.tagname);
22199                     editor.focus();
22200                 }
22201             }
22202             
22203         });
22204        */
22205         
22206          
22207        this.xtype = 'NavSimplebar';
22208         
22209         for(var i=0;i< children.length;i++) {
22210             
22211             this.buttons.add(this.addxtypeChild(children[i]));
22212             
22213         }
22214         
22215         editor.on('editorevent', this.updateToolbar, this);
22216     },
22217     onBtnClick : function(id)
22218     {
22219        this.editorcore.relayCmd(id);
22220        this.editorcore.focus();
22221     },
22222     
22223     /**
22224      * Protected method that will not generally be called directly. It triggers
22225      * a toolbar update by reading the markup state of the current selection in the editor.
22226      */
22227     updateToolbar: function(){
22228
22229         if(!this.editorcore.activated){
22230             this.editor.onFirstFocus(); // is this neeed?
22231             return;
22232         }
22233
22234         var btns = this.buttons; 
22235         var doc = this.editorcore.doc;
22236         btns.get('bold').setActive(doc.queryCommandState('bold'));
22237         btns.get('italic').setActive(doc.queryCommandState('italic'));
22238         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22239         
22240         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22241         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22242         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22243         
22244         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22245         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22246          /*
22247         
22248         var ans = this.editorcore.getAllAncestors();
22249         if (this.formatCombo) {
22250             
22251             
22252             var store = this.formatCombo.store;
22253             this.formatCombo.setValue("");
22254             for (var i =0; i < ans.length;i++) {
22255                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22256                     // select it..
22257                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22258                     break;
22259                 }
22260             }
22261         }
22262         
22263         
22264         
22265         // hides menus... - so this cant be on a menu...
22266         Roo.bootstrap.MenuMgr.hideAll();
22267         */
22268         Roo.bootstrap.MenuMgr.hideAll();
22269         //this.editorsyncValue();
22270     },
22271     onFirstFocus: function() {
22272         this.buttons.each(function(item){
22273            item.enable();
22274         });
22275     },
22276     toggleSourceEdit : function(sourceEditMode){
22277         
22278           
22279         if(sourceEditMode){
22280             Roo.log("disabling buttons");
22281            this.buttons.each( function(item){
22282                 if(item.cmd != 'pencil'){
22283                     item.disable();
22284                 }
22285             });
22286           
22287         }else{
22288             Roo.log("enabling buttons");
22289             if(this.editorcore.initialized){
22290                 this.buttons.each( function(item){
22291                     item.enable();
22292                 });
22293             }
22294             
22295         }
22296         Roo.log("calling toggole on editor");
22297         // tell the editor that it's been pressed..
22298         this.editor.toggleSourceEdit(sourceEditMode);
22299        
22300     }
22301 });
22302
22303
22304
22305
22306
22307 /**
22308  * @class Roo.bootstrap.Table.AbstractSelectionModel
22309  * @extends Roo.util.Observable
22310  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22311  * implemented by descendant classes.  This class should not be directly instantiated.
22312  * @constructor
22313  */
22314 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22315     this.locked = false;
22316     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22317 };
22318
22319
22320 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22321     /** @ignore Called by the grid automatically. Do not call directly. */
22322     init : function(grid){
22323         this.grid = grid;
22324         this.initEvents();
22325     },
22326
22327     /**
22328      * Locks the selections.
22329      */
22330     lock : function(){
22331         this.locked = true;
22332     },
22333
22334     /**
22335      * Unlocks the selections.
22336      */
22337     unlock : function(){
22338         this.locked = false;
22339     },
22340
22341     /**
22342      * Returns true if the selections are locked.
22343      * @return {Boolean}
22344      */
22345     isLocked : function(){
22346         return this.locked;
22347     }
22348 });
22349 /**
22350  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22351  * @class Roo.bootstrap.Table.RowSelectionModel
22352  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22353  * It supports multiple selections and keyboard selection/navigation. 
22354  * @constructor
22355  * @param {Object} config
22356  */
22357
22358 Roo.bootstrap.Table.RowSelectionModel = function(config){
22359     Roo.apply(this, config);
22360     this.selections = new Roo.util.MixedCollection(false, function(o){
22361         return o.id;
22362     });
22363
22364     this.last = false;
22365     this.lastActive = false;
22366
22367     this.addEvents({
22368         /**
22369              * @event selectionchange
22370              * Fires when the selection changes
22371              * @param {SelectionModel} this
22372              */
22373             "selectionchange" : true,
22374         /**
22375              * @event afterselectionchange
22376              * Fires after the selection changes (eg. by key press or clicking)
22377              * @param {SelectionModel} this
22378              */
22379             "afterselectionchange" : true,
22380         /**
22381              * @event beforerowselect
22382              * Fires when a row is selected being selected, return false to cancel.
22383              * @param {SelectionModel} this
22384              * @param {Number} rowIndex The selected index
22385              * @param {Boolean} keepExisting False if other selections will be cleared
22386              */
22387             "beforerowselect" : true,
22388         /**
22389              * @event rowselect
22390              * Fires when a row is selected.
22391              * @param {SelectionModel} this
22392              * @param {Number} rowIndex The selected index
22393              * @param {Roo.data.Record} r The record
22394              */
22395             "rowselect" : true,
22396         /**
22397              * @event rowdeselect
22398              * Fires when a row is deselected.
22399              * @param {SelectionModel} this
22400              * @param {Number} rowIndex The selected index
22401              */
22402         "rowdeselect" : true
22403     });
22404     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22405     this.locked = false;
22406     this.initEvents();
22407 };
22408
22409 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22410     /**
22411      * @cfg {Boolean} singleSelect
22412      * True to allow selection of only one row at a time (defaults to false)
22413      */
22414     singleSelect : false,
22415
22416     // private
22417     initEvents : function()
22418     {
22419
22420         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22421             this.grid.on("mousedown", this.handleMouseDown, this);
22422         }else{ // allow click to work like normal
22423             this.grid.on("rowclick", this.handleDragableRowClick, this);
22424         }
22425
22426         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22427             "up" : function(e){
22428                 if(!e.shiftKey){
22429                     this.selectPrevious(e.shiftKey);
22430                 }else if(this.last !== false && this.lastActive !== false){
22431                     var last = this.last;
22432                     this.selectRange(this.last,  this.lastActive-1);
22433                     this.grid.getView().focusRow(this.lastActive);
22434                     if(last !== false){
22435                         this.last = last;
22436                     }
22437                 }else{
22438                     this.selectFirstRow();
22439                 }
22440                 this.fireEvent("afterselectionchange", this);
22441             },
22442             "down" : function(e){
22443                 if(!e.shiftKey){
22444                     this.selectNext(e.shiftKey);
22445                 }else if(this.last !== false && this.lastActive !== false){
22446                     var last = this.last;
22447                     this.selectRange(this.last,  this.lastActive+1);
22448                     this.grid.getView().focusRow(this.lastActive);
22449                     if(last !== false){
22450                         this.last = last;
22451                     }
22452                 }else{
22453                     this.selectFirstRow();
22454                 }
22455                 this.fireEvent("afterselectionchange", this);
22456             },
22457             scope: this
22458         });
22459
22460         var view = this.grid.view;
22461         view.on("refresh", this.onRefresh, this);
22462         view.on("rowupdated", this.onRowUpdated, this);
22463         view.on("rowremoved", this.onRemove, this);
22464     },
22465
22466     // private
22467     onRefresh : function(){
22468         var ds = this.grid.dataSource, i, v = this.grid.view;
22469         var s = this.selections;
22470         s.each(function(r){
22471             if((i = ds.indexOfId(r.id)) != -1){
22472                 v.onRowSelect(i);
22473             }else{
22474                 s.remove(r);
22475             }
22476         });
22477     },
22478
22479     // private
22480     onRemove : function(v, index, r){
22481         this.selections.remove(r);
22482     },
22483
22484     // private
22485     onRowUpdated : function(v, index, r){
22486         if(this.isSelected(r)){
22487             v.onRowSelect(index);
22488         }
22489     },
22490
22491     /**
22492      * Select records.
22493      * @param {Array} records The records to select
22494      * @param {Boolean} keepExisting (optional) True to keep existing selections
22495      */
22496     selectRecords : function(records, keepExisting){
22497         if(!keepExisting){
22498             this.clearSelections();
22499         }
22500         var ds = this.grid.dataSource;
22501         for(var i = 0, len = records.length; i < len; i++){
22502             this.selectRow(ds.indexOf(records[i]), true);
22503         }
22504     },
22505
22506     /**
22507      * Gets the number of selected rows.
22508      * @return {Number}
22509      */
22510     getCount : function(){
22511         return this.selections.length;
22512     },
22513
22514     /**
22515      * Selects the first row in the grid.
22516      */
22517     selectFirstRow : function(){
22518         this.selectRow(0);
22519     },
22520
22521     /**
22522      * Select the last row.
22523      * @param {Boolean} keepExisting (optional) True to keep existing selections
22524      */
22525     selectLastRow : function(keepExisting){
22526         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22527     },
22528
22529     /**
22530      * Selects the row immediately following the last selected row.
22531      * @param {Boolean} keepExisting (optional) True to keep existing selections
22532      */
22533     selectNext : function(keepExisting){
22534         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22535             this.selectRow(this.last+1, keepExisting);
22536             this.grid.getView().focusRow(this.last);
22537         }
22538     },
22539
22540     /**
22541      * Selects the row that precedes the last selected row.
22542      * @param {Boolean} keepExisting (optional) True to keep existing selections
22543      */
22544     selectPrevious : function(keepExisting){
22545         if(this.last){
22546             this.selectRow(this.last-1, keepExisting);
22547             this.grid.getView().focusRow(this.last);
22548         }
22549     },
22550
22551     /**
22552      * Returns the selected records
22553      * @return {Array} Array of selected records
22554      */
22555     getSelections : function(){
22556         return [].concat(this.selections.items);
22557     },
22558
22559     /**
22560      * Returns the first selected record.
22561      * @return {Record}
22562      */
22563     getSelected : function(){
22564         return this.selections.itemAt(0);
22565     },
22566
22567
22568     /**
22569      * Clears all selections.
22570      */
22571     clearSelections : function(fast){
22572         if(this.locked) {
22573             return;
22574         }
22575         if(fast !== true){
22576             var ds = this.grid.dataSource;
22577             var s = this.selections;
22578             s.each(function(r){
22579                 this.deselectRow(ds.indexOfId(r.id));
22580             }, this);
22581             s.clear();
22582         }else{
22583             this.selections.clear();
22584         }
22585         this.last = false;
22586     },
22587
22588
22589     /**
22590      * Selects all rows.
22591      */
22592     selectAll : function(){
22593         if(this.locked) {
22594             return;
22595         }
22596         this.selections.clear();
22597         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22598             this.selectRow(i, true);
22599         }
22600     },
22601
22602     /**
22603      * Returns True if there is a selection.
22604      * @return {Boolean}
22605      */
22606     hasSelection : function(){
22607         return this.selections.length > 0;
22608     },
22609
22610     /**
22611      * Returns True if the specified row is selected.
22612      * @param {Number/Record} record The record or index of the record to check
22613      * @return {Boolean}
22614      */
22615     isSelected : function(index){
22616         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22617         return (r && this.selections.key(r.id) ? true : false);
22618     },
22619
22620     /**
22621      * Returns True if the specified record id is selected.
22622      * @param {String} id The id of record to check
22623      * @return {Boolean}
22624      */
22625     isIdSelected : function(id){
22626         return (this.selections.key(id) ? true : false);
22627     },
22628
22629     // private
22630     handleMouseDown : function(e, t){
22631         var view = this.grid.getView(), rowIndex;
22632         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22633             return;
22634         };
22635         if(e.shiftKey && this.last !== false){
22636             var last = this.last;
22637             this.selectRange(last, rowIndex, e.ctrlKey);
22638             this.last = last; // reset the last
22639             view.focusRow(rowIndex);
22640         }else{
22641             var isSelected = this.isSelected(rowIndex);
22642             if(e.button !== 0 && isSelected){
22643                 view.focusRow(rowIndex);
22644             }else if(e.ctrlKey && isSelected){
22645                 this.deselectRow(rowIndex);
22646             }else if(!isSelected){
22647                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22648                 view.focusRow(rowIndex);
22649             }
22650         }
22651         this.fireEvent("afterselectionchange", this);
22652     },
22653     // private
22654     handleDragableRowClick :  function(grid, rowIndex, e) 
22655     {
22656         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22657             this.selectRow(rowIndex, false);
22658             grid.view.focusRow(rowIndex);
22659              this.fireEvent("afterselectionchange", this);
22660         }
22661     },
22662     
22663     /**
22664      * Selects multiple rows.
22665      * @param {Array} rows Array of the indexes of the row to select
22666      * @param {Boolean} keepExisting (optional) True to keep existing selections
22667      */
22668     selectRows : function(rows, keepExisting){
22669         if(!keepExisting){
22670             this.clearSelections();
22671         }
22672         for(var i = 0, len = rows.length; i < len; i++){
22673             this.selectRow(rows[i], true);
22674         }
22675     },
22676
22677     /**
22678      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22679      * @param {Number} startRow The index of the first row in the range
22680      * @param {Number} endRow The index of the last row in the range
22681      * @param {Boolean} keepExisting (optional) True to retain existing selections
22682      */
22683     selectRange : function(startRow, endRow, keepExisting){
22684         if(this.locked) {
22685             return;
22686         }
22687         if(!keepExisting){
22688             this.clearSelections();
22689         }
22690         if(startRow <= endRow){
22691             for(var i = startRow; i <= endRow; i++){
22692                 this.selectRow(i, true);
22693             }
22694         }else{
22695             for(var i = startRow; i >= endRow; i--){
22696                 this.selectRow(i, true);
22697             }
22698         }
22699     },
22700
22701     /**
22702      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22703      * @param {Number} startRow The index of the first row in the range
22704      * @param {Number} endRow The index of the last row in the range
22705      */
22706     deselectRange : function(startRow, endRow, preventViewNotify){
22707         if(this.locked) {
22708             return;
22709         }
22710         for(var i = startRow; i <= endRow; i++){
22711             this.deselectRow(i, preventViewNotify);
22712         }
22713     },
22714
22715     /**
22716      * Selects a row.
22717      * @param {Number} row The index of the row to select
22718      * @param {Boolean} keepExisting (optional) True to keep existing selections
22719      */
22720     selectRow : function(index, keepExisting, preventViewNotify){
22721         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22722             return;
22723         }
22724         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22725             if(!keepExisting || this.singleSelect){
22726                 this.clearSelections();
22727             }
22728             var r = this.grid.dataSource.getAt(index);
22729             this.selections.add(r);
22730             this.last = this.lastActive = index;
22731             if(!preventViewNotify){
22732                 this.grid.getView().onRowSelect(index);
22733             }
22734             this.fireEvent("rowselect", this, index, r);
22735             this.fireEvent("selectionchange", this);
22736         }
22737     },
22738
22739     /**
22740      * Deselects a row.
22741      * @param {Number} row The index of the row to deselect
22742      */
22743     deselectRow : function(index, preventViewNotify){
22744         if(this.locked) {
22745             return;
22746         }
22747         if(this.last == index){
22748             this.last = false;
22749         }
22750         if(this.lastActive == index){
22751             this.lastActive = false;
22752         }
22753         var r = this.grid.dataSource.getAt(index);
22754         this.selections.remove(r);
22755         if(!preventViewNotify){
22756             this.grid.getView().onRowDeselect(index);
22757         }
22758         this.fireEvent("rowdeselect", this, index);
22759         this.fireEvent("selectionchange", this);
22760     },
22761
22762     // private
22763     restoreLast : function(){
22764         if(this._last){
22765             this.last = this._last;
22766         }
22767     },
22768
22769     // private
22770     acceptsNav : function(row, col, cm){
22771         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22772     },
22773
22774     // private
22775     onEditorKey : function(field, e){
22776         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22777         if(k == e.TAB){
22778             e.stopEvent();
22779             ed.completeEdit();
22780             if(e.shiftKey){
22781                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22782             }else{
22783                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22784             }
22785         }else if(k == e.ENTER && !e.ctrlKey){
22786             e.stopEvent();
22787             ed.completeEdit();
22788             if(e.shiftKey){
22789                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22790             }else{
22791                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22792             }
22793         }else if(k == e.ESC){
22794             ed.cancelEdit();
22795         }
22796         if(newCell){
22797             g.startEditing(newCell[0], newCell[1]);
22798         }
22799     }
22800 });/*
22801  * Based on:
22802  * Ext JS Library 1.1.1
22803  * Copyright(c) 2006-2007, Ext JS, LLC.
22804  *
22805  * Originally Released Under LGPL - original licence link has changed is not relivant.
22806  *
22807  * Fork - LGPL
22808  * <script type="text/javascript">
22809  */
22810  
22811 /**
22812  * @class Roo.bootstrap.PagingToolbar
22813  * @extends Roo.bootstrap.NavSimplebar
22814  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22815  * @constructor
22816  * Create a new PagingToolbar
22817  * @param {Object} config The config object
22818  * @param {Roo.data.Store} store
22819  */
22820 Roo.bootstrap.PagingToolbar = function(config)
22821 {
22822     // old args format still supported... - xtype is prefered..
22823         // created from xtype...
22824     
22825     this.ds = config.dataSource;
22826     
22827     if (config.store && !this.ds) {
22828         this.store= Roo.factory(config.store, Roo.data);
22829         this.ds = this.store;
22830         this.ds.xmodule = this.xmodule || false;
22831     }
22832     
22833     this.toolbarItems = [];
22834     if (config.items) {
22835         this.toolbarItems = config.items;
22836     }
22837     
22838     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22839     
22840     this.cursor = 0;
22841     
22842     if (this.ds) { 
22843         this.bind(this.ds);
22844     }
22845     
22846     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22847     
22848 };
22849
22850 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22851     /**
22852      * @cfg {Roo.data.Store} dataSource
22853      * The underlying data store providing the paged data
22854      */
22855     /**
22856      * @cfg {String/HTMLElement/Element} container
22857      * container The id or element that will contain the toolbar
22858      */
22859     /**
22860      * @cfg {Boolean} displayInfo
22861      * True to display the displayMsg (defaults to false)
22862      */
22863     /**
22864      * @cfg {Number} pageSize
22865      * The number of records to display per page (defaults to 20)
22866      */
22867     pageSize: 20,
22868     /**
22869      * @cfg {String} displayMsg
22870      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22871      */
22872     displayMsg : 'Displaying {0} - {1} of {2}',
22873     /**
22874      * @cfg {String} emptyMsg
22875      * The message to display when no records are found (defaults to "No data to display")
22876      */
22877     emptyMsg : 'No data to display',
22878     /**
22879      * Customizable piece of the default paging text (defaults to "Page")
22880      * @type String
22881      */
22882     beforePageText : "Page",
22883     /**
22884      * Customizable piece of the default paging text (defaults to "of %0")
22885      * @type String
22886      */
22887     afterPageText : "of {0}",
22888     /**
22889      * Customizable piece of the default paging text (defaults to "First Page")
22890      * @type String
22891      */
22892     firstText : "First Page",
22893     /**
22894      * Customizable piece of the default paging text (defaults to "Previous Page")
22895      * @type String
22896      */
22897     prevText : "Previous Page",
22898     /**
22899      * Customizable piece of the default paging text (defaults to "Next Page")
22900      * @type String
22901      */
22902     nextText : "Next Page",
22903     /**
22904      * Customizable piece of the default paging text (defaults to "Last Page")
22905      * @type String
22906      */
22907     lastText : "Last Page",
22908     /**
22909      * Customizable piece of the default paging text (defaults to "Refresh")
22910      * @type String
22911      */
22912     refreshText : "Refresh",
22913
22914     buttons : false,
22915     // private
22916     onRender : function(ct, position) 
22917     {
22918         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22919         this.navgroup.parentId = this.id;
22920         this.navgroup.onRender(this.el, null);
22921         // add the buttons to the navgroup
22922         
22923         if(this.displayInfo){
22924             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22925             this.displayEl = this.el.select('.x-paging-info', true).first();
22926 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22927 //            this.displayEl = navel.el.select('span',true).first();
22928         }
22929         
22930         var _this = this;
22931         
22932         if(this.buttons){
22933             Roo.each(_this.buttons, function(e){ // this might need to use render????
22934                Roo.factory(e).onRender(_this.el, null);
22935             });
22936         }
22937             
22938         Roo.each(_this.toolbarItems, function(e) {
22939             _this.navgroup.addItem(e);
22940         });
22941         
22942         
22943         this.first = this.navgroup.addItem({
22944             tooltip: this.firstText,
22945             cls: "prev",
22946             icon : 'fa fa-backward',
22947             disabled: true,
22948             preventDefault: true,
22949             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22950         });
22951         
22952         this.prev =  this.navgroup.addItem({
22953             tooltip: this.prevText,
22954             cls: "prev",
22955             icon : 'fa fa-step-backward',
22956             disabled: true,
22957             preventDefault: true,
22958             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22959         });
22960     //this.addSeparator();
22961         
22962         
22963         var field = this.navgroup.addItem( {
22964             tagtype : 'span',
22965             cls : 'x-paging-position',
22966             
22967             html : this.beforePageText  +
22968                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22969                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22970          } ); //?? escaped?
22971         
22972         this.field = field.el.select('input', true).first();
22973         this.field.on("keydown", this.onPagingKeydown, this);
22974         this.field.on("focus", function(){this.dom.select();});
22975     
22976     
22977         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22978         //this.field.setHeight(18);
22979         //this.addSeparator();
22980         this.next = this.navgroup.addItem({
22981             tooltip: this.nextText,
22982             cls: "next",
22983             html : ' <i class="fa fa-step-forward">',
22984             disabled: true,
22985             preventDefault: true,
22986             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22987         });
22988         this.last = this.navgroup.addItem({
22989             tooltip: this.lastText,
22990             icon : 'fa fa-forward',
22991             cls: "next",
22992             disabled: true,
22993             preventDefault: true,
22994             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22995         });
22996     //this.addSeparator();
22997         this.loading = this.navgroup.addItem({
22998             tooltip: this.refreshText,
22999             icon: 'fa fa-refresh',
23000             preventDefault: true,
23001             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23002         });
23003         
23004     },
23005
23006     // private
23007     updateInfo : function(){
23008         if(this.displayEl){
23009             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23010             var msg = count == 0 ?
23011                 this.emptyMsg :
23012                 String.format(
23013                     this.displayMsg,
23014                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23015                 );
23016             this.displayEl.update(msg);
23017         }
23018     },
23019
23020     // private
23021     onLoad : function(ds, r, o){
23022        this.cursor = o.params ? o.params.start : 0;
23023        var d = this.getPageData(),
23024             ap = d.activePage,
23025             ps = d.pages;
23026         
23027        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23028        this.field.dom.value = ap;
23029        this.first.setDisabled(ap == 1);
23030        this.prev.setDisabled(ap == 1);
23031        this.next.setDisabled(ap == ps);
23032        this.last.setDisabled(ap == ps);
23033        this.loading.enable();
23034        this.updateInfo();
23035     },
23036
23037     // private
23038     getPageData : function(){
23039         var total = this.ds.getTotalCount();
23040         return {
23041             total : total,
23042             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23043             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23044         };
23045     },
23046
23047     // private
23048     onLoadError : function(){
23049         this.loading.enable();
23050     },
23051
23052     // private
23053     onPagingKeydown : function(e){
23054         var k = e.getKey();
23055         var d = this.getPageData();
23056         if(k == e.RETURN){
23057             var v = this.field.dom.value, pageNum;
23058             if(!v || isNaN(pageNum = parseInt(v, 10))){
23059                 this.field.dom.value = d.activePage;
23060                 return;
23061             }
23062             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23063             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23064             e.stopEvent();
23065         }
23066         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))
23067         {
23068           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23069           this.field.dom.value = pageNum;
23070           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23071           e.stopEvent();
23072         }
23073         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23074         {
23075           var v = this.field.dom.value, pageNum; 
23076           var increment = (e.shiftKey) ? 10 : 1;
23077           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23078                 increment *= -1;
23079           }
23080           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23081             this.field.dom.value = d.activePage;
23082             return;
23083           }
23084           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23085           {
23086             this.field.dom.value = parseInt(v, 10) + increment;
23087             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23088             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23089           }
23090           e.stopEvent();
23091         }
23092     },
23093
23094     // private
23095     beforeLoad : function(){
23096         if(this.loading){
23097             this.loading.disable();
23098         }
23099     },
23100
23101     // private
23102     onClick : function(which){
23103         
23104         var ds = this.ds;
23105         if (!ds) {
23106             return;
23107         }
23108         
23109         switch(which){
23110             case "first":
23111                 ds.load({params:{start: 0, limit: this.pageSize}});
23112             break;
23113             case "prev":
23114                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23115             break;
23116             case "next":
23117                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23118             break;
23119             case "last":
23120                 var total = ds.getTotalCount();
23121                 var extra = total % this.pageSize;
23122                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23123                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23124             break;
23125             case "refresh":
23126                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23127             break;
23128         }
23129     },
23130
23131     /**
23132      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23133      * @param {Roo.data.Store} store The data store to unbind
23134      */
23135     unbind : function(ds){
23136         ds.un("beforeload", this.beforeLoad, this);
23137         ds.un("load", this.onLoad, this);
23138         ds.un("loadexception", this.onLoadError, this);
23139         ds.un("remove", this.updateInfo, this);
23140         ds.un("add", this.updateInfo, this);
23141         this.ds = undefined;
23142     },
23143
23144     /**
23145      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23146      * @param {Roo.data.Store} store The data store to bind
23147      */
23148     bind : function(ds){
23149         ds.on("beforeload", this.beforeLoad, this);
23150         ds.on("load", this.onLoad, this);
23151         ds.on("loadexception", this.onLoadError, this);
23152         ds.on("remove", this.updateInfo, this);
23153         ds.on("add", this.updateInfo, this);
23154         this.ds = ds;
23155     }
23156 });/*
23157  * - LGPL
23158  *
23159  * element
23160  * 
23161  */
23162
23163 /**
23164  * @class Roo.bootstrap.MessageBar
23165  * @extends Roo.bootstrap.Component
23166  * Bootstrap MessageBar class
23167  * @cfg {String} html contents of the MessageBar
23168  * @cfg {String} weight (info | success | warning | danger) default info
23169  * @cfg {String} beforeClass insert the bar before the given class
23170  * @cfg {Boolean} closable (true | false) default false
23171  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23172  * 
23173  * @constructor
23174  * Create a new Element
23175  * @param {Object} config The config object
23176  */
23177
23178 Roo.bootstrap.MessageBar = function(config){
23179     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23180 };
23181
23182 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23183     
23184     html: '',
23185     weight: 'info',
23186     closable: false,
23187     fixed: false,
23188     beforeClass: 'bootstrap-sticky-wrap',
23189     
23190     getAutoCreate : function(){
23191         
23192         var cfg = {
23193             tag: 'div',
23194             cls: 'alert alert-dismissable alert-' + this.weight,
23195             cn: [
23196                 {
23197                     tag: 'span',
23198                     cls: 'message',
23199                     html: this.html || ''
23200                 }
23201             ]
23202         };
23203         
23204         if(this.fixed){
23205             cfg.cls += ' alert-messages-fixed';
23206         }
23207         
23208         if(this.closable){
23209             cfg.cn.push({
23210                 tag: 'button',
23211                 cls: 'close',
23212                 html: 'x'
23213             });
23214         }
23215         
23216         return cfg;
23217     },
23218     
23219     onRender : function(ct, position)
23220     {
23221         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23222         
23223         if(!this.el){
23224             var cfg = Roo.apply({},  this.getAutoCreate());
23225             cfg.id = Roo.id();
23226             
23227             if (this.cls) {
23228                 cfg.cls += ' ' + this.cls;
23229             }
23230             if (this.style) {
23231                 cfg.style = this.style;
23232             }
23233             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23234             
23235             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23236         }
23237         
23238         this.el.select('>button.close').on('click', this.hide, this);
23239         
23240     },
23241     
23242     show : function()
23243     {
23244         if (!this.rendered) {
23245             this.render();
23246         }
23247         
23248         this.el.show();
23249         
23250         this.fireEvent('show', this);
23251         
23252     },
23253     
23254     hide : function()
23255     {
23256         if (!this.rendered) {
23257             this.render();
23258         }
23259         
23260         this.el.hide();
23261         
23262         this.fireEvent('hide', this);
23263     },
23264     
23265     update : function()
23266     {
23267 //        var e = this.el.dom.firstChild;
23268 //        
23269 //        if(this.closable){
23270 //            e = e.nextSibling;
23271 //        }
23272 //        
23273 //        e.data = this.html || '';
23274
23275         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23276     }
23277    
23278 });
23279
23280  
23281
23282      /*
23283  * - LGPL
23284  *
23285  * Graph
23286  * 
23287  */
23288
23289
23290 /**
23291  * @class Roo.bootstrap.Graph
23292  * @extends Roo.bootstrap.Component
23293  * Bootstrap Graph class
23294 > Prameters
23295  -sm {number} sm 4
23296  -md {number} md 5
23297  @cfg {String} graphtype  bar | vbar | pie
23298  @cfg {number} g_x coodinator | centre x (pie)
23299  @cfg {number} g_y coodinator | centre y (pie)
23300  @cfg {number} g_r radius (pie)
23301  @cfg {number} g_height height of the chart (respected by all elements in the set)
23302  @cfg {number} g_width width of the chart (respected by all elements in the set)
23303  @cfg {Object} title The title of the chart
23304     
23305  -{Array}  values
23306  -opts (object) options for the chart 
23307      o {
23308      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23309      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23310      o vgutter (number)
23311      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.
23312      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23313      o to
23314      o stretch (boolean)
23315      o }
23316  -opts (object) options for the pie
23317      o{
23318      o cut
23319      o startAngle (number)
23320      o endAngle (number)
23321      } 
23322  *
23323  * @constructor
23324  * Create a new Input
23325  * @param {Object} config The config object
23326  */
23327
23328 Roo.bootstrap.Graph = function(config){
23329     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23330     
23331     this.addEvents({
23332         // img events
23333         /**
23334          * @event click
23335          * The img click event for the img.
23336          * @param {Roo.EventObject} e
23337          */
23338         "click" : true
23339     });
23340 };
23341
23342 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23343     
23344     sm: 4,
23345     md: 5,
23346     graphtype: 'bar',
23347     g_height: 250,
23348     g_width: 400,
23349     g_x: 50,
23350     g_y: 50,
23351     g_r: 30,
23352     opts:{
23353         //g_colors: this.colors,
23354         g_type: 'soft',
23355         g_gutter: '20%'
23356
23357     },
23358     title : false,
23359
23360     getAutoCreate : function(){
23361         
23362         var cfg = {
23363             tag: 'div',
23364             html : null
23365         };
23366         
23367         
23368         return  cfg;
23369     },
23370
23371     onRender : function(ct,position){
23372         
23373         
23374         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23375         
23376         if (typeof(Raphael) == 'undefined') {
23377             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23378             return;
23379         }
23380         
23381         this.raphael = Raphael(this.el.dom);
23382         
23383                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23384                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23385                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23386                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23387                 /*
23388                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23389                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23390                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23391                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23392                 
23393                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23394                 r.barchart(330, 10, 300, 220, data1);
23395                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23396                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23397                 */
23398                 
23399                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23400                 // r.barchart(30, 30, 560, 250,  xdata, {
23401                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23402                 //     axis : "0 0 1 1",
23403                 //     axisxlabels :  xdata
23404                 //     //yvalues : cols,
23405                    
23406                 // });
23407 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23408 //        
23409 //        this.load(null,xdata,{
23410 //                axis : "0 0 1 1",
23411 //                axisxlabels :  xdata
23412 //                });
23413
23414     },
23415
23416     load : function(graphtype,xdata,opts)
23417     {
23418         this.raphael.clear();
23419         if(!graphtype) {
23420             graphtype = this.graphtype;
23421         }
23422         if(!opts){
23423             opts = this.opts;
23424         }
23425         var r = this.raphael,
23426             fin = function () {
23427                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23428             },
23429             fout = function () {
23430                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23431             },
23432             pfin = function() {
23433                 this.sector.stop();
23434                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23435
23436                 if (this.label) {
23437                     this.label[0].stop();
23438                     this.label[0].attr({ r: 7.5 });
23439                     this.label[1].attr({ "font-weight": 800 });
23440                 }
23441             },
23442             pfout = function() {
23443                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23444
23445                 if (this.label) {
23446                     this.label[0].animate({ r: 5 }, 500, "bounce");
23447                     this.label[1].attr({ "font-weight": 400 });
23448                 }
23449             };
23450
23451         switch(graphtype){
23452             case 'bar':
23453                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23454                 break;
23455             case 'hbar':
23456                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23457                 break;
23458             case 'pie':
23459 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23460 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23461 //            
23462                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23463                 
23464                 break;
23465
23466         }
23467         
23468         if(this.title){
23469             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23470         }
23471         
23472     },
23473     
23474     setTitle: function(o)
23475     {
23476         this.title = o;
23477     },
23478     
23479     initEvents: function() {
23480         
23481         if(!this.href){
23482             this.el.on('click', this.onClick, this);
23483         }
23484     },
23485     
23486     onClick : function(e)
23487     {
23488         Roo.log('img onclick');
23489         this.fireEvent('click', this, e);
23490     }
23491    
23492 });
23493
23494  
23495 /*
23496  * - LGPL
23497  *
23498  * numberBox
23499  * 
23500  */
23501 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23502
23503 /**
23504  * @class Roo.bootstrap.dash.NumberBox
23505  * @extends Roo.bootstrap.Component
23506  * Bootstrap NumberBox class
23507  * @cfg {String} headline Box headline
23508  * @cfg {String} content Box content
23509  * @cfg {String} icon Box icon
23510  * @cfg {String} footer Footer text
23511  * @cfg {String} fhref Footer href
23512  * 
23513  * @constructor
23514  * Create a new NumberBox
23515  * @param {Object} config The config object
23516  */
23517
23518
23519 Roo.bootstrap.dash.NumberBox = function(config){
23520     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23521     
23522 };
23523
23524 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23525     
23526     headline : '',
23527     content : '',
23528     icon : '',
23529     footer : '',
23530     fhref : '',
23531     ficon : '',
23532     
23533     getAutoCreate : function(){
23534         
23535         var cfg = {
23536             tag : 'div',
23537             cls : 'small-box ',
23538             cn : [
23539                 {
23540                     tag : 'div',
23541                     cls : 'inner',
23542                     cn :[
23543                         {
23544                             tag : 'h3',
23545                             cls : 'roo-headline',
23546                             html : this.headline
23547                         },
23548                         {
23549                             tag : 'p',
23550                             cls : 'roo-content',
23551                             html : this.content
23552                         }
23553                     ]
23554                 }
23555             ]
23556         };
23557         
23558         if(this.icon){
23559             cfg.cn.push({
23560                 tag : 'div',
23561                 cls : 'icon',
23562                 cn :[
23563                     {
23564                         tag : 'i',
23565                         cls : 'ion ' + this.icon
23566                     }
23567                 ]
23568             });
23569         }
23570         
23571         if(this.footer){
23572             var footer = {
23573                 tag : 'a',
23574                 cls : 'small-box-footer',
23575                 href : this.fhref || '#',
23576                 html : this.footer
23577             };
23578             
23579             cfg.cn.push(footer);
23580             
23581         }
23582         
23583         return  cfg;
23584     },
23585
23586     onRender : function(ct,position){
23587         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23588
23589
23590        
23591                 
23592     },
23593
23594     setHeadline: function (value)
23595     {
23596         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23597     },
23598     
23599     setFooter: function (value, href)
23600     {
23601         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23602         
23603         if(href){
23604             this.el.select('a.small-box-footer',true).first().attr('href', href);
23605         }
23606         
23607     },
23608
23609     setContent: function (value)
23610     {
23611         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23612     },
23613
23614     initEvents: function() 
23615     {   
23616         
23617     }
23618     
23619 });
23620
23621  
23622 /*
23623  * - LGPL
23624  *
23625  * TabBox
23626  * 
23627  */
23628 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23629
23630 /**
23631  * @class Roo.bootstrap.dash.TabBox
23632  * @extends Roo.bootstrap.Component
23633  * Bootstrap TabBox class
23634  * @cfg {String} title Title of the TabBox
23635  * @cfg {String} icon Icon of the TabBox
23636  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23637  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23638  * 
23639  * @constructor
23640  * Create a new TabBox
23641  * @param {Object} config The config object
23642  */
23643
23644
23645 Roo.bootstrap.dash.TabBox = function(config){
23646     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23647     this.addEvents({
23648         // raw events
23649         /**
23650          * @event addpane
23651          * When a pane is added
23652          * @param {Roo.bootstrap.dash.TabPane} pane
23653          */
23654         "addpane" : true,
23655         /**
23656          * @event activatepane
23657          * When a pane is activated
23658          * @param {Roo.bootstrap.dash.TabPane} pane
23659          */
23660         "activatepane" : true
23661         
23662          
23663     });
23664     
23665     this.panes = [];
23666 };
23667
23668 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23669
23670     title : '',
23671     icon : false,
23672     showtabs : true,
23673     tabScrollable : false,
23674     
23675     getChildContainer : function()
23676     {
23677         return this.el.select('.tab-content', true).first();
23678     },
23679     
23680     getAutoCreate : function(){
23681         
23682         var header = {
23683             tag: 'li',
23684             cls: 'pull-left header',
23685             html: this.title,
23686             cn : []
23687         };
23688         
23689         if(this.icon){
23690             header.cn.push({
23691                 tag: 'i',
23692                 cls: 'fa ' + this.icon
23693             });
23694         }
23695         
23696         var h = {
23697             tag: 'ul',
23698             cls: 'nav nav-tabs pull-right',
23699             cn: [
23700                 header
23701             ]
23702         };
23703         
23704         if(this.tabScrollable){
23705             h = {
23706                 tag: 'div',
23707                 cls: 'tab-header',
23708                 cn: [
23709                     {
23710                         tag: 'ul',
23711                         cls: 'nav nav-tabs pull-right',
23712                         cn: [
23713                             header
23714                         ]
23715                     }
23716                 ]
23717             };
23718         }
23719         
23720         var cfg = {
23721             tag: 'div',
23722             cls: 'nav-tabs-custom',
23723             cn: [
23724                 h,
23725                 {
23726                     tag: 'div',
23727                     cls: 'tab-content no-padding',
23728                     cn: []
23729                 }
23730             ]
23731         };
23732
23733         return  cfg;
23734     },
23735     initEvents : function()
23736     {
23737         //Roo.log('add add pane handler');
23738         this.on('addpane', this.onAddPane, this);
23739     },
23740      /**
23741      * Updates the box title
23742      * @param {String} html to set the title to.
23743      */
23744     setTitle : function(value)
23745     {
23746         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23747     },
23748     onAddPane : function(pane)
23749     {
23750         this.panes.push(pane);
23751         //Roo.log('addpane');
23752         //Roo.log(pane);
23753         // tabs are rendere left to right..
23754         if(!this.showtabs){
23755             return;
23756         }
23757         
23758         var ctr = this.el.select('.nav-tabs', true).first();
23759          
23760          
23761         var existing = ctr.select('.nav-tab',true);
23762         var qty = existing.getCount();;
23763         
23764         
23765         var tab = ctr.createChild({
23766             tag : 'li',
23767             cls : 'nav-tab' + (qty ? '' : ' active'),
23768             cn : [
23769                 {
23770                     tag : 'a',
23771                     href:'#',
23772                     html : pane.title
23773                 }
23774             ]
23775         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23776         pane.tab = tab;
23777         
23778         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23779         if (!qty) {
23780             pane.el.addClass('active');
23781         }
23782         
23783                 
23784     },
23785     onTabClick : function(ev,un,ob,pane)
23786     {
23787         //Roo.log('tab - prev default');
23788         ev.preventDefault();
23789         
23790         
23791         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23792         pane.tab.addClass('active');
23793         //Roo.log(pane.title);
23794         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23795         // technically we should have a deactivate event.. but maybe add later.
23796         // and it should not de-activate the selected tab...
23797         this.fireEvent('activatepane', pane);
23798         pane.el.addClass('active');
23799         pane.fireEvent('activate');
23800         
23801         
23802     },
23803     
23804     getActivePane : function()
23805     {
23806         var r = false;
23807         Roo.each(this.panes, function(p) {
23808             if(p.el.hasClass('active')){
23809                 r = p;
23810                 return false;
23811             }
23812             
23813             return;
23814         });
23815         
23816         return r;
23817     }
23818     
23819     
23820 });
23821
23822  
23823 /*
23824  * - LGPL
23825  *
23826  * Tab pane
23827  * 
23828  */
23829 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23830 /**
23831  * @class Roo.bootstrap.TabPane
23832  * @extends Roo.bootstrap.Component
23833  * Bootstrap TabPane class
23834  * @cfg {Boolean} active (false | true) Default false
23835  * @cfg {String} title title of panel
23836
23837  * 
23838  * @constructor
23839  * Create a new TabPane
23840  * @param {Object} config The config object
23841  */
23842
23843 Roo.bootstrap.dash.TabPane = function(config){
23844     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23845     
23846     this.addEvents({
23847         // raw events
23848         /**
23849          * @event activate
23850          * When a pane is activated
23851          * @param {Roo.bootstrap.dash.TabPane} pane
23852          */
23853         "activate" : true
23854          
23855     });
23856 };
23857
23858 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23859     
23860     active : false,
23861     title : '',
23862     
23863     // the tabBox that this is attached to.
23864     tab : false,
23865      
23866     getAutoCreate : function() 
23867     {
23868         var cfg = {
23869             tag: 'div',
23870             cls: 'tab-pane'
23871         };
23872         
23873         if(this.active){
23874             cfg.cls += ' active';
23875         }
23876         
23877         return cfg;
23878     },
23879     initEvents  : function()
23880     {
23881         //Roo.log('trigger add pane handler');
23882         this.parent().fireEvent('addpane', this)
23883     },
23884     
23885      /**
23886      * Updates the tab title 
23887      * @param {String} html to set the title to.
23888      */
23889     setTitle: function(str)
23890     {
23891         if (!this.tab) {
23892             return;
23893         }
23894         this.title = str;
23895         this.tab.select('a', true).first().dom.innerHTML = str;
23896         
23897     }
23898     
23899     
23900     
23901 });
23902
23903  
23904
23905
23906  /*
23907  * - LGPL
23908  *
23909  * menu
23910  * 
23911  */
23912 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23913
23914 /**
23915  * @class Roo.bootstrap.menu.Menu
23916  * @extends Roo.bootstrap.Component
23917  * Bootstrap Menu class - container for Menu
23918  * @cfg {String} html Text of the menu
23919  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23920  * @cfg {String} icon Font awesome icon
23921  * @cfg {String} pos Menu align to (top | bottom) default bottom
23922  * 
23923  * 
23924  * @constructor
23925  * Create a new Menu
23926  * @param {Object} config The config object
23927  */
23928
23929
23930 Roo.bootstrap.menu.Menu = function(config){
23931     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23932     
23933     this.addEvents({
23934         /**
23935          * @event beforeshow
23936          * Fires before this menu is displayed
23937          * @param {Roo.bootstrap.menu.Menu} this
23938          */
23939         beforeshow : true,
23940         /**
23941          * @event beforehide
23942          * Fires before this menu is hidden
23943          * @param {Roo.bootstrap.menu.Menu} this
23944          */
23945         beforehide : true,
23946         /**
23947          * @event show
23948          * Fires after this menu is displayed
23949          * @param {Roo.bootstrap.menu.Menu} this
23950          */
23951         show : true,
23952         /**
23953          * @event hide
23954          * Fires after this menu is hidden
23955          * @param {Roo.bootstrap.menu.Menu} this
23956          */
23957         hide : true,
23958         /**
23959          * @event click
23960          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23961          * @param {Roo.bootstrap.menu.Menu} this
23962          * @param {Roo.EventObject} e
23963          */
23964         click : true
23965     });
23966     
23967 };
23968
23969 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23970     
23971     submenu : false,
23972     html : '',
23973     weight : 'default',
23974     icon : false,
23975     pos : 'bottom',
23976     
23977     
23978     getChildContainer : function() {
23979         if(this.isSubMenu){
23980             return this.el;
23981         }
23982         
23983         return this.el.select('ul.dropdown-menu', true).first();  
23984     },
23985     
23986     getAutoCreate : function()
23987     {
23988         var text = [
23989             {
23990                 tag : 'span',
23991                 cls : 'roo-menu-text',
23992                 html : this.html
23993             }
23994         ];
23995         
23996         if(this.icon){
23997             text.unshift({
23998                 tag : 'i',
23999                 cls : 'fa ' + this.icon
24000             })
24001         }
24002         
24003         
24004         var cfg = {
24005             tag : 'div',
24006             cls : 'btn-group',
24007             cn : [
24008                 {
24009                     tag : 'button',
24010                     cls : 'dropdown-button btn btn-' + this.weight,
24011                     cn : text
24012                 },
24013                 {
24014                     tag : 'button',
24015                     cls : 'dropdown-toggle btn btn-' + this.weight,
24016                     cn : [
24017                         {
24018                             tag : 'span',
24019                             cls : 'caret'
24020                         }
24021                     ]
24022                 },
24023                 {
24024                     tag : 'ul',
24025                     cls : 'dropdown-menu'
24026                 }
24027             ]
24028             
24029         };
24030         
24031         if(this.pos == 'top'){
24032             cfg.cls += ' dropup';
24033         }
24034         
24035         if(this.isSubMenu){
24036             cfg = {
24037                 tag : 'ul',
24038                 cls : 'dropdown-menu'
24039             }
24040         }
24041         
24042         return cfg;
24043     },
24044     
24045     onRender : function(ct, position)
24046     {
24047         this.isSubMenu = ct.hasClass('dropdown-submenu');
24048         
24049         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24050     },
24051     
24052     initEvents : function() 
24053     {
24054         if(this.isSubMenu){
24055             return;
24056         }
24057         
24058         this.hidden = true;
24059         
24060         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24061         this.triggerEl.on('click', this.onTriggerPress, this);
24062         
24063         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24064         this.buttonEl.on('click', this.onClick, this);
24065         
24066     },
24067     
24068     list : function()
24069     {
24070         if(this.isSubMenu){
24071             return this.el;
24072         }
24073         
24074         return this.el.select('ul.dropdown-menu', true).first();
24075     },
24076     
24077     onClick : function(e)
24078     {
24079         this.fireEvent("click", this, e);
24080     },
24081     
24082     onTriggerPress  : function(e)
24083     {   
24084         if (this.isVisible()) {
24085             this.hide();
24086         } else {
24087             this.show();
24088         }
24089     },
24090     
24091     isVisible : function(){
24092         return !this.hidden;
24093     },
24094     
24095     show : function()
24096     {
24097         this.fireEvent("beforeshow", this);
24098         
24099         this.hidden = false;
24100         this.el.addClass('open');
24101         
24102         Roo.get(document).on("mouseup", this.onMouseUp, this);
24103         
24104         this.fireEvent("show", this);
24105         
24106         
24107     },
24108     
24109     hide : function()
24110     {
24111         this.fireEvent("beforehide", this);
24112         
24113         this.hidden = true;
24114         this.el.removeClass('open');
24115         
24116         Roo.get(document).un("mouseup", this.onMouseUp);
24117         
24118         this.fireEvent("hide", this);
24119     },
24120     
24121     onMouseUp : function()
24122     {
24123         this.hide();
24124     }
24125     
24126 });
24127
24128  
24129  /*
24130  * - LGPL
24131  *
24132  * menu item
24133  * 
24134  */
24135 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24136
24137 /**
24138  * @class Roo.bootstrap.menu.Item
24139  * @extends Roo.bootstrap.Component
24140  * Bootstrap MenuItem class
24141  * @cfg {Boolean} submenu (true | false) default false
24142  * @cfg {String} html text of the item
24143  * @cfg {String} href the link
24144  * @cfg {Boolean} disable (true | false) default false
24145  * @cfg {Boolean} preventDefault (true | false) default true
24146  * @cfg {String} icon Font awesome icon
24147  * @cfg {String} pos Submenu align to (left | right) default right 
24148  * 
24149  * 
24150  * @constructor
24151  * Create a new Item
24152  * @param {Object} config The config object
24153  */
24154
24155
24156 Roo.bootstrap.menu.Item = function(config){
24157     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24158     this.addEvents({
24159         /**
24160          * @event mouseover
24161          * Fires when the mouse is hovering over this menu
24162          * @param {Roo.bootstrap.menu.Item} this
24163          * @param {Roo.EventObject} e
24164          */
24165         mouseover : true,
24166         /**
24167          * @event mouseout
24168          * Fires when the mouse exits this menu
24169          * @param {Roo.bootstrap.menu.Item} this
24170          * @param {Roo.EventObject} e
24171          */
24172         mouseout : true,
24173         // raw events
24174         /**
24175          * @event click
24176          * The raw click event for the entire grid.
24177          * @param {Roo.EventObject} e
24178          */
24179         click : true
24180     });
24181 };
24182
24183 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24184     
24185     submenu : false,
24186     href : '',
24187     html : '',
24188     preventDefault: true,
24189     disable : false,
24190     icon : false,
24191     pos : 'right',
24192     
24193     getAutoCreate : function()
24194     {
24195         var text = [
24196             {
24197                 tag : 'span',
24198                 cls : 'roo-menu-item-text',
24199                 html : this.html
24200             }
24201         ];
24202         
24203         if(this.icon){
24204             text.unshift({
24205                 tag : 'i',
24206                 cls : 'fa ' + this.icon
24207             })
24208         }
24209         
24210         var cfg = {
24211             tag : 'li',
24212             cn : [
24213                 {
24214                     tag : 'a',
24215                     href : this.href || '#',
24216                     cn : text
24217                 }
24218             ]
24219         };
24220         
24221         if(this.disable){
24222             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24223         }
24224         
24225         if(this.submenu){
24226             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24227             
24228             if(this.pos == 'left'){
24229                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24230             }
24231         }
24232         
24233         return cfg;
24234     },
24235     
24236     initEvents : function() 
24237     {
24238         this.el.on('mouseover', this.onMouseOver, this);
24239         this.el.on('mouseout', this.onMouseOut, this);
24240         
24241         this.el.select('a', true).first().on('click', this.onClick, this);
24242         
24243     },
24244     
24245     onClick : function(e)
24246     {
24247         if(this.preventDefault){
24248             e.preventDefault();
24249         }
24250         
24251         this.fireEvent("click", this, e);
24252     },
24253     
24254     onMouseOver : function(e)
24255     {
24256         if(this.submenu && this.pos == 'left'){
24257             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24258         }
24259         
24260         this.fireEvent("mouseover", this, e);
24261     },
24262     
24263     onMouseOut : function(e)
24264     {
24265         this.fireEvent("mouseout", this, e);
24266     }
24267 });
24268
24269  
24270
24271  /*
24272  * - LGPL
24273  *
24274  * menu separator
24275  * 
24276  */
24277 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24278
24279 /**
24280  * @class Roo.bootstrap.menu.Separator
24281  * @extends Roo.bootstrap.Component
24282  * Bootstrap Separator class
24283  * 
24284  * @constructor
24285  * Create a new Separator
24286  * @param {Object} config The config object
24287  */
24288
24289
24290 Roo.bootstrap.menu.Separator = function(config){
24291     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24292 };
24293
24294 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24295     
24296     getAutoCreate : function(){
24297         var cfg = {
24298             tag : 'li',
24299             cls: 'divider'
24300         };
24301         
24302         return cfg;
24303     }
24304    
24305 });
24306
24307  
24308
24309  /*
24310  * - LGPL
24311  *
24312  * Tooltip
24313  * 
24314  */
24315
24316 /**
24317  * @class Roo.bootstrap.Tooltip
24318  * Bootstrap Tooltip class
24319  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24320  * to determine which dom element triggers the tooltip.
24321  * 
24322  * It needs to add support for additional attributes like tooltip-position
24323  * 
24324  * @constructor
24325  * Create a new Toolti
24326  * @param {Object} config The config object
24327  */
24328
24329 Roo.bootstrap.Tooltip = function(config){
24330     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24331 };
24332
24333 Roo.apply(Roo.bootstrap.Tooltip, {
24334     /**
24335      * @function init initialize tooltip monitoring.
24336      * @static
24337      */
24338     currentEl : false,
24339     currentTip : false,
24340     currentRegion : false,
24341     
24342     //  init : delay?
24343     
24344     init : function()
24345     {
24346         Roo.get(document).on('mouseover', this.enter ,this);
24347         Roo.get(document).on('mouseout', this.leave, this);
24348          
24349         
24350         this.currentTip = new Roo.bootstrap.Tooltip();
24351     },
24352     
24353     enter : function(ev)
24354     {
24355         var dom = ev.getTarget();
24356         
24357         //Roo.log(['enter',dom]);
24358         var el = Roo.fly(dom);
24359         if (this.currentEl) {
24360             //Roo.log(dom);
24361             //Roo.log(this.currentEl);
24362             //Roo.log(this.currentEl.contains(dom));
24363             if (this.currentEl == el) {
24364                 return;
24365             }
24366             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24367                 return;
24368             }
24369
24370         }
24371         
24372         if (this.currentTip.el) {
24373             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24374         }    
24375         //Roo.log(ev);
24376         
24377         if(!el || el.dom == document){
24378             return;
24379         }
24380         
24381         var bindEl = el;
24382         
24383         // you can not look for children, as if el is the body.. then everythign is the child..
24384         if (!el.attr('tooltip')) { //
24385             if (!el.select("[tooltip]").elements.length) {
24386                 return;
24387             }
24388             // is the mouse over this child...?
24389             bindEl = el.select("[tooltip]").first();
24390             var xy = ev.getXY();
24391             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24392                 //Roo.log("not in region.");
24393                 return;
24394             }
24395             //Roo.log("child element over..");
24396             
24397         }
24398         this.currentEl = bindEl;
24399         this.currentTip.bind(bindEl);
24400         this.currentRegion = Roo.lib.Region.getRegion(dom);
24401         this.currentTip.enter();
24402         
24403     },
24404     leave : function(ev)
24405     {
24406         var dom = ev.getTarget();
24407         //Roo.log(['leave',dom]);
24408         if (!this.currentEl) {
24409             return;
24410         }
24411         
24412         
24413         if (dom != this.currentEl.dom) {
24414             return;
24415         }
24416         var xy = ev.getXY();
24417         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24418             return;
24419         }
24420         // only activate leave if mouse cursor is outside... bounding box..
24421         
24422         
24423         
24424         
24425         if (this.currentTip) {
24426             this.currentTip.leave();
24427         }
24428         //Roo.log('clear currentEl');
24429         this.currentEl = false;
24430         
24431         
24432     },
24433     alignment : {
24434         'left' : ['r-l', [-2,0], 'right'],
24435         'right' : ['l-r', [2,0], 'left'],
24436         'bottom' : ['t-b', [0,2], 'top'],
24437         'top' : [ 'b-t', [0,-2], 'bottom']
24438     }
24439     
24440 });
24441
24442
24443 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24444     
24445     
24446     bindEl : false,
24447     
24448     delay : null, // can be { show : 300 , hide: 500}
24449     
24450     timeout : null,
24451     
24452     hoverState : null, //???
24453     
24454     placement : 'bottom', 
24455     
24456     getAutoCreate : function(){
24457     
24458         var cfg = {
24459            cls : 'tooltip',
24460            role : 'tooltip',
24461            cn : [
24462                 {
24463                     cls : 'tooltip-arrow'
24464                 },
24465                 {
24466                     cls : 'tooltip-inner'
24467                 }
24468            ]
24469         };
24470         
24471         return cfg;
24472     },
24473     bind : function(el)
24474     {
24475         this.bindEl = el;
24476     },
24477       
24478     
24479     enter : function () {
24480        
24481         if (this.timeout != null) {
24482             clearTimeout(this.timeout);
24483         }
24484         
24485         this.hoverState = 'in';
24486          //Roo.log("enter - show");
24487         if (!this.delay || !this.delay.show) {
24488             this.show();
24489             return;
24490         }
24491         var _t = this;
24492         this.timeout = setTimeout(function () {
24493             if (_t.hoverState == 'in') {
24494                 _t.show();
24495             }
24496         }, this.delay.show);
24497     },
24498     leave : function()
24499     {
24500         clearTimeout(this.timeout);
24501     
24502         this.hoverState = 'out';
24503          if (!this.delay || !this.delay.hide) {
24504             this.hide();
24505             return;
24506         }
24507        
24508         var _t = this;
24509         this.timeout = setTimeout(function () {
24510             //Roo.log("leave - timeout");
24511             
24512             if (_t.hoverState == 'out') {
24513                 _t.hide();
24514                 Roo.bootstrap.Tooltip.currentEl = false;
24515             }
24516         }, delay);
24517     },
24518     
24519     show : function ()
24520     {
24521         if (!this.el) {
24522             this.render(document.body);
24523         }
24524         // set content.
24525         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24526         
24527         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24528         
24529         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24530         
24531         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24532         
24533         var placement = typeof this.placement == 'function' ?
24534             this.placement.call(this, this.el, on_el) :
24535             this.placement;
24536             
24537         var autoToken = /\s?auto?\s?/i;
24538         var autoPlace = autoToken.test(placement);
24539         if (autoPlace) {
24540             placement = placement.replace(autoToken, '') || 'top';
24541         }
24542         
24543         //this.el.detach()
24544         //this.el.setXY([0,0]);
24545         this.el.show();
24546         //this.el.dom.style.display='block';
24547         
24548         //this.el.appendTo(on_el);
24549         
24550         var p = this.getPosition();
24551         var box = this.el.getBox();
24552         
24553         if (autoPlace) {
24554             // fixme..
24555         }
24556         
24557         var align = Roo.bootstrap.Tooltip.alignment[placement];
24558         
24559         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24560         
24561         if(placement == 'top' || placement == 'bottom'){
24562             if(xy[0] < 0){
24563                 placement = 'right';
24564             }
24565             
24566             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24567                 placement = 'left';
24568             }
24569             
24570             var scroll = Roo.select('body', true).first().getScroll();
24571             
24572             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24573                 placement = 'top';
24574             }
24575             
24576         }
24577         
24578         align = Roo.bootstrap.Tooltip.alignment[placement];
24579         
24580         this.el.alignTo(this.bindEl, align[0],align[1]);
24581         //var arrow = this.el.select('.arrow',true).first();
24582         //arrow.set(align[2], 
24583         
24584         this.el.addClass(placement);
24585         
24586         this.el.addClass('in fade');
24587         
24588         this.hoverState = null;
24589         
24590         if (this.el.hasClass('fade')) {
24591             // fade it?
24592         }
24593         
24594     },
24595     hide : function()
24596     {
24597          
24598         if (!this.el) {
24599             return;
24600         }
24601         //this.el.setXY([0,0]);
24602         this.el.removeClass('in');
24603         //this.el.hide();
24604         
24605     }
24606     
24607 });
24608  
24609
24610  /*
24611  * - LGPL
24612  *
24613  * Location Picker
24614  * 
24615  */
24616
24617 /**
24618  * @class Roo.bootstrap.LocationPicker
24619  * @extends Roo.bootstrap.Component
24620  * Bootstrap LocationPicker class
24621  * @cfg {Number} latitude Position when init default 0
24622  * @cfg {Number} longitude Position when init default 0
24623  * @cfg {Number} zoom default 15
24624  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24625  * @cfg {Boolean} mapTypeControl default false
24626  * @cfg {Boolean} disableDoubleClickZoom default false
24627  * @cfg {Boolean} scrollwheel default true
24628  * @cfg {Boolean} streetViewControl default false
24629  * @cfg {Number} radius default 0
24630  * @cfg {String} locationName
24631  * @cfg {Boolean} draggable default true
24632  * @cfg {Boolean} enableAutocomplete default false
24633  * @cfg {Boolean} enableReverseGeocode default true
24634  * @cfg {String} markerTitle
24635  * 
24636  * @constructor
24637  * Create a new LocationPicker
24638  * @param {Object} config The config object
24639  */
24640
24641
24642 Roo.bootstrap.LocationPicker = function(config){
24643     
24644     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24645     
24646     this.addEvents({
24647         /**
24648          * @event initial
24649          * Fires when the picker initialized.
24650          * @param {Roo.bootstrap.LocationPicker} this
24651          * @param {Google Location} location
24652          */
24653         initial : true,
24654         /**
24655          * @event positionchanged
24656          * Fires when the picker position changed.
24657          * @param {Roo.bootstrap.LocationPicker} this
24658          * @param {Google Location} location
24659          */
24660         positionchanged : true,
24661         /**
24662          * @event resize
24663          * Fires when the map resize.
24664          * @param {Roo.bootstrap.LocationPicker} this
24665          */
24666         resize : true,
24667         /**
24668          * @event show
24669          * Fires when the map show.
24670          * @param {Roo.bootstrap.LocationPicker} this
24671          */
24672         show : true,
24673         /**
24674          * @event hide
24675          * Fires when the map hide.
24676          * @param {Roo.bootstrap.LocationPicker} this
24677          */
24678         hide : true,
24679         /**
24680          * @event mapClick
24681          * Fires when click the map.
24682          * @param {Roo.bootstrap.LocationPicker} this
24683          * @param {Map event} e
24684          */
24685         mapClick : true,
24686         /**
24687          * @event mapRightClick
24688          * Fires when right click the map.
24689          * @param {Roo.bootstrap.LocationPicker} this
24690          * @param {Map event} e
24691          */
24692         mapRightClick : true,
24693         /**
24694          * @event markerClick
24695          * Fires when click the marker.
24696          * @param {Roo.bootstrap.LocationPicker} this
24697          * @param {Map event} e
24698          */
24699         markerClick : true,
24700         /**
24701          * @event markerRightClick
24702          * Fires when right click the marker.
24703          * @param {Roo.bootstrap.LocationPicker} this
24704          * @param {Map event} e
24705          */
24706         markerRightClick : true,
24707         /**
24708          * @event OverlayViewDraw
24709          * Fires when OverlayView Draw
24710          * @param {Roo.bootstrap.LocationPicker} this
24711          */
24712         OverlayViewDraw : true,
24713         /**
24714          * @event OverlayViewOnAdd
24715          * Fires when OverlayView Draw
24716          * @param {Roo.bootstrap.LocationPicker} this
24717          */
24718         OverlayViewOnAdd : true,
24719         /**
24720          * @event OverlayViewOnRemove
24721          * Fires when OverlayView Draw
24722          * @param {Roo.bootstrap.LocationPicker} this
24723          */
24724         OverlayViewOnRemove : true,
24725         /**
24726          * @event OverlayViewShow
24727          * Fires when OverlayView Draw
24728          * @param {Roo.bootstrap.LocationPicker} this
24729          * @param {Pixel} cpx
24730          */
24731         OverlayViewShow : true,
24732         /**
24733          * @event OverlayViewHide
24734          * Fires when OverlayView Draw
24735          * @param {Roo.bootstrap.LocationPicker} this
24736          */
24737         OverlayViewHide : true,
24738         /**
24739          * @event loadexception
24740          * Fires when load google lib failed.
24741          * @param {Roo.bootstrap.LocationPicker} this
24742          */
24743         loadexception : true
24744     });
24745         
24746 };
24747
24748 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24749     
24750     gMapContext: false,
24751     
24752     latitude: 0,
24753     longitude: 0,
24754     zoom: 15,
24755     mapTypeId: false,
24756     mapTypeControl: false,
24757     disableDoubleClickZoom: false,
24758     scrollwheel: true,
24759     streetViewControl: false,
24760     radius: 0,
24761     locationName: '',
24762     draggable: true,
24763     enableAutocomplete: false,
24764     enableReverseGeocode: true,
24765     markerTitle: '',
24766     
24767     getAutoCreate: function()
24768     {
24769
24770         var cfg = {
24771             tag: 'div',
24772             cls: 'roo-location-picker'
24773         };
24774         
24775         return cfg
24776     },
24777     
24778     initEvents: function(ct, position)
24779     {       
24780         if(!this.el.getWidth() || this.isApplied()){
24781             return;
24782         }
24783         
24784         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24785         
24786         this.initial();
24787     },
24788     
24789     initial: function()
24790     {
24791         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24792             this.fireEvent('loadexception', this);
24793             return;
24794         }
24795         
24796         if(!this.mapTypeId){
24797             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24798         }
24799         
24800         this.gMapContext = this.GMapContext();
24801         
24802         this.initOverlayView();
24803         
24804         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24805         
24806         var _this = this;
24807                 
24808         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24809             _this.setPosition(_this.gMapContext.marker.position);
24810         });
24811         
24812         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24813             _this.fireEvent('mapClick', this, event);
24814             
24815         });
24816
24817         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24818             _this.fireEvent('mapRightClick', this, event);
24819             
24820         });
24821         
24822         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24823             _this.fireEvent('markerClick', this, event);
24824             
24825         });
24826
24827         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24828             _this.fireEvent('markerRightClick', this, event);
24829             
24830         });
24831         
24832         this.setPosition(this.gMapContext.location);
24833         
24834         this.fireEvent('initial', this, this.gMapContext.location);
24835     },
24836     
24837     initOverlayView: function()
24838     {
24839         var _this = this;
24840         
24841         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24842             
24843             draw: function()
24844             {
24845                 _this.fireEvent('OverlayViewDraw', _this);
24846             },
24847             
24848             onAdd: function()
24849             {
24850                 _this.fireEvent('OverlayViewOnAdd', _this);
24851             },
24852             
24853             onRemove: function()
24854             {
24855                 _this.fireEvent('OverlayViewOnRemove', _this);
24856             },
24857             
24858             show: function(cpx)
24859             {
24860                 _this.fireEvent('OverlayViewShow', _this, cpx);
24861             },
24862             
24863             hide: function()
24864             {
24865                 _this.fireEvent('OverlayViewHide', _this);
24866             }
24867             
24868         });
24869     },
24870     
24871     fromLatLngToContainerPixel: function(event)
24872     {
24873         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24874     },
24875     
24876     isApplied: function() 
24877     {
24878         return this.getGmapContext() == false ? false : true;
24879     },
24880     
24881     getGmapContext: function() 
24882     {
24883         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24884     },
24885     
24886     GMapContext: function() 
24887     {
24888         var position = new google.maps.LatLng(this.latitude, this.longitude);
24889         
24890         var _map = new google.maps.Map(this.el.dom, {
24891             center: position,
24892             zoom: this.zoom,
24893             mapTypeId: this.mapTypeId,
24894             mapTypeControl: this.mapTypeControl,
24895             disableDoubleClickZoom: this.disableDoubleClickZoom,
24896             scrollwheel: this.scrollwheel,
24897             streetViewControl: this.streetViewControl,
24898             locationName: this.locationName,
24899             draggable: this.draggable,
24900             enableAutocomplete: this.enableAutocomplete,
24901             enableReverseGeocode: this.enableReverseGeocode
24902         });
24903         
24904         var _marker = new google.maps.Marker({
24905             position: position,
24906             map: _map,
24907             title: this.markerTitle,
24908             draggable: this.draggable
24909         });
24910         
24911         return {
24912             map: _map,
24913             marker: _marker,
24914             circle: null,
24915             location: position,
24916             radius: this.radius,
24917             locationName: this.locationName,
24918             addressComponents: {
24919                 formatted_address: null,
24920                 addressLine1: null,
24921                 addressLine2: null,
24922                 streetName: null,
24923                 streetNumber: null,
24924                 city: null,
24925                 district: null,
24926                 state: null,
24927                 stateOrProvince: null
24928             },
24929             settings: this,
24930             domContainer: this.el.dom,
24931             geodecoder: new google.maps.Geocoder()
24932         };
24933     },
24934     
24935     drawCircle: function(center, radius, options) 
24936     {
24937         if (this.gMapContext.circle != null) {
24938             this.gMapContext.circle.setMap(null);
24939         }
24940         if (radius > 0) {
24941             radius *= 1;
24942             options = Roo.apply({}, options, {
24943                 strokeColor: "#0000FF",
24944                 strokeOpacity: .35,
24945                 strokeWeight: 2,
24946                 fillColor: "#0000FF",
24947                 fillOpacity: .2
24948             });
24949             
24950             options.map = this.gMapContext.map;
24951             options.radius = radius;
24952             options.center = center;
24953             this.gMapContext.circle = new google.maps.Circle(options);
24954             return this.gMapContext.circle;
24955         }
24956         
24957         return null;
24958     },
24959     
24960     setPosition: function(location) 
24961     {
24962         this.gMapContext.location = location;
24963         this.gMapContext.marker.setPosition(location);
24964         this.gMapContext.map.panTo(location);
24965         this.drawCircle(location, this.gMapContext.radius, {});
24966         
24967         var _this = this;
24968         
24969         if (this.gMapContext.settings.enableReverseGeocode) {
24970             this.gMapContext.geodecoder.geocode({
24971                 latLng: this.gMapContext.location
24972             }, function(results, status) {
24973                 
24974                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24975                     _this.gMapContext.locationName = results[0].formatted_address;
24976                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24977                     
24978                     _this.fireEvent('positionchanged', this, location);
24979                 }
24980             });
24981             
24982             return;
24983         }
24984         
24985         this.fireEvent('positionchanged', this, location);
24986     },
24987     
24988     resize: function()
24989     {
24990         google.maps.event.trigger(this.gMapContext.map, "resize");
24991         
24992         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24993         
24994         this.fireEvent('resize', this);
24995     },
24996     
24997     setPositionByLatLng: function(latitude, longitude)
24998     {
24999         this.setPosition(new google.maps.LatLng(latitude, longitude));
25000     },
25001     
25002     getCurrentPosition: function() 
25003     {
25004         return {
25005             latitude: this.gMapContext.location.lat(),
25006             longitude: this.gMapContext.location.lng()
25007         };
25008     },
25009     
25010     getAddressName: function() 
25011     {
25012         return this.gMapContext.locationName;
25013     },
25014     
25015     getAddressComponents: function() 
25016     {
25017         return this.gMapContext.addressComponents;
25018     },
25019     
25020     address_component_from_google_geocode: function(address_components) 
25021     {
25022         var result = {};
25023         
25024         for (var i = 0; i < address_components.length; i++) {
25025             var component = address_components[i];
25026             if (component.types.indexOf("postal_code") >= 0) {
25027                 result.postalCode = component.short_name;
25028             } else if (component.types.indexOf("street_number") >= 0) {
25029                 result.streetNumber = component.short_name;
25030             } else if (component.types.indexOf("route") >= 0) {
25031                 result.streetName = component.short_name;
25032             } else if (component.types.indexOf("neighborhood") >= 0) {
25033                 result.city = component.short_name;
25034             } else if (component.types.indexOf("locality") >= 0) {
25035                 result.city = component.short_name;
25036             } else if (component.types.indexOf("sublocality") >= 0) {
25037                 result.district = component.short_name;
25038             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25039                 result.stateOrProvince = component.short_name;
25040             } else if (component.types.indexOf("country") >= 0) {
25041                 result.country = component.short_name;
25042             }
25043         }
25044         
25045         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25046         result.addressLine2 = "";
25047         return result;
25048     },
25049     
25050     setZoomLevel: function(zoom)
25051     {
25052         this.gMapContext.map.setZoom(zoom);
25053     },
25054     
25055     show: function()
25056     {
25057         if(!this.el){
25058             return;
25059         }
25060         
25061         this.el.show();
25062         
25063         this.resize();
25064         
25065         this.fireEvent('show', this);
25066     },
25067     
25068     hide: function()
25069     {
25070         if(!this.el){
25071             return;
25072         }
25073         
25074         this.el.hide();
25075         
25076         this.fireEvent('hide', this);
25077     }
25078     
25079 });
25080
25081 Roo.apply(Roo.bootstrap.LocationPicker, {
25082     
25083     OverlayView : function(map, options)
25084     {
25085         options = options || {};
25086         
25087         this.setMap(map);
25088     }
25089     
25090     
25091 });/*
25092  * - LGPL
25093  *
25094  * Alert
25095  * 
25096  */
25097
25098 /**
25099  * @class Roo.bootstrap.Alert
25100  * @extends Roo.bootstrap.Component
25101  * Bootstrap Alert class
25102  * @cfg {String} title The title of alert
25103  * @cfg {String} html The content of alert
25104  * @cfg {String} weight (  success | info | warning | danger )
25105  * @cfg {String} faicon font-awesomeicon
25106  * 
25107  * @constructor
25108  * Create a new alert
25109  * @param {Object} config The config object
25110  */
25111
25112
25113 Roo.bootstrap.Alert = function(config){
25114     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25115     
25116 };
25117
25118 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25119     
25120     title: '',
25121     html: '',
25122     weight: false,
25123     faicon: false,
25124     
25125     getAutoCreate : function()
25126     {
25127         
25128         var cfg = {
25129             tag : 'div',
25130             cls : 'alert',
25131             cn : [
25132                 {
25133                     tag : 'i',
25134                     cls : 'roo-alert-icon'
25135                     
25136                 },
25137                 {
25138                     tag : 'b',
25139                     cls : 'roo-alert-title',
25140                     html : this.title
25141                 },
25142                 {
25143                     tag : 'span',
25144                     cls : 'roo-alert-text',
25145                     html : this.html
25146                 }
25147             ]
25148         };
25149         
25150         if(this.faicon){
25151             cfg.cn[0].cls += ' fa ' + this.faicon;
25152         }
25153         
25154         if(this.weight){
25155             cfg.cls += ' alert-' + this.weight;
25156         }
25157         
25158         return cfg;
25159     },
25160     
25161     initEvents: function() 
25162     {
25163         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25164     },
25165     
25166     setTitle : function(str)
25167     {
25168         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25169     },
25170     
25171     setText : function(str)
25172     {
25173         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25174     },
25175     
25176     setWeight : function(weight)
25177     {
25178         if(this.weight){
25179             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25180         }
25181         
25182         this.weight = weight;
25183         
25184         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25185     },
25186     
25187     setIcon : function(icon)
25188     {
25189         if(this.faicon){
25190             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25191         }
25192         
25193         this.faicon = icon;
25194         
25195         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25196     },
25197     
25198     hide: function() 
25199     {
25200         this.el.hide();   
25201     },
25202     
25203     show: function() 
25204     {  
25205         this.el.show();   
25206     }
25207     
25208 });
25209
25210  
25211 /*
25212 * Licence: LGPL
25213 */
25214
25215 /**
25216  * @class Roo.bootstrap.UploadCropbox
25217  * @extends Roo.bootstrap.Component
25218  * Bootstrap UploadCropbox class
25219  * @cfg {String} emptyText show when image has been loaded
25220  * @cfg {String} rotateNotify show when image too small to rotate
25221  * @cfg {Number} errorTimeout default 3000
25222  * @cfg {Number} minWidth default 300
25223  * @cfg {Number} minHeight default 300
25224  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25225  * @cfg {Boolean} isDocument (true|false) default false
25226  * @cfg {String} url action url
25227  * @cfg {String} paramName default 'imageUpload'
25228  * @cfg {String} method default POST
25229  * @cfg {Boolean} loadMask (true|false) default true
25230  * @cfg {Boolean} loadingText default 'Loading...'
25231  * 
25232  * @constructor
25233  * Create a new UploadCropbox
25234  * @param {Object} config The config object
25235  */
25236
25237 Roo.bootstrap.UploadCropbox = function(config){
25238     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25239     
25240     this.addEvents({
25241         /**
25242          * @event beforeselectfile
25243          * Fire before select file
25244          * @param {Roo.bootstrap.UploadCropbox} this
25245          */
25246         "beforeselectfile" : true,
25247         /**
25248          * @event initial
25249          * Fire after initEvent
25250          * @param {Roo.bootstrap.UploadCropbox} this
25251          */
25252         "initial" : true,
25253         /**
25254          * @event crop
25255          * Fire after initEvent
25256          * @param {Roo.bootstrap.UploadCropbox} this
25257          * @param {String} data
25258          */
25259         "crop" : true,
25260         /**
25261          * @event prepare
25262          * Fire when preparing the file data
25263          * @param {Roo.bootstrap.UploadCropbox} this
25264          * @param {Object} file
25265          */
25266         "prepare" : true,
25267         /**
25268          * @event exception
25269          * Fire when get exception
25270          * @param {Roo.bootstrap.UploadCropbox} this
25271          * @param {XMLHttpRequest} xhr
25272          */
25273         "exception" : true,
25274         /**
25275          * @event beforeloadcanvas
25276          * Fire before load the canvas
25277          * @param {Roo.bootstrap.UploadCropbox} this
25278          * @param {String} src
25279          */
25280         "beforeloadcanvas" : true,
25281         /**
25282          * @event trash
25283          * Fire when trash image
25284          * @param {Roo.bootstrap.UploadCropbox} this
25285          */
25286         "trash" : true,
25287         /**
25288          * @event download
25289          * Fire when download the image
25290          * @param {Roo.bootstrap.UploadCropbox} this
25291          */
25292         "download" : true,
25293         /**
25294          * @event footerbuttonclick
25295          * Fire when footerbuttonclick
25296          * @param {Roo.bootstrap.UploadCropbox} this
25297          * @param {String} type
25298          */
25299         "footerbuttonclick" : true,
25300         /**
25301          * @event resize
25302          * Fire when resize
25303          * @param {Roo.bootstrap.UploadCropbox} this
25304          */
25305         "resize" : true,
25306         /**
25307          * @event rotate
25308          * Fire when rotate the image
25309          * @param {Roo.bootstrap.UploadCropbox} this
25310          * @param {String} pos
25311          */
25312         "rotate" : true,
25313         /**
25314          * @event inspect
25315          * Fire when inspect the file
25316          * @param {Roo.bootstrap.UploadCropbox} this
25317          * @param {Object} file
25318          */
25319         "inspect" : true,
25320         /**
25321          * @event upload
25322          * Fire when xhr upload the file
25323          * @param {Roo.bootstrap.UploadCropbox} this
25324          * @param {Object} data
25325          */
25326         "upload" : true,
25327         /**
25328          * @event arrange
25329          * Fire when arrange the file data
25330          * @param {Roo.bootstrap.UploadCropbox} this
25331          * @param {Object} formData
25332          */
25333         "arrange" : true
25334     });
25335     
25336     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25337 };
25338
25339 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25340     
25341     emptyText : 'Click to upload image',
25342     rotateNotify : 'Image is too small to rotate',
25343     errorTimeout : 3000,
25344     scale : 0,
25345     baseScale : 1,
25346     rotate : 0,
25347     dragable : false,
25348     pinching : false,
25349     mouseX : 0,
25350     mouseY : 0,
25351     cropData : false,
25352     minWidth : 300,
25353     minHeight : 300,
25354     file : false,
25355     exif : {},
25356     baseRotate : 1,
25357     cropType : 'image/jpeg',
25358     buttons : false,
25359     canvasLoaded : false,
25360     isDocument : false,
25361     method : 'POST',
25362     paramName : 'imageUpload',
25363     loadMask : true,
25364     loadingText : 'Loading...',
25365     maskEl : false,
25366     
25367     getAutoCreate : function()
25368     {
25369         var cfg = {
25370             tag : 'div',
25371             cls : 'roo-upload-cropbox',
25372             cn : [
25373                 {
25374                     tag : 'input',
25375                     cls : 'roo-upload-cropbox-selector',
25376                     type : 'file'
25377                 },
25378                 {
25379                     tag : 'div',
25380                     cls : 'roo-upload-cropbox-body',
25381                     style : 'cursor:pointer',
25382                     cn : [
25383                         {
25384                             tag : 'div',
25385                             cls : 'roo-upload-cropbox-preview'
25386                         },
25387                         {
25388                             tag : 'div',
25389                             cls : 'roo-upload-cropbox-thumb'
25390                         },
25391                         {
25392                             tag : 'div',
25393                             cls : 'roo-upload-cropbox-empty-notify',
25394                             html : this.emptyText
25395                         },
25396                         {
25397                             tag : 'div',
25398                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25399                             html : this.rotateNotify
25400                         }
25401                     ]
25402                 },
25403                 {
25404                     tag : 'div',
25405                     cls : 'roo-upload-cropbox-footer',
25406                     cn : {
25407                         tag : 'div',
25408                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25409                         cn : []
25410                     }
25411                 }
25412             ]
25413         };
25414         
25415         return cfg;
25416     },
25417     
25418     onRender : function(ct, position)
25419     {
25420         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25421         
25422         if (this.buttons.length) {
25423             
25424             Roo.each(this.buttons, function(bb) {
25425                 
25426                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25427                 
25428                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25429                 
25430             }, this);
25431         }
25432         
25433         if(this.loadMask){
25434             this.maskEl = this.el;
25435         }
25436     },
25437     
25438     initEvents : function()
25439     {
25440         this.urlAPI = (window.createObjectURL && window) || 
25441                                 (window.URL && URL.revokeObjectURL && URL) || 
25442                                 (window.webkitURL && webkitURL);
25443                         
25444         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25445         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25446         
25447         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25448         this.selectorEl.hide();
25449         
25450         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25451         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25452         
25453         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25454         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25455         this.thumbEl.hide();
25456         
25457         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25458         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25459         
25460         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25461         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25462         this.errorEl.hide();
25463         
25464         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25465         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25466         this.footerEl.hide();
25467         
25468         this.setThumbBoxSize();
25469         
25470         this.bind();
25471         
25472         this.resize();
25473         
25474         this.fireEvent('initial', this);
25475     },
25476
25477     bind : function()
25478     {
25479         var _this = this;
25480         
25481         window.addEventListener("resize", function() { _this.resize(); } );
25482         
25483         this.bodyEl.on('click', this.beforeSelectFile, this);
25484         
25485         if(Roo.isTouch){
25486             this.bodyEl.on('touchstart', this.onTouchStart, this);
25487             this.bodyEl.on('touchmove', this.onTouchMove, this);
25488             this.bodyEl.on('touchend', this.onTouchEnd, this);
25489         }
25490         
25491         if(!Roo.isTouch){
25492             this.bodyEl.on('mousedown', this.onMouseDown, this);
25493             this.bodyEl.on('mousemove', this.onMouseMove, this);
25494             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25495             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25496             Roo.get(document).on('mouseup', this.onMouseUp, this);
25497         }
25498         
25499         this.selectorEl.on('change', this.onFileSelected, this);
25500     },
25501     
25502     reset : function()
25503     {    
25504         this.scale = 0;
25505         this.baseScale = 1;
25506         this.rotate = 0;
25507         this.baseRotate = 1;
25508         this.dragable = false;
25509         this.pinching = false;
25510         this.mouseX = 0;
25511         this.mouseY = 0;
25512         this.cropData = false;
25513         this.notifyEl.dom.innerHTML = this.emptyText;
25514         
25515         this.selectorEl.dom.value = '';
25516         
25517     },
25518     
25519     resize : function()
25520     {
25521         if(this.fireEvent('resize', this) != false){
25522             this.setThumbBoxPosition();
25523             this.setCanvasPosition();
25524         }
25525     },
25526     
25527     onFooterButtonClick : function(e, el, o, type)
25528     {
25529         switch (type) {
25530             case 'rotate-left' :
25531                 this.onRotateLeft(e);
25532                 break;
25533             case 'rotate-right' :
25534                 this.onRotateRight(e);
25535                 break;
25536             case 'picture' :
25537                 this.beforeSelectFile(e);
25538                 break;
25539             case 'trash' :
25540                 this.trash(e);
25541                 break;
25542             case 'crop' :
25543                 this.crop(e);
25544                 break;
25545             case 'download' :
25546                 this.download(e);
25547                 break;
25548             default :
25549                 break;
25550         }
25551         
25552         this.fireEvent('footerbuttonclick', this, type);
25553     },
25554     
25555     beforeSelectFile : function(e)
25556     {
25557         e.preventDefault();
25558         
25559         if(this.fireEvent('beforeselectfile', this) != false){
25560             this.selectorEl.dom.click();
25561         }
25562     },
25563     
25564     onFileSelected : function(e)
25565     {
25566         e.preventDefault();
25567         
25568         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25569             return;
25570         }
25571         
25572         var file = this.selectorEl.dom.files[0];
25573         
25574         if(this.fireEvent('inspect', this, file) != false){
25575             this.prepare(file);
25576         }
25577         
25578     },
25579     
25580     trash : function(e)
25581     {
25582         this.fireEvent('trash', this);
25583     },
25584     
25585     download : function(e)
25586     {
25587         this.fireEvent('download', this);
25588     },
25589     
25590     loadCanvas : function(src)
25591     {   
25592         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25593             
25594             this.reset();
25595             
25596             this.imageEl = document.createElement('img');
25597             
25598             var _this = this;
25599             
25600             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25601             
25602             this.imageEl.src = src;
25603         }
25604     },
25605     
25606     onLoadCanvas : function()
25607     {   
25608         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25609         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25610         
25611         this.bodyEl.un('click', this.beforeSelectFile, this);
25612         
25613         this.notifyEl.hide();
25614         this.thumbEl.show();
25615         this.footerEl.show();
25616         
25617         this.baseRotateLevel();
25618         
25619         if(this.isDocument){
25620             this.setThumbBoxSize();
25621         }
25622         
25623         this.setThumbBoxPosition();
25624         
25625         this.baseScaleLevel();
25626         
25627         this.draw();
25628         
25629         this.resize();
25630         
25631         this.canvasLoaded = true;
25632         
25633         if(this.loadMask){
25634             this.maskEl.unmask();
25635         }
25636         
25637     },
25638     
25639     setCanvasPosition : function()
25640     {   
25641         if(!this.canvasEl){
25642             return;
25643         }
25644         
25645         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25646         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25647         
25648         this.previewEl.setLeft(pw);
25649         this.previewEl.setTop(ph);
25650         
25651     },
25652     
25653     onMouseDown : function(e)
25654     {   
25655         e.stopEvent();
25656         
25657         this.dragable = true;
25658         this.pinching = false;
25659         
25660         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25661             this.dragable = false;
25662             return;
25663         }
25664         
25665         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25666         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25667         
25668     },
25669     
25670     onMouseMove : function(e)
25671     {   
25672         e.stopEvent();
25673         
25674         if(!this.canvasLoaded){
25675             return;
25676         }
25677         
25678         if (!this.dragable){
25679             return;
25680         }
25681         
25682         var minX = Math.ceil(this.thumbEl.getLeft(true));
25683         var minY = Math.ceil(this.thumbEl.getTop(true));
25684         
25685         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25686         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25687         
25688         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25689         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25690         
25691         x = x - this.mouseX;
25692         y = y - this.mouseY;
25693         
25694         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25695         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25696         
25697         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25698         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25699         
25700         this.previewEl.setLeft(bgX);
25701         this.previewEl.setTop(bgY);
25702         
25703         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25704         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25705     },
25706     
25707     onMouseUp : function(e)
25708     {   
25709         e.stopEvent();
25710         
25711         this.dragable = false;
25712     },
25713     
25714     onMouseWheel : function(e)
25715     {   
25716         e.stopEvent();
25717         
25718         this.startScale = this.scale;
25719         
25720         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25721         
25722         if(!this.zoomable()){
25723             this.scale = this.startScale;
25724             return;
25725         }
25726         
25727         this.draw();
25728         
25729         return;
25730     },
25731     
25732     zoomable : function()
25733     {
25734         var minScale = this.thumbEl.getWidth() / this.minWidth;
25735         
25736         if(this.minWidth < this.minHeight){
25737             minScale = this.thumbEl.getHeight() / this.minHeight;
25738         }
25739         
25740         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25741         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25742         
25743         if(
25744                 this.isDocument &&
25745                 (this.rotate == 0 || this.rotate == 180) && 
25746                 (
25747                     width > this.imageEl.OriginWidth || 
25748                     height > this.imageEl.OriginHeight ||
25749                     (width < this.minWidth && height < this.minHeight)
25750                 )
25751         ){
25752             return false;
25753         }
25754         
25755         if(
25756                 this.isDocument &&
25757                 (this.rotate == 90 || this.rotate == 270) && 
25758                 (
25759                     width > this.imageEl.OriginWidth || 
25760                     height > this.imageEl.OriginHeight ||
25761                     (width < this.minHeight && height < this.minWidth)
25762                 )
25763         ){
25764             return false;
25765         }
25766         
25767         if(
25768                 !this.isDocument &&
25769                 (this.rotate == 0 || this.rotate == 180) && 
25770                 (
25771                     width < this.minWidth || 
25772                     width > this.imageEl.OriginWidth || 
25773                     height < this.minHeight || 
25774                     height > this.imageEl.OriginHeight
25775                 )
25776         ){
25777             return false;
25778         }
25779         
25780         if(
25781                 !this.isDocument &&
25782                 (this.rotate == 90 || this.rotate == 270) && 
25783                 (
25784                     width < this.minHeight || 
25785                     width > this.imageEl.OriginWidth || 
25786                     height < this.minWidth || 
25787                     height > this.imageEl.OriginHeight
25788                 )
25789         ){
25790             return false;
25791         }
25792         
25793         return true;
25794         
25795     },
25796     
25797     onRotateLeft : function(e)
25798     {   
25799         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25800             
25801             var minScale = this.thumbEl.getWidth() / this.minWidth;
25802             
25803             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25804             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25805             
25806             this.startScale = this.scale;
25807             
25808             while (this.getScaleLevel() < minScale){
25809             
25810                 this.scale = this.scale + 1;
25811                 
25812                 if(!this.zoomable()){
25813                     break;
25814                 }
25815                 
25816                 if(
25817                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25818                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25819                 ){
25820                     continue;
25821                 }
25822                 
25823                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25824
25825                 this.draw();
25826                 
25827                 return;
25828             }
25829             
25830             this.scale = this.startScale;
25831             
25832             this.onRotateFail();
25833             
25834             return false;
25835         }
25836         
25837         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25838
25839         if(this.isDocument){
25840             this.setThumbBoxSize();
25841             this.setThumbBoxPosition();
25842             this.setCanvasPosition();
25843         }
25844         
25845         this.draw();
25846         
25847         this.fireEvent('rotate', this, 'left');
25848         
25849     },
25850     
25851     onRotateRight : function(e)
25852     {
25853         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25854             
25855             var minScale = this.thumbEl.getWidth() / this.minWidth;
25856         
25857             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25858             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25859             
25860             this.startScale = this.scale;
25861             
25862             while (this.getScaleLevel() < minScale){
25863             
25864                 this.scale = this.scale + 1;
25865                 
25866                 if(!this.zoomable()){
25867                     break;
25868                 }
25869                 
25870                 if(
25871                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25872                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25873                 ){
25874                     continue;
25875                 }
25876                 
25877                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25878
25879                 this.draw();
25880                 
25881                 return;
25882             }
25883             
25884             this.scale = this.startScale;
25885             
25886             this.onRotateFail();
25887             
25888             return false;
25889         }
25890         
25891         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25892
25893         if(this.isDocument){
25894             this.setThumbBoxSize();
25895             this.setThumbBoxPosition();
25896             this.setCanvasPosition();
25897         }
25898         
25899         this.draw();
25900         
25901         this.fireEvent('rotate', this, 'right');
25902     },
25903     
25904     onRotateFail : function()
25905     {
25906         this.errorEl.show(true);
25907         
25908         var _this = this;
25909         
25910         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25911     },
25912     
25913     draw : function()
25914     {
25915         this.previewEl.dom.innerHTML = '';
25916         
25917         var canvasEl = document.createElement("canvas");
25918         
25919         var contextEl = canvasEl.getContext("2d");
25920         
25921         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25922         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25923         var center = this.imageEl.OriginWidth / 2;
25924         
25925         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25926             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25927             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25928             center = this.imageEl.OriginHeight / 2;
25929         }
25930         
25931         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25932         
25933         contextEl.translate(center, center);
25934         contextEl.rotate(this.rotate * Math.PI / 180);
25935
25936         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25937         
25938         this.canvasEl = document.createElement("canvas");
25939         
25940         this.contextEl = this.canvasEl.getContext("2d");
25941         
25942         switch (this.rotate) {
25943             case 0 :
25944                 
25945                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25946                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25947                 
25948                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25949                 
25950                 break;
25951             case 90 : 
25952                 
25953                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25954                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25955                 
25956                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25957                     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);
25958                     break;
25959                 }
25960                 
25961                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25962                 
25963                 break;
25964             case 180 :
25965                 
25966                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25967                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25968                 
25969                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25970                     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);
25971                     break;
25972                 }
25973                 
25974                 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);
25975                 
25976                 break;
25977             case 270 :
25978                 
25979                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25980                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25981         
25982                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25983                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25984                     break;
25985                 }
25986                 
25987                 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);
25988                 
25989                 break;
25990             default : 
25991                 break;
25992         }
25993         
25994         this.previewEl.appendChild(this.canvasEl);
25995         
25996         this.setCanvasPosition();
25997     },
25998     
25999     crop : function()
26000     {
26001         if(!this.canvasLoaded){
26002             return;
26003         }
26004         
26005         var imageCanvas = document.createElement("canvas");
26006         
26007         var imageContext = imageCanvas.getContext("2d");
26008         
26009         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26010         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26011         
26012         var center = imageCanvas.width / 2;
26013         
26014         imageContext.translate(center, center);
26015         
26016         imageContext.rotate(this.rotate * Math.PI / 180);
26017         
26018         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26019         
26020         var canvas = document.createElement("canvas");
26021         
26022         var context = canvas.getContext("2d");
26023                 
26024         canvas.width = this.minWidth;
26025         canvas.height = this.minHeight;
26026
26027         switch (this.rotate) {
26028             case 0 :
26029                 
26030                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26031                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26032                 
26033                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26034                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26035                 
26036                 var targetWidth = this.minWidth - 2 * x;
26037                 var targetHeight = this.minHeight - 2 * y;
26038                 
26039                 var scale = 1;
26040                 
26041                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26042                     scale = targetWidth / width;
26043                 }
26044                 
26045                 if(x > 0 && y == 0){
26046                     scale = targetHeight / height;
26047                 }
26048                 
26049                 if(x > 0 && y > 0){
26050                     scale = targetWidth / width;
26051                     
26052                     if(width < height){
26053                         scale = targetHeight / height;
26054                     }
26055                 }
26056                 
26057                 context.scale(scale, scale);
26058                 
26059                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26060                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26061
26062                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26063                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26064
26065                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26066                 
26067                 break;
26068             case 90 : 
26069                 
26070                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26071                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26072                 
26073                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26074                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26075                 
26076                 var targetWidth = this.minWidth - 2 * x;
26077                 var targetHeight = this.minHeight - 2 * y;
26078                 
26079                 var scale = 1;
26080                 
26081                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26082                     scale = targetWidth / width;
26083                 }
26084                 
26085                 if(x > 0 && y == 0){
26086                     scale = targetHeight / height;
26087                 }
26088                 
26089                 if(x > 0 && y > 0){
26090                     scale = targetWidth / width;
26091                     
26092                     if(width < height){
26093                         scale = targetHeight / height;
26094                     }
26095                 }
26096                 
26097                 context.scale(scale, scale);
26098                 
26099                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26100                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26101
26102                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26103                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26104                 
26105                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26106                 
26107                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26108                 
26109                 break;
26110             case 180 :
26111                 
26112                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26113                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26114                 
26115                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26116                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26117                 
26118                 var targetWidth = this.minWidth - 2 * x;
26119                 var targetHeight = this.minHeight - 2 * y;
26120                 
26121                 var scale = 1;
26122                 
26123                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26124                     scale = targetWidth / width;
26125                 }
26126                 
26127                 if(x > 0 && y == 0){
26128                     scale = targetHeight / height;
26129                 }
26130                 
26131                 if(x > 0 && y > 0){
26132                     scale = targetWidth / width;
26133                     
26134                     if(width < height){
26135                         scale = targetHeight / height;
26136                     }
26137                 }
26138                 
26139                 context.scale(scale, scale);
26140                 
26141                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26142                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26143
26144                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26145                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26146
26147                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26148                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26149                 
26150                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26151                 
26152                 break;
26153             case 270 :
26154                 
26155                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26156                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26157                 
26158                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26159                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26160                 
26161                 var targetWidth = this.minWidth - 2 * x;
26162                 var targetHeight = this.minHeight - 2 * y;
26163                 
26164                 var scale = 1;
26165                 
26166                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26167                     scale = targetWidth / width;
26168                 }
26169                 
26170                 if(x > 0 && y == 0){
26171                     scale = targetHeight / height;
26172                 }
26173                 
26174                 if(x > 0 && y > 0){
26175                     scale = targetWidth / width;
26176                     
26177                     if(width < height){
26178                         scale = targetHeight / height;
26179                     }
26180                 }
26181                 
26182                 context.scale(scale, scale);
26183                 
26184                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26185                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26186
26187                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26188                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26189                 
26190                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26191                 
26192                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26193                 
26194                 break;
26195             default : 
26196                 break;
26197         }
26198         
26199         this.cropData = canvas.toDataURL(this.cropType);
26200         
26201         if(this.fireEvent('crop', this, this.cropData) !== false){
26202             this.process(this.file, this.cropData);
26203         }
26204         
26205         return;
26206         
26207     },
26208     
26209     setThumbBoxSize : function()
26210     {
26211         var width, height;
26212         
26213         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26214             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26215             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26216             
26217             this.minWidth = width;
26218             this.minHeight = height;
26219             
26220             if(this.rotate == 90 || this.rotate == 270){
26221                 this.minWidth = height;
26222                 this.minHeight = width;
26223             }
26224         }
26225         
26226         height = 300;
26227         width = Math.ceil(this.minWidth * height / this.minHeight);
26228         
26229         if(this.minWidth > this.minHeight){
26230             width = 300;
26231             height = Math.ceil(this.minHeight * width / this.minWidth);
26232         }
26233         
26234         this.thumbEl.setStyle({
26235             width : width + 'px',
26236             height : height + 'px'
26237         });
26238
26239         return;
26240             
26241     },
26242     
26243     setThumbBoxPosition : function()
26244     {
26245         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26246         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26247         
26248         this.thumbEl.setLeft(x);
26249         this.thumbEl.setTop(y);
26250         
26251     },
26252     
26253     baseRotateLevel : function()
26254     {
26255         this.baseRotate = 1;
26256         
26257         if(
26258                 typeof(this.exif) != 'undefined' &&
26259                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26260                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26261         ){
26262             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26263         }
26264         
26265         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26266         
26267     },
26268     
26269     baseScaleLevel : function()
26270     {
26271         var width, height;
26272         
26273         if(this.isDocument){
26274             
26275             if(this.baseRotate == 6 || this.baseRotate == 8){
26276             
26277                 height = this.thumbEl.getHeight();
26278                 this.baseScale = height / this.imageEl.OriginWidth;
26279
26280                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26281                     width = this.thumbEl.getWidth();
26282                     this.baseScale = width / this.imageEl.OriginHeight;
26283                 }
26284
26285                 return;
26286             }
26287
26288             height = this.thumbEl.getHeight();
26289             this.baseScale = height / this.imageEl.OriginHeight;
26290
26291             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26292                 width = this.thumbEl.getWidth();
26293                 this.baseScale = width / this.imageEl.OriginWidth;
26294             }
26295
26296             return;
26297         }
26298         
26299         if(this.baseRotate == 6 || this.baseRotate == 8){
26300             
26301             width = this.thumbEl.getHeight();
26302             this.baseScale = width / this.imageEl.OriginHeight;
26303             
26304             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26305                 height = this.thumbEl.getWidth();
26306                 this.baseScale = height / this.imageEl.OriginHeight;
26307             }
26308             
26309             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26310                 height = this.thumbEl.getWidth();
26311                 this.baseScale = height / this.imageEl.OriginHeight;
26312                 
26313                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26314                     width = this.thumbEl.getHeight();
26315                     this.baseScale = width / this.imageEl.OriginWidth;
26316                 }
26317             }
26318             
26319             return;
26320         }
26321         
26322         width = this.thumbEl.getWidth();
26323         this.baseScale = width / this.imageEl.OriginWidth;
26324         
26325         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26326             height = this.thumbEl.getHeight();
26327             this.baseScale = height / this.imageEl.OriginHeight;
26328         }
26329         
26330         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26331             
26332             height = this.thumbEl.getHeight();
26333             this.baseScale = height / this.imageEl.OriginHeight;
26334             
26335             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26336                 width = this.thumbEl.getWidth();
26337                 this.baseScale = width / this.imageEl.OriginWidth;
26338             }
26339             
26340         }
26341         
26342         return;
26343     },
26344     
26345     getScaleLevel : function()
26346     {
26347         return this.baseScale * Math.pow(1.1, this.scale);
26348     },
26349     
26350     onTouchStart : function(e)
26351     {
26352         if(!this.canvasLoaded){
26353             this.beforeSelectFile(e);
26354             return;
26355         }
26356         
26357         var touches = e.browserEvent.touches;
26358         
26359         if(!touches){
26360             return;
26361         }
26362         
26363         if(touches.length == 1){
26364             this.onMouseDown(e);
26365             return;
26366         }
26367         
26368         if(touches.length != 2){
26369             return;
26370         }
26371         
26372         var coords = [];
26373         
26374         for(var i = 0, finger; finger = touches[i]; i++){
26375             coords.push(finger.pageX, finger.pageY);
26376         }
26377         
26378         var x = Math.pow(coords[0] - coords[2], 2);
26379         var y = Math.pow(coords[1] - coords[3], 2);
26380         
26381         this.startDistance = Math.sqrt(x + y);
26382         
26383         this.startScale = this.scale;
26384         
26385         this.pinching = true;
26386         this.dragable = false;
26387         
26388     },
26389     
26390     onTouchMove : function(e)
26391     {
26392         if(!this.pinching && !this.dragable){
26393             return;
26394         }
26395         
26396         var touches = e.browserEvent.touches;
26397         
26398         if(!touches){
26399             return;
26400         }
26401         
26402         if(this.dragable){
26403             this.onMouseMove(e);
26404             return;
26405         }
26406         
26407         var coords = [];
26408         
26409         for(var i = 0, finger; finger = touches[i]; i++){
26410             coords.push(finger.pageX, finger.pageY);
26411         }
26412         
26413         var x = Math.pow(coords[0] - coords[2], 2);
26414         var y = Math.pow(coords[1] - coords[3], 2);
26415         
26416         this.endDistance = Math.sqrt(x + y);
26417         
26418         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26419         
26420         if(!this.zoomable()){
26421             this.scale = this.startScale;
26422             return;
26423         }
26424         
26425         this.draw();
26426         
26427     },
26428     
26429     onTouchEnd : function(e)
26430     {
26431         this.pinching = false;
26432         this.dragable = false;
26433         
26434     },
26435     
26436     process : function(file, crop)
26437     {
26438         if(this.loadMask){
26439             this.maskEl.mask(this.loadingText);
26440         }
26441         
26442         this.xhr = new XMLHttpRequest();
26443         
26444         file.xhr = this.xhr;
26445
26446         this.xhr.open(this.method, this.url, true);
26447         
26448         var headers = {
26449             "Accept": "application/json",
26450             "Cache-Control": "no-cache",
26451             "X-Requested-With": "XMLHttpRequest"
26452         };
26453         
26454         for (var headerName in headers) {
26455             var headerValue = headers[headerName];
26456             if (headerValue) {
26457                 this.xhr.setRequestHeader(headerName, headerValue);
26458             }
26459         }
26460         
26461         var _this = this;
26462         
26463         this.xhr.onload = function()
26464         {
26465             _this.xhrOnLoad(_this.xhr);
26466         }
26467         
26468         this.xhr.onerror = function()
26469         {
26470             _this.xhrOnError(_this.xhr);
26471         }
26472         
26473         var formData = new FormData();
26474
26475         formData.append('returnHTML', 'NO');
26476         
26477         if(crop){
26478             formData.append('crop', crop);
26479         }
26480         
26481         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26482             formData.append(this.paramName, file, file.name);
26483         }
26484         
26485         if(typeof(file.filename) != 'undefined'){
26486             formData.append('filename', file.filename);
26487         }
26488         
26489         if(typeof(file.mimetype) != 'undefined'){
26490             formData.append('mimetype', file.mimetype);
26491         }
26492         
26493         if(this.fireEvent('arrange', this, formData) != false){
26494             this.xhr.send(formData);
26495         };
26496     },
26497     
26498     xhrOnLoad : function(xhr)
26499     {
26500         if(this.loadMask){
26501             this.maskEl.unmask();
26502         }
26503         
26504         if (xhr.readyState !== 4) {
26505             this.fireEvent('exception', this, xhr);
26506             return;
26507         }
26508
26509         var response = Roo.decode(xhr.responseText);
26510         
26511         if(!response.success){
26512             this.fireEvent('exception', this, xhr);
26513             return;
26514         }
26515         
26516         var response = Roo.decode(xhr.responseText);
26517         
26518         this.fireEvent('upload', this, response);
26519         
26520     },
26521     
26522     xhrOnError : function()
26523     {
26524         if(this.loadMask){
26525             this.maskEl.unmask();
26526         }
26527         
26528         Roo.log('xhr on error');
26529         
26530         var response = Roo.decode(xhr.responseText);
26531           
26532         Roo.log(response);
26533         
26534     },
26535     
26536     prepare : function(file)
26537     {   
26538         if(this.loadMask){
26539             this.maskEl.mask(this.loadingText);
26540         }
26541         
26542         this.file = false;
26543         this.exif = {};
26544         
26545         if(typeof(file) === 'string'){
26546             this.loadCanvas(file);
26547             return;
26548         }
26549         
26550         if(!file || !this.urlAPI){
26551             return;
26552         }
26553         
26554         this.file = file;
26555         this.cropType = file.type;
26556         
26557         var _this = this;
26558         
26559         if(this.fireEvent('prepare', this, this.file) != false){
26560             
26561             var reader = new FileReader();
26562             
26563             reader.onload = function (e) {
26564                 if (e.target.error) {
26565                     Roo.log(e.target.error);
26566                     return;
26567                 }
26568                 
26569                 var buffer = e.target.result,
26570                     dataView = new DataView(buffer),
26571                     offset = 2,
26572                     maxOffset = dataView.byteLength - 4,
26573                     markerBytes,
26574                     markerLength;
26575                 
26576                 if (dataView.getUint16(0) === 0xffd8) {
26577                     while (offset < maxOffset) {
26578                         markerBytes = dataView.getUint16(offset);
26579                         
26580                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26581                             markerLength = dataView.getUint16(offset + 2) + 2;
26582                             if (offset + markerLength > dataView.byteLength) {
26583                                 Roo.log('Invalid meta data: Invalid segment size.');
26584                                 break;
26585                             }
26586                             
26587                             if(markerBytes == 0xffe1){
26588                                 _this.parseExifData(
26589                                     dataView,
26590                                     offset,
26591                                     markerLength
26592                                 );
26593                             }
26594                             
26595                             offset += markerLength;
26596                             
26597                             continue;
26598                         }
26599                         
26600                         break;
26601                     }
26602                     
26603                 }
26604                 
26605                 var url = _this.urlAPI.createObjectURL(_this.file);
26606                 
26607                 _this.loadCanvas(url);
26608                 
26609                 return;
26610             }
26611             
26612             reader.readAsArrayBuffer(this.file);
26613             
26614         }
26615         
26616     },
26617     
26618     parseExifData : function(dataView, offset, length)
26619     {
26620         var tiffOffset = offset + 10,
26621             littleEndian,
26622             dirOffset;
26623     
26624         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26625             // No Exif data, might be XMP data instead
26626             return;
26627         }
26628         
26629         // Check for the ASCII code for "Exif" (0x45786966):
26630         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26631             // No Exif data, might be XMP data instead
26632             return;
26633         }
26634         if (tiffOffset + 8 > dataView.byteLength) {
26635             Roo.log('Invalid Exif data: Invalid segment size.');
26636             return;
26637         }
26638         // Check for the two null bytes:
26639         if (dataView.getUint16(offset + 8) !== 0x0000) {
26640             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26641             return;
26642         }
26643         // Check the byte alignment:
26644         switch (dataView.getUint16(tiffOffset)) {
26645         case 0x4949:
26646             littleEndian = true;
26647             break;
26648         case 0x4D4D:
26649             littleEndian = false;
26650             break;
26651         default:
26652             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26653             return;
26654         }
26655         // Check for the TIFF tag marker (0x002A):
26656         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26657             Roo.log('Invalid Exif data: Missing TIFF marker.');
26658             return;
26659         }
26660         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26661         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26662         
26663         this.parseExifTags(
26664             dataView,
26665             tiffOffset,
26666             tiffOffset + dirOffset,
26667             littleEndian
26668         );
26669     },
26670     
26671     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26672     {
26673         var tagsNumber,
26674             dirEndOffset,
26675             i;
26676         if (dirOffset + 6 > dataView.byteLength) {
26677             Roo.log('Invalid Exif data: Invalid directory offset.');
26678             return;
26679         }
26680         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26681         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26682         if (dirEndOffset + 4 > dataView.byteLength) {
26683             Roo.log('Invalid Exif data: Invalid directory size.');
26684             return;
26685         }
26686         for (i = 0; i < tagsNumber; i += 1) {
26687             this.parseExifTag(
26688                 dataView,
26689                 tiffOffset,
26690                 dirOffset + 2 + 12 * i, // tag offset
26691                 littleEndian
26692             );
26693         }
26694         // Return the offset to the next directory:
26695         return dataView.getUint32(dirEndOffset, littleEndian);
26696     },
26697     
26698     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26699     {
26700         var tag = dataView.getUint16(offset, littleEndian);
26701         
26702         this.exif[tag] = this.getExifValue(
26703             dataView,
26704             tiffOffset,
26705             offset,
26706             dataView.getUint16(offset + 2, littleEndian), // tag type
26707             dataView.getUint32(offset + 4, littleEndian), // tag length
26708             littleEndian
26709         );
26710     },
26711     
26712     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26713     {
26714         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26715             tagSize,
26716             dataOffset,
26717             values,
26718             i,
26719             str,
26720             c;
26721     
26722         if (!tagType) {
26723             Roo.log('Invalid Exif data: Invalid tag type.');
26724             return;
26725         }
26726         
26727         tagSize = tagType.size * length;
26728         // Determine if the value is contained in the dataOffset bytes,
26729         // or if the value at the dataOffset is a pointer to the actual data:
26730         dataOffset = tagSize > 4 ?
26731                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26732         if (dataOffset + tagSize > dataView.byteLength) {
26733             Roo.log('Invalid Exif data: Invalid data offset.');
26734             return;
26735         }
26736         if (length === 1) {
26737             return tagType.getValue(dataView, dataOffset, littleEndian);
26738         }
26739         values = [];
26740         for (i = 0; i < length; i += 1) {
26741             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26742         }
26743         
26744         if (tagType.ascii) {
26745             str = '';
26746             // Concatenate the chars:
26747             for (i = 0; i < values.length; i += 1) {
26748                 c = values[i];
26749                 // Ignore the terminating NULL byte(s):
26750                 if (c === '\u0000') {
26751                     break;
26752                 }
26753                 str += c;
26754             }
26755             return str;
26756         }
26757         return values;
26758     }
26759     
26760 });
26761
26762 Roo.apply(Roo.bootstrap.UploadCropbox, {
26763     tags : {
26764         'Orientation': 0x0112
26765     },
26766     
26767     Orientation: {
26768             1: 0, //'top-left',
26769 //            2: 'top-right',
26770             3: 180, //'bottom-right',
26771 //            4: 'bottom-left',
26772 //            5: 'left-top',
26773             6: 90, //'right-top',
26774 //            7: 'right-bottom',
26775             8: 270 //'left-bottom'
26776     },
26777     
26778     exifTagTypes : {
26779         // byte, 8-bit unsigned int:
26780         1: {
26781             getValue: function (dataView, dataOffset) {
26782                 return dataView.getUint8(dataOffset);
26783             },
26784             size: 1
26785         },
26786         // ascii, 8-bit byte:
26787         2: {
26788             getValue: function (dataView, dataOffset) {
26789                 return String.fromCharCode(dataView.getUint8(dataOffset));
26790             },
26791             size: 1,
26792             ascii: true
26793         },
26794         // short, 16 bit int:
26795         3: {
26796             getValue: function (dataView, dataOffset, littleEndian) {
26797                 return dataView.getUint16(dataOffset, littleEndian);
26798             },
26799             size: 2
26800         },
26801         // long, 32 bit int:
26802         4: {
26803             getValue: function (dataView, dataOffset, littleEndian) {
26804                 return dataView.getUint32(dataOffset, littleEndian);
26805             },
26806             size: 4
26807         },
26808         // rational = two long values, first is numerator, second is denominator:
26809         5: {
26810             getValue: function (dataView, dataOffset, littleEndian) {
26811                 return dataView.getUint32(dataOffset, littleEndian) /
26812                     dataView.getUint32(dataOffset + 4, littleEndian);
26813             },
26814             size: 8
26815         },
26816         // slong, 32 bit signed int:
26817         9: {
26818             getValue: function (dataView, dataOffset, littleEndian) {
26819                 return dataView.getInt32(dataOffset, littleEndian);
26820             },
26821             size: 4
26822         },
26823         // srational, two slongs, first is numerator, second is denominator:
26824         10: {
26825             getValue: function (dataView, dataOffset, littleEndian) {
26826                 return dataView.getInt32(dataOffset, littleEndian) /
26827                     dataView.getInt32(dataOffset + 4, littleEndian);
26828             },
26829             size: 8
26830         }
26831     },
26832     
26833     footer : {
26834         STANDARD : [
26835             {
26836                 tag : 'div',
26837                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26838                 action : 'rotate-left',
26839                 cn : [
26840                     {
26841                         tag : 'button',
26842                         cls : 'btn btn-default',
26843                         html : '<i class="fa fa-undo"></i>'
26844                     }
26845                 ]
26846             },
26847             {
26848                 tag : 'div',
26849                 cls : 'btn-group roo-upload-cropbox-picture',
26850                 action : 'picture',
26851                 cn : [
26852                     {
26853                         tag : 'button',
26854                         cls : 'btn btn-default',
26855                         html : '<i class="fa fa-picture-o"></i>'
26856                     }
26857                 ]
26858             },
26859             {
26860                 tag : 'div',
26861                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26862                 action : 'rotate-right',
26863                 cn : [
26864                     {
26865                         tag : 'button',
26866                         cls : 'btn btn-default',
26867                         html : '<i class="fa fa-repeat"></i>'
26868                     }
26869                 ]
26870             }
26871         ],
26872         DOCUMENT : [
26873             {
26874                 tag : 'div',
26875                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26876                 action : 'rotate-left',
26877                 cn : [
26878                     {
26879                         tag : 'button',
26880                         cls : 'btn btn-default',
26881                         html : '<i class="fa fa-undo"></i>'
26882                     }
26883                 ]
26884             },
26885             {
26886                 tag : 'div',
26887                 cls : 'btn-group roo-upload-cropbox-download',
26888                 action : 'download',
26889                 cn : [
26890                     {
26891                         tag : 'button',
26892                         cls : 'btn btn-default',
26893                         html : '<i class="fa fa-download"></i>'
26894                     }
26895                 ]
26896             },
26897             {
26898                 tag : 'div',
26899                 cls : 'btn-group roo-upload-cropbox-crop',
26900                 action : 'crop',
26901                 cn : [
26902                     {
26903                         tag : 'button',
26904                         cls : 'btn btn-default',
26905                         html : '<i class="fa fa-crop"></i>'
26906                     }
26907                 ]
26908             },
26909             {
26910                 tag : 'div',
26911                 cls : 'btn-group roo-upload-cropbox-trash',
26912                 action : 'trash',
26913                 cn : [
26914                     {
26915                         tag : 'button',
26916                         cls : 'btn btn-default',
26917                         html : '<i class="fa fa-trash"></i>'
26918                     }
26919                 ]
26920             },
26921             {
26922                 tag : 'div',
26923                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26924                 action : 'rotate-right',
26925                 cn : [
26926                     {
26927                         tag : 'button',
26928                         cls : 'btn btn-default',
26929                         html : '<i class="fa fa-repeat"></i>'
26930                     }
26931                 ]
26932             }
26933         ],
26934         ROTATOR : [
26935             {
26936                 tag : 'div',
26937                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26938                 action : 'rotate-left',
26939                 cn : [
26940                     {
26941                         tag : 'button',
26942                         cls : 'btn btn-default',
26943                         html : '<i class="fa fa-undo"></i>'
26944                     }
26945                 ]
26946             },
26947             {
26948                 tag : 'div',
26949                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26950                 action : 'rotate-right',
26951                 cn : [
26952                     {
26953                         tag : 'button',
26954                         cls : 'btn btn-default',
26955                         html : '<i class="fa fa-repeat"></i>'
26956                     }
26957                 ]
26958             }
26959         ]
26960     }
26961 });
26962
26963 /*
26964 * Licence: LGPL
26965 */
26966
26967 /**
26968  * @class Roo.bootstrap.DocumentManager
26969  * @extends Roo.bootstrap.Component
26970  * Bootstrap DocumentManager class
26971  * @cfg {String} paramName default 'imageUpload'
26972  * @cfg {String} method default POST
26973  * @cfg {String} url action url
26974  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26975  * @cfg {Boolean} multiple multiple upload default true
26976  * @cfg {Number} thumbSize default 300
26977  * @cfg {String} fieldLabel
26978  * @cfg {Number} labelWidth default 4
26979  * @cfg {String} labelAlign (left|top) default left
26980  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26981  * 
26982  * @constructor
26983  * Create a new DocumentManager
26984  * @param {Object} config The config object
26985  */
26986
26987 Roo.bootstrap.DocumentManager = function(config){
26988     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26989     
26990     this.addEvents({
26991         /**
26992          * @event initial
26993          * Fire when initial the DocumentManager
26994          * @param {Roo.bootstrap.DocumentManager} this
26995          */
26996         "initial" : true,
26997         /**
26998          * @event inspect
26999          * inspect selected file
27000          * @param {Roo.bootstrap.DocumentManager} this
27001          * @param {File} file
27002          */
27003         "inspect" : true,
27004         /**
27005          * @event exception
27006          * Fire when xhr load exception
27007          * @param {Roo.bootstrap.DocumentManager} this
27008          * @param {XMLHttpRequest} xhr
27009          */
27010         "exception" : true,
27011         /**
27012          * @event prepare
27013          * prepare the form data
27014          * @param {Roo.bootstrap.DocumentManager} this
27015          * @param {Object} formData
27016          */
27017         "prepare" : true,
27018         /**
27019          * @event remove
27020          * Fire when remove the file
27021          * @param {Roo.bootstrap.DocumentManager} this
27022          * @param {Object} file
27023          */
27024         "remove" : true,
27025         /**
27026          * @event refresh
27027          * Fire after refresh the file
27028          * @param {Roo.bootstrap.DocumentManager} this
27029          */
27030         "refresh" : true,
27031         /**
27032          * @event click
27033          * Fire after click the image
27034          * @param {Roo.bootstrap.DocumentManager} this
27035          * @param {Object} file
27036          */
27037         "click" : true,
27038         /**
27039          * @event edit
27040          * Fire when upload a image and editable set to true
27041          * @param {Roo.bootstrap.DocumentManager} this
27042          * @param {Object} file
27043          */
27044         "edit" : true,
27045         /**
27046          * @event beforeselectfile
27047          * Fire before select file
27048          * @param {Roo.bootstrap.DocumentManager} this
27049          */
27050         "beforeselectfile" : true,
27051         /**
27052          * @event process
27053          * Fire before process file
27054          * @param {Roo.bootstrap.DocumentManager} this
27055          * @param {Object} file
27056          */
27057         "process" : true
27058         
27059     });
27060 };
27061
27062 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27063     
27064     boxes : 0,
27065     inputName : '',
27066     thumbSize : 300,
27067     multiple : true,
27068     files : [],
27069     method : 'POST',
27070     url : '',
27071     paramName : 'imageUpload',
27072     fieldLabel : '',
27073     labelWidth : 4,
27074     labelAlign : 'left',
27075     editable : true,
27076     delegates : [],
27077     
27078     
27079     xhr : false, 
27080     
27081     getAutoCreate : function()
27082     {   
27083         var managerWidget = {
27084             tag : 'div',
27085             cls : 'roo-document-manager',
27086             cn : [
27087                 {
27088                     tag : 'input',
27089                     cls : 'roo-document-manager-selector',
27090                     type : 'file'
27091                 },
27092                 {
27093                     tag : 'div',
27094                     cls : 'roo-document-manager-uploader',
27095                     cn : [
27096                         {
27097                             tag : 'div',
27098                             cls : 'roo-document-manager-upload-btn',
27099                             html : '<i class="fa fa-plus"></i>'
27100                         }
27101                     ]
27102                     
27103                 }
27104             ]
27105         };
27106         
27107         var content = [
27108             {
27109                 tag : 'div',
27110                 cls : 'column col-md-12',
27111                 cn : managerWidget
27112             }
27113         ];
27114         
27115         if(this.fieldLabel.length){
27116             
27117             content = [
27118                 {
27119                     tag : 'div',
27120                     cls : 'column col-md-12',
27121                     html : this.fieldLabel
27122                 },
27123                 {
27124                     tag : 'div',
27125                     cls : 'column col-md-12',
27126                     cn : managerWidget
27127                 }
27128             ];
27129
27130             if(this.labelAlign == 'left'){
27131                 content = [
27132                     {
27133                         tag : 'div',
27134                         cls : 'column col-md-' + this.labelWidth,
27135                         html : this.fieldLabel
27136                     },
27137                     {
27138                         tag : 'div',
27139                         cls : 'column col-md-' + (12 - this.labelWidth),
27140                         cn : managerWidget
27141                     }
27142                 ];
27143                 
27144             }
27145         }
27146         
27147         var cfg = {
27148             tag : 'div',
27149             cls : 'row clearfix',
27150             cn : content
27151         };
27152         
27153         return cfg;
27154         
27155     },
27156     
27157     initEvents : function()
27158     {
27159         this.managerEl = this.el.select('.roo-document-manager', true).first();
27160         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27161         
27162         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27163         this.selectorEl.hide();
27164         
27165         if(this.multiple){
27166             this.selectorEl.attr('multiple', 'multiple');
27167         }
27168         
27169         this.selectorEl.on('change', this.onFileSelected, this);
27170         
27171         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27172         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27173         
27174         this.uploader.on('click', this.onUploaderClick, this);
27175         
27176         this.renderProgressDialog();
27177         
27178         var _this = this;
27179         
27180         window.addEventListener("resize", function() { _this.refresh(); } );
27181         
27182         this.fireEvent('initial', this);
27183     },
27184     
27185     renderProgressDialog : function()
27186     {
27187         var _this = this;
27188         
27189         this.progressDialog = new Roo.bootstrap.Modal({
27190             cls : 'roo-document-manager-progress-dialog',
27191             allow_close : false,
27192             title : '',
27193             buttons : [
27194                 {
27195                     name  :'cancel',
27196                     weight : 'danger',
27197                     html : 'Cancel'
27198                 }
27199             ], 
27200             listeners : { 
27201                 btnclick : function() {
27202                     _this.uploadCancel();
27203                     this.hide();
27204                 }
27205             }
27206         });
27207          
27208         this.progressDialog.render(Roo.get(document.body));
27209          
27210         this.progress = new Roo.bootstrap.Progress({
27211             cls : 'roo-document-manager-progress',
27212             active : true,
27213             striped : true
27214         });
27215         
27216         this.progress.render(this.progressDialog.getChildContainer());
27217         
27218         this.progressBar = new Roo.bootstrap.ProgressBar({
27219             cls : 'roo-document-manager-progress-bar',
27220             aria_valuenow : 0,
27221             aria_valuemin : 0,
27222             aria_valuemax : 12,
27223             panel : 'success'
27224         });
27225         
27226         this.progressBar.render(this.progress.getChildContainer());
27227     },
27228     
27229     onUploaderClick : function(e)
27230     {
27231         e.preventDefault();
27232      
27233         if(this.fireEvent('beforeselectfile', this) != false){
27234             this.selectorEl.dom.click();
27235         }
27236         
27237     },
27238     
27239     onFileSelected : function(e)
27240     {
27241         e.preventDefault();
27242         
27243         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27244             return;
27245         }
27246         
27247         Roo.each(this.selectorEl.dom.files, function(file){
27248             if(this.fireEvent('inspect', this, file) != false){
27249                 this.files.push(file);
27250             }
27251         }, this);
27252         
27253         this.queue();
27254         
27255     },
27256     
27257     queue : function()
27258     {
27259         this.selectorEl.dom.value = '';
27260         
27261         if(!this.files.length){
27262             return;
27263         }
27264         
27265         if(this.boxes > 0 && this.files.length > this.boxes){
27266             this.files = this.files.slice(0, this.boxes);
27267         }
27268         
27269         this.uploader.show();
27270         
27271         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27272             this.uploader.hide();
27273         }
27274         
27275         var _this = this;
27276         
27277         var files = [];
27278         
27279         var docs = [];
27280         
27281         Roo.each(this.files, function(file){
27282             
27283             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27284                 var f = this.renderPreview(file);
27285                 files.push(f);
27286                 return;
27287             }
27288             
27289             if(file.type.indexOf('image') != -1){
27290                 this.delegates.push(
27291                     (function(){
27292                         _this.process(file);
27293                     }).createDelegate(this)
27294                 );
27295         
27296                 return;
27297             }
27298             
27299             docs.push(
27300                 (function(){
27301                     _this.process(file);
27302                 }).createDelegate(this)
27303             );
27304             
27305         }, this);
27306         
27307         this.files = files;
27308         
27309         this.delegates = this.delegates.concat(docs);
27310         
27311         if(!this.delegates.length){
27312             this.refresh();
27313             return;
27314         }
27315         
27316         this.progressBar.aria_valuemax = this.delegates.length;
27317         
27318         this.arrange();
27319         
27320         return;
27321     },
27322     
27323     arrange : function()
27324     {
27325         if(!this.delegates.length){
27326             this.progressDialog.hide();
27327             this.refresh();
27328             return;
27329         }
27330         
27331         var delegate = this.delegates.shift();
27332         
27333         this.progressDialog.show();
27334         
27335         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27336         
27337         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27338         
27339         delegate();
27340     },
27341     
27342     refresh : function()
27343     {
27344         this.uploader.show();
27345         
27346         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27347             this.uploader.hide();
27348         }
27349         
27350         Roo.isTouch ? this.closable(false) : this.closable(true);
27351         
27352         this.fireEvent('refresh', this);
27353     },
27354     
27355     onRemove : function(e, el, o)
27356     {
27357         e.preventDefault();
27358         
27359         this.fireEvent('remove', this, o);
27360         
27361     },
27362     
27363     remove : function(o)
27364     {
27365         var files = [];
27366         
27367         Roo.each(this.files, function(file){
27368             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27369                 files.push(file);
27370                 return;
27371             }
27372
27373             o.target.remove();
27374
27375         }, this);
27376         
27377         this.files = files;
27378         
27379         this.refresh();
27380     },
27381     
27382     clear : function()
27383     {
27384         Roo.each(this.files, function(file){
27385             if(!file.target){
27386                 return;
27387             }
27388             
27389             file.target.remove();
27390
27391         }, this);
27392         
27393         this.files = [];
27394         
27395         this.refresh();
27396     },
27397     
27398     onClick : function(e, el, o)
27399     {
27400         e.preventDefault();
27401         
27402         this.fireEvent('click', this, o);
27403         
27404     },
27405     
27406     closable : function(closable)
27407     {
27408         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27409             
27410             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27411             
27412             if(closable){
27413                 el.show();
27414                 return;
27415             }
27416             
27417             el.hide();
27418             
27419         }, this);
27420     },
27421     
27422     xhrOnLoad : function(xhr)
27423     {
27424         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27425             el.remove();
27426         }, this);
27427         
27428         if (xhr.readyState !== 4) {
27429             this.arrange();
27430             this.fireEvent('exception', this, xhr);
27431             return;
27432         }
27433
27434         var response = Roo.decode(xhr.responseText);
27435         
27436         if(!response.success){
27437             this.arrange();
27438             this.fireEvent('exception', this, xhr);
27439             return;
27440         }
27441         
27442         var file = this.renderPreview(response.data);
27443         
27444         this.files.push(file);
27445         
27446         this.arrange();
27447         
27448     },
27449     
27450     xhrOnError : function(xhr)
27451     {
27452         Roo.log('xhr on error');
27453         
27454         var response = Roo.decode(xhr.responseText);
27455           
27456         Roo.log(response);
27457         
27458         this.arrange();
27459     },
27460     
27461     process : function(file)
27462     {
27463         if(this.fireEvent('process', this, file) !== false){
27464             if(this.editable && file.type.indexOf('image') != -1){
27465                 this.fireEvent('edit', this, file);
27466                 return;
27467             }
27468
27469             this.uploadStart(file, false);
27470
27471             return;
27472         }
27473         
27474     },
27475     
27476     uploadStart : function(file, crop)
27477     {
27478         this.xhr = new XMLHttpRequest();
27479         
27480         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27481             this.arrange();
27482             return;
27483         }
27484         
27485         file.xhr = this.xhr;
27486             
27487         this.managerEl.createChild({
27488             tag : 'div',
27489             cls : 'roo-document-manager-loading',
27490             cn : [
27491                 {
27492                     tag : 'div',
27493                     tooltip : file.name,
27494                     cls : 'roo-document-manager-thumb',
27495                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27496                 }
27497             ]
27498
27499         });
27500
27501         this.xhr.open(this.method, this.url, true);
27502         
27503         var headers = {
27504             "Accept": "application/json",
27505             "Cache-Control": "no-cache",
27506             "X-Requested-With": "XMLHttpRequest"
27507         };
27508         
27509         for (var headerName in headers) {
27510             var headerValue = headers[headerName];
27511             if (headerValue) {
27512                 this.xhr.setRequestHeader(headerName, headerValue);
27513             }
27514         }
27515         
27516         var _this = this;
27517         
27518         this.xhr.onload = function()
27519         {
27520             _this.xhrOnLoad(_this.xhr);
27521         }
27522         
27523         this.xhr.onerror = function()
27524         {
27525             _this.xhrOnError(_this.xhr);
27526         }
27527         
27528         var formData = new FormData();
27529
27530         formData.append('returnHTML', 'NO');
27531         
27532         if(crop){
27533             formData.append('crop', crop);
27534         }
27535         
27536         formData.append(this.paramName, file, file.name);
27537         
27538         if(this.fireEvent('prepare', this, formData) != false){
27539             this.xhr.send(formData);
27540         };
27541     },
27542     
27543     uploadCancel : function()
27544     {
27545         if (this.xhr) {
27546             this.xhr.abort();
27547         }
27548         
27549         
27550         this.delegates = [];
27551         
27552         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27553             el.remove();
27554         }, this);
27555         
27556         this.arrange();
27557     },
27558     
27559     renderPreview : function(file)
27560     {
27561         if(typeof(file.target) != 'undefined' && file.target){
27562             return file;
27563         }
27564         
27565         var previewEl = this.managerEl.createChild({
27566             tag : 'div',
27567             cls : 'roo-document-manager-preview',
27568             cn : [
27569                 {
27570                     tag : 'div',
27571                     tooltip : file.filename,
27572                     cls : 'roo-document-manager-thumb',
27573                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27574                 },
27575                 {
27576                     tag : 'button',
27577                     cls : 'close',
27578                     html : '<i class="fa fa-times-circle"></i>'
27579                 }
27580             ]
27581         });
27582
27583         var close = previewEl.select('button.close', true).first();
27584
27585         close.on('click', this.onRemove, this, file);
27586
27587         file.target = previewEl;
27588
27589         var image = previewEl.select('img', true).first();
27590         
27591         var _this = this;
27592         
27593         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27594         
27595         image.on('click', this.onClick, this, file);
27596         
27597         return file;
27598         
27599     },
27600     
27601     onPreviewLoad : function(file, image)
27602     {
27603         if(typeof(file.target) == 'undefined' || !file.target){
27604             return;
27605         }
27606         
27607         var width = image.dom.naturalWidth || image.dom.width;
27608         var height = image.dom.naturalHeight || image.dom.height;
27609         
27610         if(width > height){
27611             file.target.addClass('wide');
27612             return;
27613         }
27614         
27615         file.target.addClass('tall');
27616         return;
27617         
27618     },
27619     
27620     uploadFromSource : function(file, crop)
27621     {
27622         this.xhr = new XMLHttpRequest();
27623         
27624         this.managerEl.createChild({
27625             tag : 'div',
27626             cls : 'roo-document-manager-loading',
27627             cn : [
27628                 {
27629                     tag : 'div',
27630                     tooltip : file.name,
27631                     cls : 'roo-document-manager-thumb',
27632                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27633                 }
27634             ]
27635
27636         });
27637
27638         this.xhr.open(this.method, this.url, true);
27639         
27640         var headers = {
27641             "Accept": "application/json",
27642             "Cache-Control": "no-cache",
27643             "X-Requested-With": "XMLHttpRequest"
27644         };
27645         
27646         for (var headerName in headers) {
27647             var headerValue = headers[headerName];
27648             if (headerValue) {
27649                 this.xhr.setRequestHeader(headerName, headerValue);
27650             }
27651         }
27652         
27653         var _this = this;
27654         
27655         this.xhr.onload = function()
27656         {
27657             _this.xhrOnLoad(_this.xhr);
27658         }
27659         
27660         this.xhr.onerror = function()
27661         {
27662             _this.xhrOnError(_this.xhr);
27663         }
27664         
27665         var formData = new FormData();
27666
27667         formData.append('returnHTML', 'NO');
27668         
27669         formData.append('crop', crop);
27670         
27671         if(typeof(file.filename) != 'undefined'){
27672             formData.append('filename', file.filename);
27673         }
27674         
27675         if(typeof(file.mimetype) != 'undefined'){
27676             formData.append('mimetype', file.mimetype);
27677         }
27678         
27679         if(this.fireEvent('prepare', this, formData) != false){
27680             this.xhr.send(formData);
27681         };
27682     }
27683 });
27684
27685 /*
27686 * Licence: LGPL
27687 */
27688
27689 /**
27690  * @class Roo.bootstrap.DocumentViewer
27691  * @extends Roo.bootstrap.Component
27692  * Bootstrap DocumentViewer class
27693  * 
27694  * @constructor
27695  * Create a new DocumentViewer
27696  * @param {Object} config The config object
27697  */
27698
27699 Roo.bootstrap.DocumentViewer = function(config){
27700     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27701     
27702     this.addEvents({
27703         /**
27704          * @event initial
27705          * Fire after initEvent
27706          * @param {Roo.bootstrap.DocumentViewer} this
27707          */
27708         "initial" : true,
27709         /**
27710          * @event click
27711          * Fire after click
27712          * @param {Roo.bootstrap.DocumentViewer} this
27713          */
27714         "click" : true,
27715         /**
27716          * @event trash
27717          * Fire after trash button
27718          * @param {Roo.bootstrap.DocumentViewer} this
27719          */
27720         "trash" : true
27721         
27722     });
27723 };
27724
27725 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27726     
27727     getAutoCreate : function()
27728     {
27729         var cfg = {
27730             tag : 'div',
27731             cls : 'roo-document-viewer',
27732             cn : [
27733                 {
27734                     tag : 'div',
27735                     cls : 'roo-document-viewer-body',
27736                     cn : [
27737                         {
27738                             tag : 'div',
27739                             cls : 'roo-document-viewer-thumb',
27740                             cn : [
27741                                 {
27742                                     tag : 'img',
27743                                     cls : 'roo-document-viewer-image'
27744                                 }
27745                             ]
27746                         }
27747                     ]
27748                 },
27749                 {
27750                     tag : 'div',
27751                     cls : 'roo-document-viewer-footer',
27752                     cn : {
27753                         tag : 'div',
27754                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27755                         cn : [
27756                             {
27757                                 tag : 'div',
27758                                 cls : 'btn-group',
27759                                 cn : [
27760                                     {
27761                                         tag : 'button',
27762                                         cls : 'btn btn-default roo-document-viewer-trash',
27763                                         html : '<i class="fa fa-trash"></i>'
27764                                     }
27765                                 ]
27766                             }
27767                         ]
27768                     }
27769                 }
27770             ]
27771         };
27772         
27773         return cfg;
27774     },
27775     
27776     initEvents : function()
27777     {
27778         
27779         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27780         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27781         
27782         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27783         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27784         
27785         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27786         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27787         
27788         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27789         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27790         
27791         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27792         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27793         
27794         this.bodyEl.on('click', this.onClick, this);
27795         
27796         this.trashBtn.on('click', this.onTrash, this);
27797         
27798     },
27799     
27800     initial : function()
27801     {
27802 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27803         
27804         
27805         this.fireEvent('initial', this);
27806         
27807     },
27808     
27809     onClick : function(e)
27810     {
27811         e.preventDefault();
27812         
27813         this.fireEvent('click', this);
27814     },
27815     
27816     onTrash : function(e)
27817     {
27818         e.preventDefault();
27819         
27820         this.fireEvent('trash', this);
27821     }
27822     
27823 });
27824 /*
27825  * - LGPL
27826  *
27827  * nav progress bar
27828  * 
27829  */
27830
27831 /**
27832  * @class Roo.bootstrap.NavProgressBar
27833  * @extends Roo.bootstrap.Component
27834  * Bootstrap NavProgressBar class
27835  * 
27836  * @constructor
27837  * Create a new nav progress bar
27838  * @param {Object} config The config object
27839  */
27840
27841 Roo.bootstrap.NavProgressBar = function(config){
27842     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27843
27844     this.bullets = this.bullets || [];
27845    
27846 //    Roo.bootstrap.NavProgressBar.register(this);
27847      this.addEvents({
27848         /**
27849              * @event changed
27850              * Fires when the active item changes
27851              * @param {Roo.bootstrap.NavProgressBar} this
27852              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27853              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27854          */
27855         'changed': true
27856      });
27857     
27858 };
27859
27860 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27861     
27862     bullets : [],
27863     barItems : [],
27864     
27865     getAutoCreate : function()
27866     {
27867         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27868         
27869         cfg = {
27870             tag : 'div',
27871             cls : 'roo-navigation-bar-group',
27872             cn : [
27873                 {
27874                     tag : 'div',
27875                     cls : 'roo-navigation-top-bar'
27876                 },
27877                 {
27878                     tag : 'div',
27879                     cls : 'roo-navigation-bullets-bar',
27880                     cn : [
27881                         {
27882                             tag : 'ul',
27883                             cls : 'roo-navigation-bar'
27884                         }
27885                     ]
27886                 },
27887                 
27888                 {
27889                     tag : 'div',
27890                     cls : 'roo-navigation-bottom-bar'
27891                 }
27892             ]
27893             
27894         };
27895         
27896         return cfg;
27897         
27898     },
27899     
27900     initEvents: function() 
27901     {
27902         
27903     },
27904     
27905     onRender : function(ct, position) 
27906     {
27907         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27908         
27909         if(this.bullets.length){
27910             Roo.each(this.bullets, function(b){
27911                this.addItem(b);
27912             }, this);
27913         }
27914         
27915         this.format();
27916         
27917     },
27918     
27919     addItem : function(cfg)
27920     {
27921         var item = new Roo.bootstrap.NavProgressItem(cfg);
27922         
27923         item.parentId = this.id;
27924         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27925         
27926         if(cfg.html){
27927             var top = new Roo.bootstrap.Element({
27928                 tag : 'div',
27929                 cls : 'roo-navigation-bar-text'
27930             });
27931             
27932             var bottom = new Roo.bootstrap.Element({
27933                 tag : 'div',
27934                 cls : 'roo-navigation-bar-text'
27935             });
27936             
27937             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27938             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27939             
27940             var topText = new Roo.bootstrap.Element({
27941                 tag : 'span',
27942                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27943             });
27944             
27945             var bottomText = new Roo.bootstrap.Element({
27946                 tag : 'span',
27947                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27948             });
27949             
27950             topText.onRender(top.el, null);
27951             bottomText.onRender(bottom.el, null);
27952             
27953             item.topEl = top;
27954             item.bottomEl = bottom;
27955         }
27956         
27957         this.barItems.push(item);
27958         
27959         return item;
27960     },
27961     
27962     getActive : function()
27963     {
27964         var active = false;
27965         
27966         Roo.each(this.barItems, function(v){
27967             
27968             if (!v.isActive()) {
27969                 return;
27970             }
27971             
27972             active = v;
27973             return false;
27974             
27975         });
27976         
27977         return active;
27978     },
27979     
27980     setActiveItem : function(item)
27981     {
27982         var prev = false;
27983         
27984         Roo.each(this.barItems, function(v){
27985             if (v.rid == item.rid) {
27986                 return ;
27987             }
27988             
27989             if (v.isActive()) {
27990                 v.setActive(false);
27991                 prev = v;
27992             }
27993         });
27994
27995         item.setActive(true);
27996         
27997         this.fireEvent('changed', this, item, prev);
27998     },
27999     
28000     getBarItem: function(rid)
28001     {
28002         var ret = false;
28003         
28004         Roo.each(this.barItems, function(e) {
28005             if (e.rid != rid) {
28006                 return;
28007             }
28008             
28009             ret =  e;
28010             return false;
28011         });
28012         
28013         return ret;
28014     },
28015     
28016     indexOfItem : function(item)
28017     {
28018         var index = false;
28019         
28020         Roo.each(this.barItems, function(v, i){
28021             
28022             if (v.rid != item.rid) {
28023                 return;
28024             }
28025             
28026             index = i;
28027             return false
28028         });
28029         
28030         return index;
28031     },
28032     
28033     setActiveNext : function()
28034     {
28035         var i = this.indexOfItem(this.getActive());
28036         
28037         if (i > this.barItems.length) {
28038             return;
28039         }
28040         
28041         this.setActiveItem(this.barItems[i+1]);
28042     },
28043     
28044     setActivePrev : function()
28045     {
28046         var i = this.indexOfItem(this.getActive());
28047         
28048         if (i  < 1) {
28049             return;
28050         }
28051         
28052         this.setActiveItem(this.barItems[i-1]);
28053     },
28054     
28055     format : function()
28056     {
28057         if(!this.barItems.length){
28058             return;
28059         }
28060      
28061         var width = 100 / this.barItems.length;
28062         
28063         Roo.each(this.barItems, function(i){
28064             i.el.setStyle('width', width + '%');
28065             i.topEl.el.setStyle('width', width + '%');
28066             i.bottomEl.el.setStyle('width', width + '%');
28067         }, this);
28068         
28069     }
28070     
28071 });
28072 /*
28073  * - LGPL
28074  *
28075  * Nav Progress Item
28076  * 
28077  */
28078
28079 /**
28080  * @class Roo.bootstrap.NavProgressItem
28081  * @extends Roo.bootstrap.Component
28082  * Bootstrap NavProgressItem class
28083  * @cfg {String} rid the reference id
28084  * @cfg {Boolean} active (true|false) Is item active default false
28085  * @cfg {Boolean} disabled (true|false) Is item active default false
28086  * @cfg {String} html
28087  * @cfg {String} position (top|bottom) text position default bottom
28088  * @cfg {String} icon show icon instead of number
28089  * 
28090  * @constructor
28091  * Create a new NavProgressItem
28092  * @param {Object} config The config object
28093  */
28094 Roo.bootstrap.NavProgressItem = function(config){
28095     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28096     this.addEvents({
28097         // raw events
28098         /**
28099          * @event click
28100          * The raw click event for the entire grid.
28101          * @param {Roo.bootstrap.NavProgressItem} this
28102          * @param {Roo.EventObject} e
28103          */
28104         "click" : true
28105     });
28106    
28107 };
28108
28109 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28110     
28111     rid : '',
28112     active : false,
28113     disabled : false,
28114     html : '',
28115     position : 'bottom',
28116     icon : false,
28117     
28118     getAutoCreate : function()
28119     {
28120         var iconCls = 'roo-navigation-bar-item-icon';
28121         
28122         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28123         
28124         var cfg = {
28125             tag: 'li',
28126             cls: 'roo-navigation-bar-item',
28127             cn : [
28128                 {
28129                     tag : 'i',
28130                     cls : iconCls
28131                 }
28132             ]
28133         };
28134         
28135         if(this.active){
28136             cfg.cls += ' active';
28137         }
28138         if(this.disabled){
28139             cfg.cls += ' disabled';
28140         }
28141         
28142         return cfg;
28143     },
28144     
28145     disable : function()
28146     {
28147         this.setDisabled(true);
28148     },
28149     
28150     enable : function()
28151     {
28152         this.setDisabled(false);
28153     },
28154     
28155     initEvents: function() 
28156     {
28157         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28158         
28159         this.iconEl.on('click', this.onClick, this);
28160     },
28161     
28162     onClick : function(e)
28163     {
28164         e.preventDefault();
28165         
28166         if(this.disabled){
28167             return;
28168         }
28169         
28170         if(this.fireEvent('click', this, e) === false){
28171             return;
28172         };
28173         
28174         this.parent().setActiveItem(this);
28175     },
28176     
28177     isActive: function () 
28178     {
28179         return this.active;
28180     },
28181     
28182     setActive : function(state)
28183     {
28184         if(this.active == state){
28185             return;
28186         }
28187         
28188         this.active = state;
28189         
28190         if (state) {
28191             this.el.addClass('active');
28192             return;
28193         }
28194         
28195         this.el.removeClass('active');
28196         
28197         return;
28198     },
28199     
28200     setDisabled : function(state)
28201     {
28202         if(this.disabled == state){
28203             return;
28204         }
28205         
28206         this.disabled = state;
28207         
28208         if (state) {
28209             this.el.addClass('disabled');
28210             return;
28211         }
28212         
28213         this.el.removeClass('disabled');
28214     },
28215     
28216     tooltipEl : function()
28217     {
28218         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28219     }
28220 });
28221  
28222
28223  /*
28224  * - LGPL
28225  *
28226  * FieldLabel
28227  * 
28228  */
28229
28230 /**
28231  * @class Roo.bootstrap.FieldLabel
28232  * @extends Roo.bootstrap.Component
28233  * Bootstrap FieldLabel class
28234  * @cfg {String} html contents of the element
28235  * @cfg {String} tag tag of the element default label
28236  * @cfg {String} cls class of the element
28237  * @cfg {String} target label target 
28238  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28239  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28240  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28241  * @cfg {String} iconTooltip default "This field is required"
28242  * 
28243  * @constructor
28244  * Create a new FieldLabel
28245  * @param {Object} config The config object
28246  */
28247
28248 Roo.bootstrap.FieldLabel = function(config){
28249     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28250     
28251     this.addEvents({
28252             /**
28253              * @event invalid
28254              * Fires after the field has been marked as invalid.
28255              * @param {Roo.form.FieldLabel} this
28256              * @param {String} msg The validation message
28257              */
28258             invalid : true,
28259             /**
28260              * @event valid
28261              * Fires after the field has been validated with no errors.
28262              * @param {Roo.form.FieldLabel} this
28263              */
28264             valid : true
28265         });
28266 };
28267
28268 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28269     
28270     tag: 'label',
28271     cls: '',
28272     html: '',
28273     target: '',
28274     allowBlank : true,
28275     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28276     validClass : 'text-success fa fa-lg fa-check',
28277     iconTooltip : 'This field is required',
28278     
28279     getAutoCreate : function(){
28280         
28281         var cfg = {
28282             tag : this.tag,
28283             cls : 'roo-bootstrap-field-label ' + this.cls,
28284             for : this.target,
28285             cn : [
28286                 {
28287                     tag : 'i',
28288                     cls : '',
28289                     tooltip : this.iconTooltip
28290                 },
28291                 {
28292                     tag : 'span',
28293                     html : this.html
28294                 }
28295             ] 
28296         };
28297         
28298         return cfg;
28299     },
28300     
28301     initEvents: function() 
28302     {
28303         Roo.bootstrap.Element.superclass.initEvents.call(this);
28304         
28305         this.iconEl = this.el.select('i', true).first();
28306         
28307         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28308         
28309         Roo.bootstrap.FieldLabel.register(this);
28310     },
28311     
28312     /**
28313      * Mark this field as valid
28314      */
28315     markValid : function()
28316     {
28317         this.iconEl.show();
28318         
28319         this.iconEl.removeClass(this.invalidClass);
28320         
28321         this.iconEl.addClass(this.validClass);
28322         
28323         this.fireEvent('valid', this);
28324     },
28325     
28326     /**
28327      * Mark this field as invalid
28328      * @param {String} msg The validation message
28329      */
28330     markInvalid : function(msg)
28331     {
28332         this.iconEl.show();
28333         
28334         this.iconEl.removeClass(this.validClass);
28335         
28336         this.iconEl.addClass(this.invalidClass);
28337         
28338         this.fireEvent('invalid', this, msg);
28339     }
28340     
28341    
28342 });
28343
28344 Roo.apply(Roo.bootstrap.FieldLabel, {
28345     
28346     groups: {},
28347     
28348      /**
28349     * register a FieldLabel Group
28350     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28351     */
28352     register : function(label)
28353     {
28354         if(this.groups.hasOwnProperty(label.target)){
28355             return;
28356         }
28357      
28358         this.groups[label.target] = label;
28359         
28360     },
28361     /**
28362     * fetch a FieldLabel Group based on the target
28363     * @param {string} target
28364     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28365     */
28366     get: function(target) {
28367         if (typeof(this.groups[target]) == 'undefined') {
28368             return false;
28369         }
28370         
28371         return this.groups[target] ;
28372     }
28373 });
28374
28375  
28376
28377  /*
28378  * - LGPL
28379  *
28380  * page DateSplitField.
28381  * 
28382  */
28383
28384
28385 /**
28386  * @class Roo.bootstrap.DateSplitField
28387  * @extends Roo.bootstrap.Component
28388  * Bootstrap DateSplitField class
28389  * @cfg {string} fieldLabel - the label associated
28390  * @cfg {Number} labelWidth set the width of label (0-12)
28391  * @cfg {String} labelAlign (top|left)
28392  * @cfg {Boolean} dayAllowBlank (true|false) default false
28393  * @cfg {Boolean} monthAllowBlank (true|false) default false
28394  * @cfg {Boolean} yearAllowBlank (true|false) default false
28395  * @cfg {string} dayPlaceholder 
28396  * @cfg {string} monthPlaceholder
28397  * @cfg {string} yearPlaceholder
28398  * @cfg {string} dayFormat default 'd'
28399  * @cfg {string} monthFormat default 'm'
28400  * @cfg {string} yearFormat default 'Y'
28401
28402  *     
28403  * @constructor
28404  * Create a new DateSplitField
28405  * @param {Object} config The config object
28406  */
28407
28408 Roo.bootstrap.DateSplitField = function(config){
28409     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28410     
28411     this.addEvents({
28412         // raw events
28413          /**
28414          * @event years
28415          * getting the data of years
28416          * @param {Roo.bootstrap.DateSplitField} this
28417          * @param {Object} years
28418          */
28419         "years" : true,
28420         /**
28421          * @event days
28422          * getting the data of days
28423          * @param {Roo.bootstrap.DateSplitField} this
28424          * @param {Object} days
28425          */
28426         "days" : true,
28427         /**
28428          * @event invalid
28429          * Fires after the field has been marked as invalid.
28430          * @param {Roo.form.Field} this
28431          * @param {String} msg The validation message
28432          */
28433         invalid : true,
28434        /**
28435          * @event valid
28436          * Fires after the field has been validated with no errors.
28437          * @param {Roo.form.Field} this
28438          */
28439         valid : true
28440     });
28441 };
28442
28443 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28444     
28445     fieldLabel : '',
28446     labelAlign : 'top',
28447     labelWidth : 3,
28448     dayAllowBlank : false,
28449     monthAllowBlank : false,
28450     yearAllowBlank : false,
28451     dayPlaceholder : '',
28452     monthPlaceholder : '',
28453     yearPlaceholder : '',
28454     dayFormat : 'd',
28455     monthFormat : 'm',
28456     yearFormat : 'Y',
28457     isFormField : true,
28458     
28459     getAutoCreate : function()
28460     {
28461         var cfg = {
28462             tag : 'div',
28463             cls : 'row roo-date-split-field-group',
28464             cn : [
28465                 {
28466                     tag : 'input',
28467                     type : 'hidden',
28468                     cls : 'form-hidden-field roo-date-split-field-group-value',
28469                     name : this.name
28470                 }
28471             ]
28472         };
28473         
28474         if(this.fieldLabel){
28475             cfg.cn.push({
28476                 tag : 'div',
28477                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28478                 cn : [
28479                     {
28480                         tag : 'label',
28481                         html : this.fieldLabel
28482                     }
28483                 ]
28484             });
28485         }
28486         
28487         Roo.each(['day', 'month', 'year'], function(t){
28488             cfg.cn.push({
28489                 tag : 'div',
28490                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28491             });
28492         }, this);
28493         
28494         return cfg;
28495     },
28496     
28497     inputEl: function ()
28498     {
28499         return this.el.select('.roo-date-split-field-group-value', true).first();
28500     },
28501     
28502     onRender : function(ct, position) 
28503     {
28504         var _this = this;
28505         
28506         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28507         
28508         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28509         
28510         this.dayField = new Roo.bootstrap.ComboBox({
28511             allowBlank : this.dayAllowBlank,
28512             alwaysQuery : true,
28513             displayField : 'value',
28514             editable : false,
28515             fieldLabel : '',
28516             forceSelection : true,
28517             mode : 'local',
28518             placeholder : this.dayPlaceholder,
28519             selectOnFocus : true,
28520             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28521             triggerAction : 'all',
28522             typeAhead : true,
28523             valueField : 'value',
28524             store : new Roo.data.SimpleStore({
28525                 data : (function() {    
28526                     var days = [];
28527                     _this.fireEvent('days', _this, days);
28528                     return days;
28529                 })(),
28530                 fields : [ 'value' ]
28531             }),
28532             listeners : {
28533                 select : function (_self, record, index)
28534                 {
28535                     _this.setValue(_this.getValue());
28536                 }
28537             }
28538         });
28539
28540         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28541         
28542         this.monthField = new Roo.bootstrap.MonthField({
28543             after : '<i class=\"fa fa-calendar\"></i>',
28544             allowBlank : this.monthAllowBlank,
28545             placeholder : this.monthPlaceholder,
28546             readOnly : true,
28547             listeners : {
28548                 render : function (_self)
28549                 {
28550                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28551                         e.preventDefault();
28552                         _self.focus();
28553                     });
28554                 },
28555                 select : function (_self, oldvalue, newvalue)
28556                 {
28557                     _this.setValue(_this.getValue());
28558                 }
28559             }
28560         });
28561         
28562         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28563         
28564         this.yearField = new Roo.bootstrap.ComboBox({
28565             allowBlank : this.yearAllowBlank,
28566             alwaysQuery : true,
28567             displayField : 'value',
28568             editable : false,
28569             fieldLabel : '',
28570             forceSelection : true,
28571             mode : 'local',
28572             placeholder : this.yearPlaceholder,
28573             selectOnFocus : true,
28574             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28575             triggerAction : 'all',
28576             typeAhead : true,
28577             valueField : 'value',
28578             store : new Roo.data.SimpleStore({
28579                 data : (function() {
28580                     var years = [];
28581                     _this.fireEvent('years', _this, years);
28582                     return years;
28583                 })(),
28584                 fields : [ 'value' ]
28585             }),
28586             listeners : {
28587                 select : function (_self, record, index)
28588                 {
28589                     _this.setValue(_this.getValue());
28590                 }
28591             }
28592         });
28593
28594         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28595     },
28596     
28597     setValue : function(v, format)
28598     {
28599         this.inputEl.dom.value = v;
28600         
28601         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28602         
28603         var d = Date.parseDate(v, f);
28604         
28605         if(!d){
28606             this.validate();
28607             return;
28608         }
28609         
28610         this.setDay(d.format(this.dayFormat));
28611         this.setMonth(d.format(this.monthFormat));
28612         this.setYear(d.format(this.yearFormat));
28613         
28614         this.validate();
28615         
28616         return;
28617     },
28618     
28619     setDay : function(v)
28620     {
28621         this.dayField.setValue(v);
28622         this.inputEl.dom.value = this.getValue();
28623         this.validate();
28624         return;
28625     },
28626     
28627     setMonth : function(v)
28628     {
28629         this.monthField.setValue(v, true);
28630         this.inputEl.dom.value = this.getValue();
28631         this.validate();
28632         return;
28633     },
28634     
28635     setYear : function(v)
28636     {
28637         this.yearField.setValue(v);
28638         this.inputEl.dom.value = this.getValue();
28639         this.validate();
28640         return;
28641     },
28642     
28643     getDay : function()
28644     {
28645         return this.dayField.getValue();
28646     },
28647     
28648     getMonth : function()
28649     {
28650         return this.monthField.getValue();
28651     },
28652     
28653     getYear : function()
28654     {
28655         return this.yearField.getValue();
28656     },
28657     
28658     getValue : function()
28659     {
28660         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28661         
28662         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28663         
28664         return date;
28665     },
28666     
28667     reset : function()
28668     {
28669         this.setDay('');
28670         this.setMonth('');
28671         this.setYear('');
28672         this.inputEl.dom.value = '';
28673         this.validate();
28674         return;
28675     },
28676     
28677     validate : function()
28678     {
28679         var d = this.dayField.validate();
28680         var m = this.monthField.validate();
28681         var y = this.yearField.validate();
28682         
28683         var valid = true;
28684         
28685         if(
28686                 (!this.dayAllowBlank && !d) ||
28687                 (!this.monthAllowBlank && !m) ||
28688                 (!this.yearAllowBlank && !y)
28689         ){
28690             valid = false;
28691         }
28692         
28693         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28694             return valid;
28695         }
28696         
28697         if(valid){
28698             this.markValid();
28699             return valid;
28700         }
28701         
28702         this.markInvalid();
28703         
28704         return valid;
28705     },
28706     
28707     markValid : function()
28708     {
28709         
28710         var label = this.el.select('label', true).first();
28711         var icon = this.el.select('i.fa-star', true).first();
28712
28713         if(label && icon){
28714             icon.remove();
28715         }
28716         
28717         this.fireEvent('valid', this);
28718     },
28719     
28720      /**
28721      * Mark this field as invalid
28722      * @param {String} msg The validation message
28723      */
28724     markInvalid : function(msg)
28725     {
28726         
28727         var label = this.el.select('label', true).first();
28728         var icon = this.el.select('i.fa-star', true).first();
28729
28730         if(label && !icon){
28731             this.el.select('.roo-date-split-field-label', true).createChild({
28732                 tag : 'i',
28733                 cls : 'text-danger fa fa-lg fa-star',
28734                 tooltip : 'This field is required',
28735                 style : 'margin-right:5px;'
28736             }, label, true);
28737         }
28738         
28739         this.fireEvent('invalid', this, msg);
28740     },
28741     
28742     clearInvalid : function()
28743     {
28744         var label = this.el.select('label', true).first();
28745         var icon = this.el.select('i.fa-star', true).first();
28746
28747         if(label && icon){
28748             icon.remove();
28749         }
28750         
28751         this.fireEvent('valid', this);
28752     },
28753     
28754     getName: function()
28755     {
28756         return this.name;
28757     }
28758     
28759 });
28760
28761  /**
28762  *
28763  * This is based on 
28764  * http://masonry.desandro.com
28765  *
28766  * The idea is to render all the bricks based on vertical width...
28767  *
28768  * The original code extends 'outlayer' - we might need to use that....
28769  * 
28770  */
28771
28772
28773 /**
28774  * @class Roo.bootstrap.LayoutMasonry
28775  * @extends Roo.bootstrap.Component
28776  * Bootstrap Layout Masonry class
28777  * 
28778  * @constructor
28779  * Create a new Element
28780  * @param {Object} config The config object
28781  */
28782
28783 Roo.bootstrap.LayoutMasonry = function(config){
28784     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28785     
28786     this.bricks = [];
28787     
28788 };
28789
28790 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28791     
28792     /**
28793      * @cfg {Boolean} isLayoutInstant = no animation?
28794      */   
28795     isLayoutInstant : false, // needed?
28796    
28797     /**
28798      * @cfg {Number} boxWidth  width of the columns
28799      */   
28800     boxWidth : 450,
28801     
28802       /**
28803      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28804      */   
28805     boxHeight : 0,
28806     
28807     /**
28808      * @cfg {Number} padWidth padding below box..
28809      */   
28810     padWidth : 10, 
28811     
28812     /**
28813      * @cfg {Number} gutter gutter width..
28814      */   
28815     gutter : 10,
28816     
28817      /**
28818      * @cfg {Number} maxCols maximum number of columns
28819      */   
28820     
28821     maxCols: 0,
28822     
28823     /**
28824      * @cfg {Boolean} isAutoInitial defalut true
28825      */   
28826     isAutoInitial : true, 
28827     
28828     containerWidth: 0,
28829     
28830     /**
28831      * @cfg {Boolean} isHorizontal defalut false
28832      */   
28833     isHorizontal : false, 
28834
28835     currentSize : null,
28836     
28837     tag: 'div',
28838     
28839     cls: '',
28840     
28841     bricks: null, //CompositeElement
28842     
28843     cols : 1,
28844     
28845     _isLayoutInited : false,
28846     
28847 //    isAlternative : false, // only use for vertical layout...
28848     
28849     /**
28850      * @cfg {Number} alternativePadWidth padding below box..
28851      */   
28852     alternativePadWidth : 50, 
28853     
28854     getAutoCreate : function(){
28855         
28856         var cfg = {
28857             tag: this.tag,
28858             cls: 'blog-masonary-wrapper ' + this.cls,
28859             cn : {
28860                 cls : 'mas-boxes masonary'
28861             }
28862         };
28863         
28864         return cfg;
28865     },
28866     
28867     getChildContainer: function( )
28868     {
28869         if (this.boxesEl) {
28870             return this.boxesEl;
28871         }
28872         
28873         this.boxesEl = this.el.select('.mas-boxes').first();
28874         
28875         return this.boxesEl;
28876     },
28877     
28878     
28879     initEvents : function()
28880     {
28881         var _this = this;
28882         
28883         if(this.isAutoInitial){
28884             Roo.log('hook children rendered');
28885             this.on('childrenrendered', function() {
28886                 Roo.log('children rendered');
28887                 _this.initial();
28888             } ,this);
28889         }
28890     },
28891     
28892     initial : function()
28893     {
28894         this.currentSize = this.el.getBox(true);
28895         
28896         Roo.EventManager.onWindowResize(this.resize, this); 
28897
28898         if(!this.isAutoInitial){
28899             this.layout();
28900             return;
28901         }
28902         
28903         this.layout();
28904         
28905         return;
28906         //this.layout.defer(500,this);
28907         
28908     },
28909     
28910     resize : function()
28911     {
28912         Roo.log('resize');
28913         
28914         var cs = this.el.getBox(true);
28915         
28916         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28917             Roo.log("no change in with or X");
28918             return;
28919         }
28920         
28921         this.currentSize = cs;
28922         
28923         this.layout();
28924         
28925     },
28926     
28927     layout : function()
28928     {   
28929         this._resetLayout();
28930         
28931         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28932         
28933         this.layoutItems( isInstant );
28934       
28935         this._isLayoutInited = true;
28936         
28937     },
28938     
28939     _resetLayout : function()
28940     {
28941         if(this.isHorizontal){
28942             this.horizontalMeasureColumns();
28943             return;
28944         }
28945         
28946         this.verticalMeasureColumns();
28947         
28948     },
28949     
28950     verticalMeasureColumns : function()
28951     {
28952         this.getContainerWidth();
28953         
28954 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28955 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28956 //            return;
28957 //        }
28958         
28959         var boxWidth = this.boxWidth + this.padWidth;
28960         
28961         if(this.containerWidth < this.boxWidth){
28962             boxWidth = this.containerWidth
28963         }
28964         
28965         var containerWidth = this.containerWidth;
28966         
28967         var cols = Math.floor(containerWidth / boxWidth);
28968         
28969         this.cols = Math.max( cols, 1 );
28970         
28971         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28972         
28973         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28974         
28975         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28976         
28977         this.colWidth = boxWidth + avail - this.padWidth;
28978         
28979         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28980         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28981     },
28982     
28983     horizontalMeasureColumns : function()
28984     {
28985         this.getContainerWidth();
28986         
28987         var boxWidth = this.boxWidth;
28988         
28989         if(this.containerWidth < boxWidth){
28990             boxWidth = this.containerWidth;
28991         }
28992         
28993         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28994         
28995         this.el.setHeight(boxWidth);
28996         
28997     },
28998     
28999     getContainerWidth : function()
29000     {
29001         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29002     },
29003     
29004     layoutItems : function( isInstant )
29005     {
29006         var items = Roo.apply([], this.bricks);
29007         
29008         if(this.isHorizontal){
29009             this._horizontalLayoutItems( items , isInstant );
29010             return;
29011         }
29012         
29013 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29014 //            this._verticalAlternativeLayoutItems( items , isInstant );
29015 //            return;
29016 //        }
29017         
29018         this._verticalLayoutItems( items , isInstant );
29019         
29020     },
29021     
29022     _verticalLayoutItems : function ( items , isInstant)
29023     {
29024         if ( !items || !items.length ) {
29025             return;
29026         }
29027         
29028         var standard = [
29029             ['xs', 'xs', 'xs', 'tall'],
29030             ['xs', 'xs', 'tall'],
29031             ['xs', 'xs', 'sm'],
29032             ['xs', 'xs', 'xs'],
29033             ['xs', 'tall'],
29034             ['xs', 'sm'],
29035             ['xs', 'xs'],
29036             ['xs'],
29037             
29038             ['sm', 'xs', 'xs'],
29039             ['sm', 'xs'],
29040             ['sm'],
29041             
29042             ['tall', 'xs', 'xs', 'xs'],
29043             ['tall', 'xs', 'xs'],
29044             ['tall', 'xs'],
29045             ['tall']
29046             
29047         ];
29048         
29049         var queue = [];
29050         
29051         var boxes = [];
29052         
29053         var box = [];
29054         
29055         Roo.each(items, function(item, k){
29056             
29057             switch (item.size) {
29058                 // these layouts take up a full box,
29059                 case 'md' :
29060                 case 'md-left' :
29061                 case 'md-right' :
29062                 case 'wide' :
29063                     
29064                     if(box.length){
29065                         boxes.push(box);
29066                         box = [];
29067                     }
29068                     
29069                     boxes.push([item]);
29070                     
29071                     break;
29072                     
29073                 case 'xs' :
29074                 case 'sm' :
29075                 case 'tall' :
29076                     
29077                     box.push(item);
29078                     
29079                     break;
29080                 default :
29081                     break;
29082                     
29083             }
29084             
29085         }, this);
29086         
29087         if(box.length){
29088             boxes.push(box);
29089             box = [];
29090         }
29091         
29092         var filterPattern = function(box, length)
29093         {
29094             if(!box.length){
29095                 return;
29096             }
29097             
29098             var match = false;
29099             
29100             var pattern = box.slice(0, length);
29101             
29102             var format = [];
29103             
29104             Roo.each(pattern, function(i){
29105                 format.push(i.size);
29106             }, this);
29107             
29108             Roo.each(standard, function(s){
29109                 
29110                 if(String(s) != String(format)){
29111                     return;
29112                 }
29113                 
29114                 match = true;
29115                 return false;
29116                 
29117             }, this);
29118             
29119             if(!match && length == 1){
29120                 return;
29121             }
29122             
29123             if(!match){
29124                 filterPattern(box, length - 1);
29125                 return;
29126             }
29127                 
29128             queue.push(pattern);
29129
29130             box = box.slice(length, box.length);
29131
29132             filterPattern(box, 4);
29133
29134             return;
29135             
29136         }
29137         
29138         Roo.each(boxes, function(box, k){
29139             
29140             if(!box.length){
29141                 return;
29142             }
29143             
29144             if(box.length == 1){
29145                 queue.push(box);
29146                 return;
29147             }
29148             
29149             filterPattern(box, 4);
29150             
29151         }, this);
29152         
29153         this._processVerticalLayoutQueue( queue, isInstant );
29154         
29155     },
29156     
29157 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29158 //    {
29159 //        if ( !items || !items.length ) {
29160 //            return;
29161 //        }
29162 //
29163 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29164 //        
29165 //    },
29166     
29167     _horizontalLayoutItems : function ( items , isInstant)
29168     {
29169         if ( !items || !items.length || items.length < 3) {
29170             return;
29171         }
29172         
29173         items.reverse();
29174         
29175         var eItems = items.slice(0, 3);
29176         
29177         items = items.slice(3, items.length);
29178         
29179         var standard = [
29180             ['xs', 'xs', 'xs', 'wide'],
29181             ['xs', 'xs', 'wide'],
29182             ['xs', 'xs', 'sm'],
29183             ['xs', 'xs', 'xs'],
29184             ['xs', 'wide'],
29185             ['xs', 'sm'],
29186             ['xs', 'xs'],
29187             ['xs'],
29188             
29189             ['sm', 'xs', 'xs'],
29190             ['sm', 'xs'],
29191             ['sm'],
29192             
29193             ['wide', 'xs', 'xs', 'xs'],
29194             ['wide', 'xs', 'xs'],
29195             ['wide', 'xs'],
29196             ['wide'],
29197             
29198             ['wide-thin']
29199         ];
29200         
29201         var queue = [];
29202         
29203         var boxes = [];
29204         
29205         var box = [];
29206         
29207         Roo.each(items, function(item, k){
29208             
29209             switch (item.size) {
29210                 case 'md' :
29211                 case 'md-left' :
29212                 case 'md-right' :
29213                 case 'tall' :
29214                     
29215                     if(box.length){
29216                         boxes.push(box);
29217                         box = [];
29218                     }
29219                     
29220                     boxes.push([item]);
29221                     
29222                     break;
29223                     
29224                 case 'xs' :
29225                 case 'sm' :
29226                 case 'wide' :
29227                 case 'wide-thin' :
29228                     
29229                     box.push(item);
29230                     
29231                     break;
29232                 default :
29233                     break;
29234                     
29235             }
29236             
29237         }, this);
29238         
29239         if(box.length){
29240             boxes.push(box);
29241             box = [];
29242         }
29243         
29244         var filterPattern = function(box, length)
29245         {
29246             if(!box.length){
29247                 return;
29248             }
29249             
29250             var match = false;
29251             
29252             var pattern = box.slice(0, length);
29253             
29254             var format = [];
29255             
29256             Roo.each(pattern, function(i){
29257                 format.push(i.size);
29258             }, this);
29259             
29260             Roo.each(standard, function(s){
29261                 
29262                 if(String(s) != String(format)){
29263                     return;
29264                 }
29265                 
29266                 match = true;
29267                 return false;
29268                 
29269             }, this);
29270             
29271             if(!match && length == 1){
29272                 return;
29273             }
29274             
29275             if(!match){
29276                 filterPattern(box, length - 1);
29277                 return;
29278             }
29279                 
29280             queue.push(pattern);
29281
29282             box = box.slice(length, box.length);
29283
29284             filterPattern(box, 4);
29285
29286             return;
29287             
29288         }
29289         
29290         Roo.each(boxes, function(box, k){
29291             
29292             if(!box.length){
29293                 return;
29294             }
29295             
29296             if(box.length == 1){
29297                 queue.push(box);
29298                 return;
29299             }
29300             
29301             filterPattern(box, 4);
29302             
29303         }, this);
29304         
29305         
29306         var prune = [];
29307         
29308         var pos = this.el.getBox(true);
29309         
29310         var minX = pos.x;
29311         
29312         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29313         
29314         var hit_end = false;
29315         
29316         Roo.each(queue, function(box){
29317             
29318             if(hit_end){
29319                 
29320                 Roo.each(box, function(b){
29321                 
29322                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29323                     b.el.hide();
29324
29325                 }, this);
29326
29327                 return;
29328             }
29329             
29330             var mx = 0;
29331             
29332             Roo.each(box, function(b){
29333                 
29334                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29335                 b.el.show();
29336
29337                 mx = Math.max(mx, b.x);
29338                 
29339             }, this);
29340             
29341             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29342             
29343             if(maxX < minX){
29344                 
29345                 Roo.each(box, function(b){
29346                 
29347                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29348                     b.el.hide();
29349                     
29350                 }, this);
29351                 
29352                 hit_end = true;
29353                 
29354                 return;
29355             }
29356             
29357             prune.push(box);
29358             
29359         }, this);
29360         
29361         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29362     },
29363     
29364     /** Sets position of item in DOM
29365     * @param {Element} item
29366     * @param {Number} x - horizontal position
29367     * @param {Number} y - vertical position
29368     * @param {Boolean} isInstant - disables transitions
29369     */
29370     _processVerticalLayoutQueue : function( queue, isInstant )
29371     {
29372         var pos = this.el.getBox(true);
29373         var x = pos.x;
29374         var y = pos.y;
29375         var maxY = [];
29376         
29377         for (var i = 0; i < this.cols; i++){
29378             maxY[i] = pos.y;
29379         }
29380         
29381         Roo.each(queue, function(box, k){
29382             
29383             var col = k % this.cols;
29384             
29385             Roo.each(box, function(b,kk){
29386                 
29387                 b.el.position('absolute');
29388                 
29389                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29390                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29391                 
29392                 if(b.size == 'md-left' || b.size == 'md-right'){
29393                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29394                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29395                 }
29396                 
29397                 b.el.setWidth(width);
29398                 b.el.setHeight(height);
29399                 // iframe?
29400                 b.el.select('iframe',true).setSize(width,height);
29401                 
29402             }, this);
29403             
29404             for (var i = 0; i < this.cols; i++){
29405                 
29406                 if(maxY[i] < maxY[col]){
29407                     col = i;
29408                     continue;
29409                 }
29410                 
29411                 col = Math.min(col, i);
29412                 
29413             }
29414             
29415             x = pos.x + col * (this.colWidth + this.padWidth);
29416             
29417             y = maxY[col];
29418             
29419             var positions = [];
29420             
29421             switch (box.length){
29422                 case 1 :
29423                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29424                     break;
29425                 case 2 :
29426                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29427                     break;
29428                 case 3 :
29429                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29430                     break;
29431                 case 4 :
29432                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29433                     break;
29434                 default :
29435                     break;
29436             }
29437             
29438             Roo.each(box, function(b,kk){
29439                 
29440                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29441                 
29442                 var sz = b.el.getSize();
29443                 
29444                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29445                 
29446             }, this);
29447             
29448         }, this);
29449         
29450         var mY = 0;
29451         
29452         for (var i = 0; i < this.cols; i++){
29453             mY = Math.max(mY, maxY[i]);
29454         }
29455         
29456         this.el.setHeight(mY - pos.y);
29457         
29458     },
29459     
29460 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29461 //    {
29462 //        var pos = this.el.getBox(true);
29463 //        var x = pos.x;
29464 //        var y = pos.y;
29465 //        var maxX = pos.right;
29466 //        
29467 //        var maxHeight = 0;
29468 //        
29469 //        Roo.each(items, function(item, k){
29470 //            
29471 //            var c = k % 2;
29472 //            
29473 //            item.el.position('absolute');
29474 //                
29475 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29476 //
29477 //            item.el.setWidth(width);
29478 //
29479 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29480 //
29481 //            item.el.setHeight(height);
29482 //            
29483 //            if(c == 0){
29484 //                item.el.setXY([x, y], isInstant ? false : true);
29485 //            } else {
29486 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29487 //            }
29488 //            
29489 //            y = y + height + this.alternativePadWidth;
29490 //            
29491 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29492 //            
29493 //        }, this);
29494 //        
29495 //        this.el.setHeight(maxHeight);
29496 //        
29497 //    },
29498     
29499     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29500     {
29501         var pos = this.el.getBox(true);
29502         
29503         var minX = pos.x;
29504         var minY = pos.y;
29505         
29506         var maxX = pos.right;
29507         
29508         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29509         
29510         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29511         
29512         Roo.each(queue, function(box, k){
29513             
29514             Roo.each(box, function(b, kk){
29515                 
29516                 b.el.position('absolute');
29517                 
29518                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29519                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29520                 
29521                 if(b.size == 'md-left' || b.size == 'md-right'){
29522                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29523                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29524                 }
29525                 
29526                 b.el.setWidth(width);
29527                 b.el.setHeight(height);
29528                 
29529             }, this);
29530             
29531             if(!box.length){
29532                 return;
29533             }
29534             
29535             var positions = [];
29536             
29537             switch (box.length){
29538                 case 1 :
29539                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29540                     break;
29541                 case 2 :
29542                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29543                     break;
29544                 case 3 :
29545                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29546                     break;
29547                 case 4 :
29548                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29549                     break;
29550                 default :
29551                     break;
29552             }
29553             
29554             Roo.each(box, function(b,kk){
29555                 
29556                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29557                 
29558                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29559                 
29560             }, this);
29561             
29562         }, this);
29563         
29564     },
29565     
29566     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29567     {
29568         Roo.each(eItems, function(b,k){
29569             
29570             b.size = (k == 0) ? 'sm' : 'xs';
29571             b.x = (k == 0) ? 2 : 1;
29572             b.y = (k == 0) ? 2 : 1;
29573             
29574             b.el.position('absolute');
29575             
29576             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29577                 
29578             b.el.setWidth(width);
29579             
29580             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29581             
29582             b.el.setHeight(height);
29583             
29584         }, this);
29585
29586         var positions = [];
29587         
29588         positions.push({
29589             x : maxX - this.unitWidth * 2 - this.gutter,
29590             y : minY
29591         });
29592         
29593         positions.push({
29594             x : maxX - this.unitWidth,
29595             y : minY + (this.unitWidth + this.gutter) * 2
29596         });
29597         
29598         positions.push({
29599             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29600             y : minY
29601         });
29602         
29603         Roo.each(eItems, function(b,k){
29604             
29605             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29606
29607         }, this);
29608         
29609     },
29610     
29611     getVerticalOneBoxColPositions : function(x, y, box)
29612     {
29613         var pos = [];
29614         
29615         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29616         
29617         if(box[0].size == 'md-left'){
29618             rand = 0;
29619         }
29620         
29621         if(box[0].size == 'md-right'){
29622             rand = 1;
29623         }
29624         
29625         pos.push({
29626             x : x + (this.unitWidth + this.gutter) * rand,
29627             y : y
29628         });
29629         
29630         return pos;
29631     },
29632     
29633     getVerticalTwoBoxColPositions : function(x, y, box)
29634     {
29635         var pos = [];
29636         
29637         if(box[0].size == 'xs'){
29638             
29639             pos.push({
29640                 x : x,
29641                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29642             });
29643
29644             pos.push({
29645                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29646                 y : y
29647             });
29648             
29649             return pos;
29650             
29651         }
29652         
29653         pos.push({
29654             x : x,
29655             y : y
29656         });
29657
29658         pos.push({
29659             x : x + (this.unitWidth + this.gutter) * 2,
29660             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29661         });
29662         
29663         return pos;
29664         
29665     },
29666     
29667     getVerticalThreeBoxColPositions : function(x, y, box)
29668     {
29669         var pos = [];
29670         
29671         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29672             
29673             pos.push({
29674                 x : x,
29675                 y : y
29676             });
29677
29678             pos.push({
29679                 x : x + (this.unitWidth + this.gutter) * 1,
29680                 y : y
29681             });
29682             
29683             pos.push({
29684                 x : x + (this.unitWidth + this.gutter) * 2,
29685                 y : y
29686             });
29687             
29688             return pos;
29689             
29690         }
29691         
29692         if(box[0].size == 'xs' && box[1].size == 'xs'){
29693             
29694             pos.push({
29695                 x : x,
29696                 y : y
29697             });
29698
29699             pos.push({
29700                 x : x,
29701                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29702             });
29703             
29704             pos.push({
29705                 x : x + (this.unitWidth + this.gutter) * 1,
29706                 y : y
29707             });
29708             
29709             return pos;
29710             
29711         }
29712         
29713         pos.push({
29714             x : x,
29715             y : y
29716         });
29717
29718         pos.push({
29719             x : x + (this.unitWidth + this.gutter) * 2,
29720             y : y
29721         });
29722
29723         pos.push({
29724             x : x + (this.unitWidth + this.gutter) * 2,
29725             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29726         });
29727             
29728         return pos;
29729         
29730     },
29731     
29732     getVerticalFourBoxColPositions : function(x, y, box)
29733     {
29734         var pos = [];
29735         
29736         if(box[0].size == 'xs'){
29737             
29738             pos.push({
29739                 x : x,
29740                 y : y
29741             });
29742
29743             pos.push({
29744                 x : x,
29745                 y : y + (this.unitHeight + this.gutter) * 1
29746             });
29747             
29748             pos.push({
29749                 x : x,
29750                 y : y + (this.unitHeight + this.gutter) * 2
29751             });
29752             
29753             pos.push({
29754                 x : x + (this.unitWidth + this.gutter) * 1,
29755                 y : y
29756             });
29757             
29758             return pos;
29759             
29760         }
29761         
29762         pos.push({
29763             x : x,
29764             y : y
29765         });
29766
29767         pos.push({
29768             x : x + (this.unitWidth + this.gutter) * 2,
29769             y : y
29770         });
29771
29772         pos.push({
29773             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29774             y : y + (this.unitHeight + this.gutter) * 1
29775         });
29776
29777         pos.push({
29778             x : x + (this.unitWidth + this.gutter) * 2,
29779             y : y + (this.unitWidth + this.gutter) * 2
29780         });
29781
29782         return pos;
29783         
29784     },
29785     
29786     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29787     {
29788         var pos = [];
29789         
29790         if(box[0].size == 'md-left'){
29791             pos.push({
29792                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29793                 y : minY
29794             });
29795             
29796             return pos;
29797         }
29798         
29799         if(box[0].size == 'md-right'){
29800             pos.push({
29801                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29802                 y : minY + (this.unitWidth + this.gutter) * 1
29803             });
29804             
29805             return pos;
29806         }
29807         
29808         var rand = Math.floor(Math.random() * (4 - box[0].y));
29809         
29810         pos.push({
29811             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29812             y : minY + (this.unitWidth + this.gutter) * rand
29813         });
29814         
29815         return pos;
29816         
29817     },
29818     
29819     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29820     {
29821         var pos = [];
29822         
29823         if(box[0].size == 'xs'){
29824             
29825             pos.push({
29826                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29827                 y : minY
29828             });
29829
29830             pos.push({
29831                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29832                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29833             });
29834             
29835             return pos;
29836             
29837         }
29838         
29839         pos.push({
29840             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29841             y : minY
29842         });
29843
29844         pos.push({
29845             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29846             y : minY + (this.unitWidth + this.gutter) * 2
29847         });
29848         
29849         return pos;
29850         
29851     },
29852     
29853     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29854     {
29855         var pos = [];
29856         
29857         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29858             
29859             pos.push({
29860                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29861                 y : minY
29862             });
29863
29864             pos.push({
29865                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29866                 y : minY + (this.unitWidth + this.gutter) * 1
29867             });
29868             
29869             pos.push({
29870                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29871                 y : minY + (this.unitWidth + this.gutter) * 2
29872             });
29873             
29874             return pos;
29875             
29876         }
29877         
29878         if(box[0].size == 'xs' && box[1].size == 'xs'){
29879             
29880             pos.push({
29881                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29882                 y : minY
29883             });
29884
29885             pos.push({
29886                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29887                 y : minY
29888             });
29889             
29890             pos.push({
29891                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29892                 y : minY + (this.unitWidth + this.gutter) * 1
29893             });
29894             
29895             return pos;
29896             
29897         }
29898         
29899         pos.push({
29900             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29901             y : minY
29902         });
29903
29904         pos.push({
29905             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29906             y : minY + (this.unitWidth + this.gutter) * 2
29907         });
29908
29909         pos.push({
29910             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29911             y : minY + (this.unitWidth + this.gutter) * 2
29912         });
29913             
29914         return pos;
29915         
29916     },
29917     
29918     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29919     {
29920         var pos = [];
29921         
29922         if(box[0].size == 'xs'){
29923             
29924             pos.push({
29925                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29926                 y : minY
29927             });
29928
29929             pos.push({
29930                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29931                 y : minY
29932             });
29933             
29934             pos.push({
29935                 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),
29936                 y : minY
29937             });
29938             
29939             pos.push({
29940                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29941                 y : minY + (this.unitWidth + this.gutter) * 1
29942             });
29943             
29944             return pos;
29945             
29946         }
29947         
29948         pos.push({
29949             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29950             y : minY
29951         });
29952         
29953         pos.push({
29954             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29955             y : minY + (this.unitWidth + this.gutter) * 2
29956         });
29957         
29958         pos.push({
29959             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29960             y : minY + (this.unitWidth + this.gutter) * 2
29961         });
29962         
29963         pos.push({
29964             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),
29965             y : minY + (this.unitWidth + this.gutter) * 2
29966         });
29967
29968         return pos;
29969         
29970     }
29971     
29972 });
29973
29974  
29975
29976  /**
29977  *
29978  * This is based on 
29979  * http://masonry.desandro.com
29980  *
29981  * The idea is to render all the bricks based on vertical width...
29982  *
29983  * The original code extends 'outlayer' - we might need to use that....
29984  * 
29985  */
29986
29987
29988 /**
29989  * @class Roo.bootstrap.LayoutMasonryAuto
29990  * @extends Roo.bootstrap.Component
29991  * Bootstrap Layout Masonry class
29992  * 
29993  * @constructor
29994  * Create a new Element
29995  * @param {Object} config The config object
29996  */
29997
29998 Roo.bootstrap.LayoutMasonryAuto = function(config){
29999     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30000 };
30001
30002 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30003     
30004       /**
30005      * @cfg {Boolean} isFitWidth  - resize the width..
30006      */   
30007     isFitWidth : false,  // options..
30008     /**
30009      * @cfg {Boolean} isOriginLeft = left align?
30010      */   
30011     isOriginLeft : true,
30012     /**
30013      * @cfg {Boolean} isOriginTop = top align?
30014      */   
30015     isOriginTop : false,
30016     /**
30017      * @cfg {Boolean} isLayoutInstant = no animation?
30018      */   
30019     isLayoutInstant : false, // needed?
30020     /**
30021      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30022      */   
30023     isResizingContainer : true,
30024     /**
30025      * @cfg {Number} columnWidth  width of the columns 
30026      */   
30027     
30028     columnWidth : 0,
30029     
30030     /**
30031      * @cfg {Number} maxCols maximum number of columns
30032      */   
30033     
30034     maxCols: 0,
30035     /**
30036      * @cfg {Number} padHeight padding below box..
30037      */   
30038     
30039     padHeight : 10, 
30040     
30041     /**
30042      * @cfg {Boolean} isAutoInitial defalut true
30043      */   
30044     
30045     isAutoInitial : true, 
30046     
30047     // private?
30048     gutter : 0,
30049     
30050     containerWidth: 0,
30051     initialColumnWidth : 0,
30052     currentSize : null,
30053     
30054     colYs : null, // array.
30055     maxY : 0,
30056     padWidth: 10,
30057     
30058     
30059     tag: 'div',
30060     cls: '',
30061     bricks: null, //CompositeElement
30062     cols : 0, // array?
30063     // element : null, // wrapped now this.el
30064     _isLayoutInited : null, 
30065     
30066     
30067     getAutoCreate : function(){
30068         
30069         var cfg = {
30070             tag: this.tag,
30071             cls: 'blog-masonary-wrapper ' + this.cls,
30072             cn : {
30073                 cls : 'mas-boxes masonary'
30074             }
30075         };
30076         
30077         return cfg;
30078     },
30079     
30080     getChildContainer: function( )
30081     {
30082         if (this.boxesEl) {
30083             return this.boxesEl;
30084         }
30085         
30086         this.boxesEl = this.el.select('.mas-boxes').first();
30087         
30088         return this.boxesEl;
30089     },
30090     
30091     
30092     initEvents : function()
30093     {
30094         var _this = this;
30095         
30096         if(this.isAutoInitial){
30097             Roo.log('hook children rendered');
30098             this.on('childrenrendered', function() {
30099                 Roo.log('children rendered');
30100                 _this.initial();
30101             } ,this);
30102         }
30103         
30104     },
30105     
30106     initial : function()
30107     {
30108         this.reloadItems();
30109
30110         this.currentSize = this.el.getBox(true);
30111
30112         /// was window resize... - let's see if this works..
30113         Roo.EventManager.onWindowResize(this.resize, this); 
30114
30115         if(!this.isAutoInitial){
30116             this.layout();
30117             return;
30118         }
30119         
30120         this.layout.defer(500,this);
30121     },
30122     
30123     reloadItems: function()
30124     {
30125         this.bricks = this.el.select('.masonry-brick', true);
30126         
30127         this.bricks.each(function(b) {
30128             //Roo.log(b.getSize());
30129             if (!b.attr('originalwidth')) {
30130                 b.attr('originalwidth',  b.getSize().width);
30131             }
30132             
30133         });
30134         
30135         Roo.log(this.bricks.elements.length);
30136     },
30137     
30138     resize : function()
30139     {
30140         Roo.log('resize');
30141         var cs = this.el.getBox(true);
30142         
30143         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30144             Roo.log("no change in with or X");
30145             return;
30146         }
30147         this.currentSize = cs;
30148         this.layout();
30149     },
30150     
30151     layout : function()
30152     {
30153          Roo.log('layout');
30154         this._resetLayout();
30155         //this._manageStamps();
30156       
30157         // don't animate first layout
30158         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30159         this.layoutItems( isInstant );
30160       
30161         // flag for initalized
30162         this._isLayoutInited = true;
30163     },
30164     
30165     layoutItems : function( isInstant )
30166     {
30167         //var items = this._getItemsForLayout( this.items );
30168         // original code supports filtering layout items.. we just ignore it..
30169         
30170         this._layoutItems( this.bricks , isInstant );
30171       
30172         this._postLayout();
30173     },
30174     _layoutItems : function ( items , isInstant)
30175     {
30176        //this.fireEvent( 'layout', this, items );
30177     
30178
30179         if ( !items || !items.elements.length ) {
30180           // no items, emit event with empty array
30181             return;
30182         }
30183
30184         var queue = [];
30185         items.each(function(item) {
30186             Roo.log("layout item");
30187             Roo.log(item);
30188             // get x/y object from method
30189             var position = this._getItemLayoutPosition( item );
30190             // enqueue
30191             position.item = item;
30192             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30193             queue.push( position );
30194         }, this);
30195       
30196         this._processLayoutQueue( queue );
30197     },
30198     /** Sets position of item in DOM
30199     * @param {Element} item
30200     * @param {Number} x - horizontal position
30201     * @param {Number} y - vertical position
30202     * @param {Boolean} isInstant - disables transitions
30203     */
30204     _processLayoutQueue : function( queue )
30205     {
30206         for ( var i=0, len = queue.length; i < len; i++ ) {
30207             var obj = queue[i];
30208             obj.item.position('absolute');
30209             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30210         }
30211     },
30212       
30213     
30214     /**
30215     * Any logic you want to do after each layout,
30216     * i.e. size the container
30217     */
30218     _postLayout : function()
30219     {
30220         this.resizeContainer();
30221     },
30222     
30223     resizeContainer : function()
30224     {
30225         if ( !this.isResizingContainer ) {
30226             return;
30227         }
30228         var size = this._getContainerSize();
30229         if ( size ) {
30230             this.el.setSize(size.width,size.height);
30231             this.boxesEl.setSize(size.width,size.height);
30232         }
30233     },
30234     
30235     
30236     
30237     _resetLayout : function()
30238     {
30239         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30240         this.colWidth = this.el.getWidth();
30241         //this.gutter = this.el.getWidth(); 
30242         
30243         this.measureColumns();
30244
30245         // reset column Y
30246         var i = this.cols;
30247         this.colYs = [];
30248         while (i--) {
30249             this.colYs.push( 0 );
30250         }
30251     
30252         this.maxY = 0;
30253     },
30254
30255     measureColumns : function()
30256     {
30257         this.getContainerWidth();
30258       // if columnWidth is 0, default to outerWidth of first item
30259         if ( !this.columnWidth ) {
30260             var firstItem = this.bricks.first();
30261             Roo.log(firstItem);
30262             this.columnWidth  = this.containerWidth;
30263             if (firstItem && firstItem.attr('originalwidth') ) {
30264                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30265             }
30266             // columnWidth fall back to item of first element
30267             Roo.log("set column width?");
30268                         this.initialColumnWidth = this.columnWidth  ;
30269
30270             // if first elem has no width, default to size of container
30271             
30272         }
30273         
30274         
30275         if (this.initialColumnWidth) {
30276             this.columnWidth = this.initialColumnWidth;
30277         }
30278         
30279         
30280             
30281         // column width is fixed at the top - however if container width get's smaller we should
30282         // reduce it...
30283         
30284         // this bit calcs how man columns..
30285             
30286         var columnWidth = this.columnWidth += this.gutter;
30287       
30288         // calculate columns
30289         var containerWidth = this.containerWidth + this.gutter;
30290         
30291         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30292         // fix rounding errors, typically with gutters
30293         var excess = columnWidth - containerWidth % columnWidth;
30294         
30295         
30296         // if overshoot is less than a pixel, round up, otherwise floor it
30297         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30298         cols = Math[ mathMethod ]( cols );
30299         this.cols = Math.max( cols, 1 );
30300         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30301         
30302          // padding positioning..
30303         var totalColWidth = this.cols * this.columnWidth;
30304         var padavail = this.containerWidth - totalColWidth;
30305         // so for 2 columns - we need 3 'pads'
30306         
30307         var padNeeded = (1+this.cols) * this.padWidth;
30308         
30309         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30310         
30311         this.columnWidth += padExtra
30312         //this.padWidth = Math.floor(padavail /  ( this.cols));
30313         
30314         // adjust colum width so that padding is fixed??
30315         
30316         // we have 3 columns ... total = width * 3
30317         // we have X left over... that should be used by 
30318         
30319         //if (this.expandC) {
30320             
30321         //}
30322         
30323         
30324         
30325     },
30326     
30327     getContainerWidth : function()
30328     {
30329        /* // container is parent if fit width
30330         var container = this.isFitWidth ? this.element.parentNode : this.element;
30331         // check that this.size and size are there
30332         // IE8 triggers resize on body size change, so they might not be
30333         
30334         var size = getSize( container );  //FIXME
30335         this.containerWidth = size && size.innerWidth; //FIXME
30336         */
30337          
30338         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30339         
30340     },
30341     
30342     _getItemLayoutPosition : function( item )  // what is item?
30343     {
30344         // we resize the item to our columnWidth..
30345       
30346         item.setWidth(this.columnWidth);
30347         item.autoBoxAdjust  = false;
30348         
30349         var sz = item.getSize();
30350  
30351         // how many columns does this brick span
30352         var remainder = this.containerWidth % this.columnWidth;
30353         
30354         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30355         // round if off by 1 pixel, otherwise use ceil
30356         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30357         colSpan = Math.min( colSpan, this.cols );
30358         
30359         // normally this should be '1' as we dont' currently allow multi width columns..
30360         
30361         var colGroup = this._getColGroup( colSpan );
30362         // get the minimum Y value from the columns
30363         var minimumY = Math.min.apply( Math, colGroup );
30364         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30365         
30366         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30367          
30368         // position the brick
30369         var position = {
30370             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30371             y: this.currentSize.y + minimumY + this.padHeight
30372         };
30373         
30374         Roo.log(position);
30375         // apply setHeight to necessary columns
30376         var setHeight = minimumY + sz.height + this.padHeight;
30377         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30378         
30379         var setSpan = this.cols + 1 - colGroup.length;
30380         for ( var i = 0; i < setSpan; i++ ) {
30381           this.colYs[ shortColIndex + i ] = setHeight ;
30382         }
30383       
30384         return position;
30385     },
30386     
30387     /**
30388      * @param {Number} colSpan - number of columns the element spans
30389      * @returns {Array} colGroup
30390      */
30391     _getColGroup : function( colSpan )
30392     {
30393         if ( colSpan < 2 ) {
30394           // if brick spans only one column, use all the column Ys
30395           return this.colYs;
30396         }
30397       
30398         var colGroup = [];
30399         // how many different places could this brick fit horizontally
30400         var groupCount = this.cols + 1 - colSpan;
30401         // for each group potential horizontal position
30402         for ( var i = 0; i < groupCount; i++ ) {
30403           // make an array of colY values for that one group
30404           var groupColYs = this.colYs.slice( i, i + colSpan );
30405           // and get the max value of the array
30406           colGroup[i] = Math.max.apply( Math, groupColYs );
30407         }
30408         return colGroup;
30409     },
30410     /*
30411     _manageStamp : function( stamp )
30412     {
30413         var stampSize =  stamp.getSize();
30414         var offset = stamp.getBox();
30415         // get the columns that this stamp affects
30416         var firstX = this.isOriginLeft ? offset.x : offset.right;
30417         var lastX = firstX + stampSize.width;
30418         var firstCol = Math.floor( firstX / this.columnWidth );
30419         firstCol = Math.max( 0, firstCol );
30420         
30421         var lastCol = Math.floor( lastX / this.columnWidth );
30422         // lastCol should not go over if multiple of columnWidth #425
30423         lastCol -= lastX % this.columnWidth ? 0 : 1;
30424         lastCol = Math.min( this.cols - 1, lastCol );
30425         
30426         // set colYs to bottom of the stamp
30427         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30428             stampSize.height;
30429             
30430         for ( var i = firstCol; i <= lastCol; i++ ) {
30431           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30432         }
30433     },
30434     */
30435     
30436     _getContainerSize : function()
30437     {
30438         this.maxY = Math.max.apply( Math, this.colYs );
30439         var size = {
30440             height: this.maxY
30441         };
30442       
30443         if ( this.isFitWidth ) {
30444             size.width = this._getContainerFitWidth();
30445         }
30446       
30447         return size;
30448     },
30449     
30450     _getContainerFitWidth : function()
30451     {
30452         var unusedCols = 0;
30453         // count unused columns
30454         var i = this.cols;
30455         while ( --i ) {
30456           if ( this.colYs[i] !== 0 ) {
30457             break;
30458           }
30459           unusedCols++;
30460         }
30461         // fit container to columns that have been used
30462         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30463     },
30464     
30465     needsResizeLayout : function()
30466     {
30467         var previousWidth = this.containerWidth;
30468         this.getContainerWidth();
30469         return previousWidth !== this.containerWidth;
30470     }
30471  
30472 });
30473
30474  
30475
30476  /*
30477  * - LGPL
30478  *
30479  * element
30480  * 
30481  */
30482
30483 /**
30484  * @class Roo.bootstrap.MasonryBrick
30485  * @extends Roo.bootstrap.Component
30486  * Bootstrap MasonryBrick class
30487  * 
30488  * @constructor
30489  * Create a new MasonryBrick
30490  * @param {Object} config The config object
30491  */
30492
30493 Roo.bootstrap.MasonryBrick = function(config){
30494     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30495     
30496     this.addEvents({
30497         // raw events
30498         /**
30499          * @event click
30500          * When a MasonryBrick is clcik
30501          * @param {Roo.bootstrap.MasonryBrick} this
30502          * @param {Roo.EventObject} e
30503          */
30504         "click" : true
30505     });
30506 };
30507
30508 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30509     
30510     /**
30511      * @cfg {String} title
30512      */   
30513     title : '',
30514     /**
30515      * @cfg {String} html
30516      */   
30517     html : '',
30518     /**
30519      * @cfg {String} bgimage
30520      */   
30521     bgimage : '',
30522     /**
30523      * @cfg {String} videourl
30524      */   
30525     videourl : '',
30526     /**
30527      * @cfg {String} cls
30528      */   
30529     cls : '',
30530     /**
30531      * @cfg {String} href
30532      */   
30533     href : '',
30534     /**
30535      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30536      */   
30537     size : 'xs',
30538     
30539     /**
30540      * @cfg {String} (center|bottom) placetitle
30541      */   
30542     placetitle : '',
30543     
30544     getAutoCreate : function()
30545     {
30546         var cls = 'masonry-brick';
30547         
30548         if(this.href.length){
30549             cls += ' masonry-brick-link';
30550         }
30551         
30552         if(this.bgimage.length){
30553             cls += ' masonry-brick-image';
30554         }
30555         
30556         if(this.size){
30557             cls += ' masonry-' + this.size + '-brick';
30558         }
30559         
30560         if(this.placetitle.length){
30561             
30562             switch (this.placetitle) {
30563                 case 'center' :
30564                     cls += ' masonry-center-title';
30565                     break;
30566                 case 'bottom' :
30567                     cls += ' masonry-bottom-title';
30568                     break;
30569                 default:
30570                     break;
30571             }
30572             
30573         } else {
30574             if(!this.html.length && !this.bgimage.length){
30575                 cls += ' masonry-center-title';
30576             }
30577
30578             if(!this.html.length && this.bgimage.length){
30579                 cls += ' masonry-bottom-title';
30580             }
30581         }
30582         
30583         if(this.cls){
30584             cls += ' ' + this.cls;
30585         }
30586         
30587         var cfg = {
30588             tag: (this.href.length) ? 'a' : 'div',
30589             cls: cls,
30590             cn: [
30591                 {
30592                     tag: 'div',
30593                     cls: 'masonry-brick-paragraph',
30594                     cn: []
30595                 }
30596             ]
30597         };
30598         
30599         if(this.href.length){
30600             cfg.href = this.href;
30601         }
30602         
30603         var cn = cfg.cn[0].cn;
30604         
30605         if(this.title.length){
30606             cn.push({
30607                 tag: 'h4',
30608                 cls: 'masonry-brick-title',
30609                 html: this.title
30610             });
30611         }
30612         
30613         if(this.html.length){
30614             cn.push({
30615                 tag: 'p',
30616                 cls: 'masonry-brick-text',
30617                 html: this.html
30618             });
30619         }  
30620         if (!this.title.length && !this.html.length) {
30621             cfg.cn[0].cls += ' hide';
30622         }
30623         
30624         if(this.bgimage.length){
30625             cfg.cn.push({
30626                 tag: 'img',
30627                 cls: 'masonry-brick-image-view',
30628                 src: this.bgimage
30629             });
30630         }
30631         if(this.videourl.length){
30632             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30633             // youtube support only?
30634             cfg.cn.push({
30635                 tag: 'iframe',
30636                 cls: 'masonry-brick-image-view',
30637                 src: vurl,
30638                 frameborder : 0,
30639                 allowfullscreen : true
30640             });
30641             
30642             
30643         }
30644         return cfg;
30645         
30646     },
30647     
30648     initEvents: function() 
30649     {
30650         switch (this.size) {
30651             case 'xs' :
30652 //                this.intSize = 1;
30653                 this.x = 1;
30654                 this.y = 1;
30655                 break;
30656             case 'sm' :
30657 //                this.intSize = 2;
30658                 this.x = 2;
30659                 this.y = 2;
30660                 break;
30661             case 'md' :
30662             case 'md-left' :
30663             case 'md-right' :
30664 //                this.intSize = 3;
30665                 this.x = 3;
30666                 this.y = 3;
30667                 break;
30668             case 'tall' :
30669 //                this.intSize = 3;
30670                 this.x = 2;
30671                 this.y = 3;
30672                 break;
30673             case 'wide' :
30674 //                this.intSize = 3;
30675                 this.x = 3;
30676                 this.y = 2;
30677                 break;
30678             case 'wide-thin' :
30679 //                this.intSize = 3;
30680                 this.x = 3;
30681                 this.y = 1;
30682                 break;
30683                         
30684             default :
30685                 break;
30686         }
30687         
30688         
30689         
30690         if(Roo.isTouch){
30691             this.el.on('touchstart', this.onTouchStart, this);
30692             this.el.on('touchmove', this.onTouchMove, this);
30693             this.el.on('touchend', this.onTouchEnd, this);
30694             this.el.on('contextmenu', this.onContextMenu, this);
30695         } else {
30696             this.el.on('mouseenter'  ,this.enter, this);
30697             this.el.on('mouseleave', this.leave, this);
30698         }
30699         
30700         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30701             this.parent().bricks.push(this);   
30702         }
30703         
30704     },
30705     
30706     onClick: function(e, el)
30707     {
30708         if(!Roo.isTouch){
30709             return;
30710         }
30711         
30712         var time = this.endTimer - this.startTimer;
30713         
30714         //alert(time);
30715         
30716         if(time < 1000){
30717             return;
30718         }
30719         
30720         e.preventDefault();
30721     },
30722     
30723     enter: function(e, el)
30724     {
30725         e.preventDefault();
30726         
30727         if(this.bgimage.length && this.html.length){
30728             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30729         }
30730     },
30731     
30732     leave: function(e, el)
30733     {
30734         e.preventDefault();
30735         
30736         if(this.bgimage.length && this.html.length){
30737             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30738         }
30739     },
30740     
30741     onTouchStart: function(e, el)
30742     {
30743 //        e.preventDefault();
30744         
30745         this.touchmoved = false;
30746         
30747         if(!this.bgimage.length || !this.html.length){
30748             return;
30749         }
30750         
30751         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30752         
30753         this.timer = new Date().getTime();
30754         
30755     },
30756     
30757     onTouchMove: function(e, el)
30758     {
30759         this.touchmoved = true;
30760     },
30761     
30762     onContextMenu : function(e,el)
30763     {
30764         e.preventDefault();
30765         e.stopPropagation();
30766         return false;
30767     },
30768     
30769     onTouchEnd: function(e, el)
30770     {
30771 //        e.preventDefault();
30772         
30773         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30774         
30775             this.leave(e,el);
30776             
30777             return;
30778         }
30779         
30780         if(!this.bgimage.length || !this.html.length){
30781             
30782             if(this.href.length){
30783                 window.location.href = this.href;
30784             }
30785             
30786             return;
30787         }
30788         
30789         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30790         
30791         window.location.href = this.href;
30792     }
30793     
30794 });
30795
30796  
30797
30798  /*
30799  * - LGPL
30800  *
30801  * element
30802  * 
30803  */
30804
30805 /**
30806  * @class Roo.bootstrap.Brick
30807  * @extends Roo.bootstrap.Component
30808  * Bootstrap Brick class
30809  * 
30810  * @constructor
30811  * Create a new Brick
30812  * @param {Object} config The config object
30813  */
30814
30815 Roo.bootstrap.Brick = function(config){
30816     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30817     
30818     this.addEvents({
30819         // raw events
30820         /**
30821          * @event click
30822          * When a Brick is click
30823          * @param {Roo.bootstrap.Brick} this
30824          * @param {Roo.EventObject} e
30825          */
30826         "click" : true
30827     });
30828 };
30829
30830 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30831     
30832     /**
30833      * @cfg {String} title
30834      */   
30835     title : '',
30836     /**
30837      * @cfg {String} html
30838      */   
30839     html : '',
30840     /**
30841      * @cfg {String} bgimage
30842      */   
30843     bgimage : '',
30844     /**
30845      * @cfg {String} cls
30846      */   
30847     cls : '',
30848     /**
30849      * @cfg {String} href
30850      */   
30851     href : '',
30852     /**
30853      * @cfg {String} video
30854      */   
30855     video : '',
30856     /**
30857      * @cfg {Boolean} square
30858      */   
30859     square : true,
30860     
30861     getAutoCreate : function()
30862     {
30863         var cls = 'roo-brick';
30864         
30865         if(this.href.length){
30866             cls += ' roo-brick-link';
30867         }
30868         
30869         if(this.bgimage.length){
30870             cls += ' roo-brick-image';
30871         }
30872         
30873         if(!this.html.length && !this.bgimage.length){
30874             cls += ' roo-brick-center-title';
30875         }
30876         
30877         if(!this.html.length && this.bgimage.length){
30878             cls += ' roo-brick-bottom-title';
30879         }
30880         
30881         if(this.cls){
30882             cls += ' ' + this.cls;
30883         }
30884         
30885         var cfg = {
30886             tag: (this.href.length) ? 'a' : 'div',
30887             cls: cls,
30888             cn: [
30889                 {
30890                     tag: 'div',
30891                     cls: 'roo-brick-paragraph',
30892                     cn: []
30893                 }
30894             ]
30895         };
30896         
30897         if(this.href.length){
30898             cfg.href = this.href;
30899         }
30900         
30901         var cn = cfg.cn[0].cn;
30902         
30903         if(this.title.length){
30904             cn.push({
30905                 tag: 'h4',
30906                 cls: 'roo-brick-title',
30907                 html: this.title
30908             });
30909         }
30910         
30911         if(this.html.length){
30912             cn.push({
30913                 tag: 'p',
30914                 cls: 'roo-brick-text',
30915                 html: this.html
30916             });
30917         } else {
30918             cn.cls += ' hide';
30919         }
30920         
30921         if(this.bgimage.length){
30922             cfg.cn.push({
30923                 tag: 'img',
30924                 cls: 'roo-brick-image-view',
30925                 src: this.bgimage
30926             });
30927         }
30928         
30929         return cfg;
30930     },
30931     
30932     initEvents: function() 
30933     {
30934         if(this.title.length || this.html.length){
30935             this.el.on('mouseenter'  ,this.enter, this);
30936             this.el.on('mouseleave', this.leave, this);
30937         }
30938         
30939         
30940         Roo.EventManager.onWindowResize(this.resize, this); 
30941         
30942         this.resize();
30943     },
30944     
30945     resize : function()
30946     {
30947         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30948         
30949         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30950 //        paragraph.setHeight(paragraph.getWidth());
30951         
30952         if(this.bgimage.length){
30953             var image = this.el.select('.roo-brick-image-view', true).first();
30954             image.setWidth(paragraph.getWidth());
30955             image.setHeight(paragraph.getWidth());
30956         }
30957         
30958     },
30959     
30960     enter: function(e, el)
30961     {
30962         e.preventDefault();
30963         
30964         if(this.bgimage.length){
30965             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30966             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30967         }
30968     },
30969     
30970     leave: function(e, el)
30971     {
30972         e.preventDefault();
30973         
30974         if(this.bgimage.length){
30975             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30976             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30977         }
30978     }
30979     
30980 });
30981
30982  
30983
30984  /*
30985  * Based on:
30986  * Ext JS Library 1.1.1
30987  * Copyright(c) 2006-2007, Ext JS, LLC.
30988  *
30989  * Originally Released Under LGPL - original licence link has changed is not relivant.
30990  *
30991  * Fork - LGPL
30992  * <script type="text/javascript">
30993  */
30994
30995
30996 /**
30997  * @class Roo.bootstrap.SplitBar
30998  * @extends Roo.util.Observable
30999  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31000  * <br><br>
31001  * Usage:
31002  * <pre><code>
31003 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31004                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31005 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31006 split.minSize = 100;
31007 split.maxSize = 600;
31008 split.animate = true;
31009 split.on('moved', splitterMoved);
31010 </code></pre>
31011  * @constructor
31012  * Create a new SplitBar
31013  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31014  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31015  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31016  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31017                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31018                         position of the SplitBar).
31019  */
31020 Roo.bootstrap.SplitBar = function(cfg){
31021     
31022     /** @private */
31023     
31024     //{
31025     //  dragElement : elm
31026     //  resizingElement: el,
31027         // optional..
31028     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31029     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31030         // existingProxy ???
31031     //}
31032     
31033     this.el = Roo.get(cfg.dragElement, true);
31034     this.el.dom.unselectable = "on";
31035     /** @private */
31036     this.resizingEl = Roo.get(cfg.resizingElement, true);
31037
31038     /**
31039      * @private
31040      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31041      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31042      * @type Number
31043      */
31044     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31045     
31046     /**
31047      * The minimum size of the resizing element. (Defaults to 0)
31048      * @type Number
31049      */
31050     this.minSize = 0;
31051     
31052     /**
31053      * The maximum size of the resizing element. (Defaults to 2000)
31054      * @type Number
31055      */
31056     this.maxSize = 2000;
31057     
31058     /**
31059      * Whether to animate the transition to the new size
31060      * @type Boolean
31061      */
31062     this.animate = false;
31063     
31064     /**
31065      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31066      * @type Boolean
31067      */
31068     this.useShim = false;
31069     
31070     /** @private */
31071     this.shim = null;
31072     
31073     if(!cfg.existingProxy){
31074         /** @private */
31075         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31076     }else{
31077         this.proxy = Roo.get(cfg.existingProxy).dom;
31078     }
31079     /** @private */
31080     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31081     
31082     /** @private */
31083     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31084     
31085     /** @private */
31086     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31087     
31088     /** @private */
31089     this.dragSpecs = {};
31090     
31091     /**
31092      * @private The adapter to use to positon and resize elements
31093      */
31094     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31095     this.adapter.init(this);
31096     
31097     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31098         /** @private */
31099         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31100         this.el.addClass("roo-splitbar-h");
31101     }else{
31102         /** @private */
31103         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31104         this.el.addClass("roo-splitbar-v");
31105     }
31106     
31107     this.addEvents({
31108         /**
31109          * @event resize
31110          * Fires when the splitter is moved (alias for {@link #event-moved})
31111          * @param {Roo.bootstrap.SplitBar} this
31112          * @param {Number} newSize the new width or height
31113          */
31114         "resize" : true,
31115         /**
31116          * @event moved
31117          * Fires when the splitter is moved
31118          * @param {Roo.bootstrap.SplitBar} this
31119          * @param {Number} newSize the new width or height
31120          */
31121         "moved" : true,
31122         /**
31123          * @event beforeresize
31124          * Fires before the splitter is dragged
31125          * @param {Roo.bootstrap.SplitBar} this
31126          */
31127         "beforeresize" : true,
31128
31129         "beforeapply" : true
31130     });
31131
31132     Roo.util.Observable.call(this);
31133 };
31134
31135 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31136     onStartProxyDrag : function(x, y){
31137         this.fireEvent("beforeresize", this);
31138         if(!this.overlay){
31139             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31140             o.unselectable();
31141             o.enableDisplayMode("block");
31142             // all splitbars share the same overlay
31143             Roo.bootstrap.SplitBar.prototype.overlay = o;
31144         }
31145         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31146         this.overlay.show();
31147         Roo.get(this.proxy).setDisplayed("block");
31148         var size = this.adapter.getElementSize(this);
31149         this.activeMinSize = this.getMinimumSize();;
31150         this.activeMaxSize = this.getMaximumSize();;
31151         var c1 = size - this.activeMinSize;
31152         var c2 = Math.max(this.activeMaxSize - size, 0);
31153         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31154             this.dd.resetConstraints();
31155             this.dd.setXConstraint(
31156                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31157                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31158             );
31159             this.dd.setYConstraint(0, 0);
31160         }else{
31161             this.dd.resetConstraints();
31162             this.dd.setXConstraint(0, 0);
31163             this.dd.setYConstraint(
31164                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31165                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31166             );
31167          }
31168         this.dragSpecs.startSize = size;
31169         this.dragSpecs.startPoint = [x, y];
31170         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31171     },
31172     
31173     /** 
31174      * @private Called after the drag operation by the DDProxy
31175      */
31176     onEndProxyDrag : function(e){
31177         Roo.get(this.proxy).setDisplayed(false);
31178         var endPoint = Roo.lib.Event.getXY(e);
31179         if(this.overlay){
31180             this.overlay.hide();
31181         }
31182         var newSize;
31183         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31184             newSize = this.dragSpecs.startSize + 
31185                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31186                     endPoint[0] - this.dragSpecs.startPoint[0] :
31187                     this.dragSpecs.startPoint[0] - endPoint[0]
31188                 );
31189         }else{
31190             newSize = this.dragSpecs.startSize + 
31191                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31192                     endPoint[1] - this.dragSpecs.startPoint[1] :
31193                     this.dragSpecs.startPoint[1] - endPoint[1]
31194                 );
31195         }
31196         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31197         if(newSize != this.dragSpecs.startSize){
31198             if(this.fireEvent('beforeapply', this, newSize) !== false){
31199                 this.adapter.setElementSize(this, newSize);
31200                 this.fireEvent("moved", this, newSize);
31201                 this.fireEvent("resize", this, newSize);
31202             }
31203         }
31204     },
31205     
31206     /**
31207      * Get the adapter this SplitBar uses
31208      * @return The adapter object
31209      */
31210     getAdapter : function(){
31211         return this.adapter;
31212     },
31213     
31214     /**
31215      * Set the adapter this SplitBar uses
31216      * @param {Object} adapter A SplitBar adapter object
31217      */
31218     setAdapter : function(adapter){
31219         this.adapter = adapter;
31220         this.adapter.init(this);
31221     },
31222     
31223     /**
31224      * Gets the minimum size for the resizing element
31225      * @return {Number} The minimum size
31226      */
31227     getMinimumSize : function(){
31228         return this.minSize;
31229     },
31230     
31231     /**
31232      * Sets the minimum size for the resizing element
31233      * @param {Number} minSize The minimum size
31234      */
31235     setMinimumSize : function(minSize){
31236         this.minSize = minSize;
31237     },
31238     
31239     /**
31240      * Gets the maximum size for the resizing element
31241      * @return {Number} The maximum size
31242      */
31243     getMaximumSize : function(){
31244         return this.maxSize;
31245     },
31246     
31247     /**
31248      * Sets the maximum size for the resizing element
31249      * @param {Number} maxSize The maximum size
31250      */
31251     setMaximumSize : function(maxSize){
31252         this.maxSize = maxSize;
31253     },
31254     
31255     /**
31256      * Sets the initialize size for the resizing element
31257      * @param {Number} size The initial size
31258      */
31259     setCurrentSize : function(size){
31260         var oldAnimate = this.animate;
31261         this.animate = false;
31262         this.adapter.setElementSize(this, size);
31263         this.animate = oldAnimate;
31264     },
31265     
31266     /**
31267      * Destroy this splitbar. 
31268      * @param {Boolean} removeEl True to remove the element
31269      */
31270     destroy : function(removeEl){
31271         if(this.shim){
31272             this.shim.remove();
31273         }
31274         this.dd.unreg();
31275         this.proxy.parentNode.removeChild(this.proxy);
31276         if(removeEl){
31277             this.el.remove();
31278         }
31279     }
31280 });
31281
31282 /**
31283  * @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.
31284  */
31285 Roo.bootstrap.SplitBar.createProxy = function(dir){
31286     var proxy = new Roo.Element(document.createElement("div"));
31287     proxy.unselectable();
31288     var cls = 'roo-splitbar-proxy';
31289     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31290     document.body.appendChild(proxy.dom);
31291     return proxy.dom;
31292 };
31293
31294 /** 
31295  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31296  * Default Adapter. It assumes the splitter and resizing element are not positioned
31297  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31298  */
31299 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31300 };
31301
31302 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31303     // do nothing for now
31304     init : function(s){
31305     
31306     },
31307     /**
31308      * Called before drag operations to get the current size of the resizing element. 
31309      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31310      */
31311      getElementSize : function(s){
31312         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31313             return s.resizingEl.getWidth();
31314         }else{
31315             return s.resizingEl.getHeight();
31316         }
31317     },
31318     
31319     /**
31320      * Called after drag operations to set the size of the resizing element.
31321      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31322      * @param {Number} newSize The new size to set
31323      * @param {Function} onComplete A function to be invoked when resizing is complete
31324      */
31325     setElementSize : function(s, newSize, onComplete){
31326         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31327             if(!s.animate){
31328                 s.resizingEl.setWidth(newSize);
31329                 if(onComplete){
31330                     onComplete(s, newSize);
31331                 }
31332             }else{
31333                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31334             }
31335         }else{
31336             
31337             if(!s.animate){
31338                 s.resizingEl.setHeight(newSize);
31339                 if(onComplete){
31340                     onComplete(s, newSize);
31341                 }
31342             }else{
31343                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31344             }
31345         }
31346     }
31347 };
31348
31349 /** 
31350  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31351  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31352  * Adapter that  moves the splitter element to align with the resized sizing element. 
31353  * Used with an absolute positioned SplitBar.
31354  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31355  * document.body, make sure you assign an id to the body element.
31356  */
31357 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31358     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31359     this.container = Roo.get(container);
31360 };
31361
31362 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31363     init : function(s){
31364         this.basic.init(s);
31365     },
31366     
31367     getElementSize : function(s){
31368         return this.basic.getElementSize(s);
31369     },
31370     
31371     setElementSize : function(s, newSize, onComplete){
31372         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31373     },
31374     
31375     moveSplitter : function(s){
31376         var yes = Roo.bootstrap.SplitBar;
31377         switch(s.placement){
31378             case yes.LEFT:
31379                 s.el.setX(s.resizingEl.getRight());
31380                 break;
31381             case yes.RIGHT:
31382                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31383                 break;
31384             case yes.TOP:
31385                 s.el.setY(s.resizingEl.getBottom());
31386                 break;
31387             case yes.BOTTOM:
31388                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31389                 break;
31390         }
31391     }
31392 };
31393
31394 /**
31395  * Orientation constant - Create a vertical SplitBar
31396  * @static
31397  * @type Number
31398  */
31399 Roo.bootstrap.SplitBar.VERTICAL = 1;
31400
31401 /**
31402  * Orientation constant - Create a horizontal SplitBar
31403  * @static
31404  * @type Number
31405  */
31406 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31407
31408 /**
31409  * Placement constant - The resizing element is to the left of the splitter element
31410  * @static
31411  * @type Number
31412  */
31413 Roo.bootstrap.SplitBar.LEFT = 1;
31414
31415 /**
31416  * Placement constant - The resizing element is to the right of the splitter element
31417  * @static
31418  * @type Number
31419  */
31420 Roo.bootstrap.SplitBar.RIGHT = 2;
31421
31422 /**
31423  * Placement constant - The resizing element is positioned above the splitter element
31424  * @static
31425  * @type Number
31426  */
31427 Roo.bootstrap.SplitBar.TOP = 3;
31428
31429 /**
31430  * Placement constant - The resizing element is positioned under splitter element
31431  * @static
31432  * @type Number
31433  */
31434 Roo.bootstrap.SplitBar.BOTTOM = 4;
31435 Roo.namespace("Roo.bootstrap.layout");/*
31436  * Based on:
31437  * Ext JS Library 1.1.1
31438  * Copyright(c) 2006-2007, Ext JS, LLC.
31439  *
31440  * Originally Released Under LGPL - original licence link has changed is not relivant.
31441  *
31442  * Fork - LGPL
31443  * <script type="text/javascript">
31444  */
31445  
31446 /**
31447  * @class Roo.bootstrap.layout.Manager
31448  * @extends Roo.bootstrap.Component
31449  * Base class for layout managers.
31450  */
31451 Roo.bootstrap.layout.Manager = function(config)
31452 {
31453     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31454     
31455     
31456      
31457     
31458     
31459     /** false to disable window resize monitoring @type Boolean */
31460     this.monitorWindowResize = true;
31461     this.regions = {};
31462     this.addEvents({
31463         /**
31464          * @event layout
31465          * Fires when a layout is performed. 
31466          * @param {Roo.LayoutManager} this
31467          */
31468         "layout" : true,
31469         /**
31470          * @event regionresized
31471          * Fires when the user resizes a region. 
31472          * @param {Roo.LayoutRegion} region The resized region
31473          * @param {Number} newSize The new size (width for east/west, height for north/south)
31474          */
31475         "regionresized" : true,
31476         /**
31477          * @event regioncollapsed
31478          * Fires when a region is collapsed. 
31479          * @param {Roo.LayoutRegion} region The collapsed region
31480          */
31481         "regioncollapsed" : true,
31482         /**
31483          * @event regionexpanded
31484          * Fires when a region is expanded.  
31485          * @param {Roo.LayoutRegion} region The expanded region
31486          */
31487         "regionexpanded" : true
31488     });
31489     this.updating = false;
31490     
31491     if (config.el) {
31492         this.el = Roo.get(config.el);
31493         this.initEvents();
31494     }
31495     
31496 };
31497
31498 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31499     
31500     
31501     regions : null,
31502     
31503     monitorWindowResize : true,
31504     
31505     
31506     updating : false,
31507     
31508     
31509     onRender : function(ct, position)
31510     {
31511         if(!this.el){
31512             this.el = Roo.get(ct);
31513             this.initEvents();
31514         }
31515     },
31516     
31517     
31518     initEvents: function()
31519     {
31520         
31521         
31522         // ie scrollbar fix
31523         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31524             document.body.scroll = "no";
31525         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31526             this.el.position('relative');
31527         }
31528         this.id = this.el.id;
31529         this.el.addClass("roo-layout-container");
31530         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31531         if(this.el.dom != document.body ) {
31532             this.el.on('resize', this.layout,this);
31533             this.el.on('show', this.layout,this);
31534         }
31535
31536     },
31537     
31538     /**
31539      * Returns true if this layout is currently being updated
31540      * @return {Boolean}
31541      */
31542     isUpdating : function(){
31543         return this.updating; 
31544     },
31545     
31546     /**
31547      * Suspend the LayoutManager from doing auto-layouts while
31548      * making multiple add or remove calls
31549      */
31550     beginUpdate : function(){
31551         this.updating = true;    
31552     },
31553     
31554     /**
31555      * Restore auto-layouts and optionally disable the manager from performing a layout
31556      * @param {Boolean} noLayout true to disable a layout update 
31557      */
31558     endUpdate : function(noLayout){
31559         this.updating = false;
31560         if(!noLayout){
31561             this.layout();
31562         }    
31563     },
31564     
31565     layout: function(){
31566         // abstract...
31567     },
31568     
31569     onRegionResized : function(region, newSize){
31570         this.fireEvent("regionresized", region, newSize);
31571         this.layout();
31572     },
31573     
31574     onRegionCollapsed : function(region){
31575         this.fireEvent("regioncollapsed", region);
31576     },
31577     
31578     onRegionExpanded : function(region){
31579         this.fireEvent("regionexpanded", region);
31580     },
31581         
31582     /**
31583      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31584      * performs box-model adjustments.
31585      * @return {Object} The size as an object {width: (the width), height: (the height)}
31586      */
31587     getViewSize : function()
31588     {
31589         var size;
31590         if(this.el.dom != document.body){
31591             size = this.el.getSize();
31592         }else{
31593             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31594         }
31595         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31596         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31597         return size;
31598     },
31599     
31600     /**
31601      * Returns the Element this layout is bound to.
31602      * @return {Roo.Element}
31603      */
31604     getEl : function(){
31605         return this.el;
31606     },
31607     
31608     /**
31609      * Returns the specified region.
31610      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31611      * @return {Roo.LayoutRegion}
31612      */
31613     getRegion : function(target){
31614         return this.regions[target.toLowerCase()];
31615     },
31616     
31617     onWindowResize : function(){
31618         if(this.monitorWindowResize){
31619             this.layout();
31620         }
31621     }
31622 });/*
31623  * Based on:
31624  * Ext JS Library 1.1.1
31625  * Copyright(c) 2006-2007, Ext JS, LLC.
31626  *
31627  * Originally Released Under LGPL - original licence link has changed is not relivant.
31628  *
31629  * Fork - LGPL
31630  * <script type="text/javascript">
31631  */
31632 /**
31633  * @class Roo.bootstrap.layout.Border
31634  * @extends Roo.bootstrap.layout.Manager
31635  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31636  * please see: examples/bootstrap/nested.html<br><br>
31637  
31638 <b>The container the layout is rendered into can be either the body element or any other element.
31639 If it is not the body element, the container needs to either be an absolute positioned element,
31640 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31641 the container size if it is not the body element.</b>
31642
31643 * @constructor
31644 * Create a new Border
31645 * @param {Object} config Configuration options
31646  */
31647 Roo.bootstrap.layout.Border = function(config){
31648     config = config || {};
31649     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31650     
31651     
31652     
31653     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31654         if(config[region]){
31655             config[region].region = region;
31656             this.addRegion(config[region]);
31657         }
31658     },this);
31659     
31660 };
31661
31662 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31663
31664 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31665     /**
31666      * Creates and adds a new region if it doesn't already exist.
31667      * @param {String} target The target region key (north, south, east, west or center).
31668      * @param {Object} config The regions config object
31669      * @return {BorderLayoutRegion} The new region
31670      */
31671     addRegion : function(config)
31672     {
31673         if(!this.regions[config.region]){
31674             var r = this.factory(config);
31675             this.bindRegion(r);
31676         }
31677         return this.regions[config.region];
31678     },
31679
31680     // private (kinda)
31681     bindRegion : function(r){
31682         this.regions[r.config.region] = r;
31683         
31684         r.on("visibilitychange",    this.layout, this);
31685         r.on("paneladded",          this.layout, this);
31686         r.on("panelremoved",        this.layout, this);
31687         r.on("invalidated",         this.layout, this);
31688         r.on("resized",             this.onRegionResized, this);
31689         r.on("collapsed",           this.onRegionCollapsed, this);
31690         r.on("expanded",            this.onRegionExpanded, this);
31691     },
31692
31693     /**
31694      * Performs a layout update.
31695      */
31696     layout : function()
31697     {
31698         if(this.updating) {
31699             return;
31700         }
31701         var size = this.getViewSize();
31702         var w = size.width;
31703         var h = size.height;
31704         var centerW = w;
31705         var centerH = h;
31706         var centerY = 0;
31707         var centerX = 0;
31708         //var x = 0, y = 0;
31709
31710         var rs = this.regions;
31711         var north = rs["north"];
31712         var south = rs["south"]; 
31713         var west = rs["west"];
31714         var east = rs["east"];
31715         var center = rs["center"];
31716         //if(this.hideOnLayout){ // not supported anymore
31717             //c.el.setStyle("display", "none");
31718         //}
31719         if(north && north.isVisible()){
31720             var b = north.getBox();
31721             var m = north.getMargins();
31722             b.width = w - (m.left+m.right);
31723             b.x = m.left;
31724             b.y = m.top;
31725             centerY = b.height + b.y + m.bottom;
31726             centerH -= centerY;
31727             north.updateBox(this.safeBox(b));
31728         }
31729         if(south && south.isVisible()){
31730             var b = south.getBox();
31731             var m = south.getMargins();
31732             b.width = w - (m.left+m.right);
31733             b.x = m.left;
31734             var totalHeight = (b.height + m.top + m.bottom);
31735             b.y = h - totalHeight + m.top;
31736             centerH -= totalHeight;
31737             south.updateBox(this.safeBox(b));
31738         }
31739         if(west && west.isVisible()){
31740             var b = west.getBox();
31741             var m = west.getMargins();
31742             b.height = centerH - (m.top+m.bottom);
31743             b.x = m.left;
31744             b.y = centerY + m.top;
31745             var totalWidth = (b.width + m.left + m.right);
31746             centerX += totalWidth;
31747             centerW -= totalWidth;
31748             west.updateBox(this.safeBox(b));
31749         }
31750         if(east && east.isVisible()){
31751             var b = east.getBox();
31752             var m = east.getMargins();
31753             b.height = centerH - (m.top+m.bottom);
31754             var totalWidth = (b.width + m.left + m.right);
31755             b.x = w - totalWidth + m.left;
31756             b.y = centerY + m.top;
31757             centerW -= totalWidth;
31758             east.updateBox(this.safeBox(b));
31759         }
31760         if(center){
31761             var m = center.getMargins();
31762             var centerBox = {
31763                 x: centerX + m.left,
31764                 y: centerY + m.top,
31765                 width: centerW - (m.left+m.right),
31766                 height: centerH - (m.top+m.bottom)
31767             };
31768             //if(this.hideOnLayout){
31769                 //center.el.setStyle("display", "block");
31770             //}
31771             center.updateBox(this.safeBox(centerBox));
31772         }
31773         this.el.repaint();
31774         this.fireEvent("layout", this);
31775     },
31776
31777     // private
31778     safeBox : function(box){
31779         box.width = Math.max(0, box.width);
31780         box.height = Math.max(0, box.height);
31781         return box;
31782     },
31783
31784     /**
31785      * Adds a ContentPanel (or subclass) to this layout.
31786      * @param {String} target The target region key (north, south, east, west or center).
31787      * @param {Roo.ContentPanel} panel The panel to add
31788      * @return {Roo.ContentPanel} The added panel
31789      */
31790     add : function(target, panel){
31791          
31792         target = target.toLowerCase();
31793         return this.regions[target].add(panel);
31794     },
31795
31796     /**
31797      * Remove a ContentPanel (or subclass) to this layout.
31798      * @param {String} target The target region key (north, south, east, west or center).
31799      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31800      * @return {Roo.ContentPanel} The removed panel
31801      */
31802     remove : function(target, panel){
31803         target = target.toLowerCase();
31804         return this.regions[target].remove(panel);
31805     },
31806
31807     /**
31808      * Searches all regions for a panel with the specified id
31809      * @param {String} panelId
31810      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31811      */
31812     findPanel : function(panelId){
31813         var rs = this.regions;
31814         for(var target in rs){
31815             if(typeof rs[target] != "function"){
31816                 var p = rs[target].getPanel(panelId);
31817                 if(p){
31818                     return p;
31819                 }
31820             }
31821         }
31822         return null;
31823     },
31824
31825     /**
31826      * Searches all regions for a panel with the specified id and activates (shows) it.
31827      * @param {String/ContentPanel} panelId The panels id or the panel itself
31828      * @return {Roo.ContentPanel} The shown panel or null
31829      */
31830     showPanel : function(panelId) {
31831       var rs = this.regions;
31832       for(var target in rs){
31833          var r = rs[target];
31834          if(typeof r != "function"){
31835             if(r.hasPanel(panelId)){
31836                return r.showPanel(panelId);
31837             }
31838          }
31839       }
31840       return null;
31841    },
31842
31843    /**
31844      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31845      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31846      */
31847    /*
31848     restoreState : function(provider){
31849         if(!provider){
31850             provider = Roo.state.Manager;
31851         }
31852         var sm = new Roo.LayoutStateManager();
31853         sm.init(this, provider);
31854     },
31855 */
31856  
31857  
31858     /**
31859      * Adds a xtype elements to the layout.
31860      * <pre><code>
31861
31862 layout.addxtype({
31863        xtype : 'ContentPanel',
31864        region: 'west',
31865        items: [ .... ]
31866    }
31867 );
31868
31869 layout.addxtype({
31870         xtype : 'NestedLayoutPanel',
31871         region: 'west',
31872         layout: {
31873            center: { },
31874            west: { }   
31875         },
31876         items : [ ... list of content panels or nested layout panels.. ]
31877    }
31878 );
31879 </code></pre>
31880      * @param {Object} cfg Xtype definition of item to add.
31881      */
31882     addxtype : function(cfg)
31883     {
31884         // basically accepts a pannel...
31885         // can accept a layout region..!?!?
31886         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31887         
31888         
31889         // theory?  children can only be panels??
31890         
31891         //if (!cfg.xtype.match(/Panel$/)) {
31892         //    return false;
31893         //}
31894         var ret = false;
31895         
31896         if (typeof(cfg.region) == 'undefined') {
31897             Roo.log("Failed to add Panel, region was not set");
31898             Roo.log(cfg);
31899             return false;
31900         }
31901         var region = cfg.region;
31902         delete cfg.region;
31903         
31904           
31905         var xitems = [];
31906         if (cfg.items) {
31907             xitems = cfg.items;
31908             delete cfg.items;
31909         }
31910         var nb = false;
31911         
31912         switch(cfg.xtype) 
31913         {
31914             case 'Content':  // ContentPanel (el, cfg)
31915             case 'Scroll':  // ContentPanel (el, cfg)
31916             case 'View': 
31917                 cfg.autoCreate = true;
31918                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31919                 //} else {
31920                 //    var el = this.el.createChild();
31921                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31922                 //}
31923                 
31924                 this.add(region, ret);
31925                 break;
31926             
31927             /*
31928             case 'TreePanel': // our new panel!
31929                 cfg.el = this.el.createChild();
31930                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31931                 this.add(region, ret);
31932                 break;
31933             */
31934             
31935             case 'Nest': 
31936                 // create a new Layout (which is  a Border Layout...
31937                 
31938                 var clayout = cfg.layout;
31939                 clayout.el  = this.el.createChild();
31940                 clayout.items   = clayout.items  || [];
31941                 
31942                 delete cfg.layout;
31943                 
31944                 // replace this exitems with the clayout ones..
31945                 xitems = clayout.items;
31946                  
31947                 // force background off if it's in center...
31948                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31949                     cfg.background = false;
31950                 }
31951                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31952                 
31953                 
31954                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31955                 //console.log('adding nested layout panel '  + cfg.toSource());
31956                 this.add(region, ret);
31957                 nb = {}; /// find first...
31958                 break;
31959             
31960             case 'Grid':
31961                 
31962                 // needs grid and region
31963                 
31964                 //var el = this.getRegion(region).el.createChild();
31965                 /*
31966                  *var el = this.el.createChild();
31967                 // create the grid first...
31968                 cfg.grid.container = el;
31969                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31970                 */
31971                 
31972                 if (region == 'center' && this.active ) {
31973                     cfg.background = false;
31974                 }
31975                 
31976                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31977                 
31978                 this.add(region, ret);
31979                 /*
31980                 if (cfg.background) {
31981                     // render grid on panel activation (if panel background)
31982                     ret.on('activate', function(gp) {
31983                         if (!gp.grid.rendered) {
31984                     //        gp.grid.render(el);
31985                         }
31986                     });
31987                 } else {
31988                   //  cfg.grid.render(el);
31989                 }
31990                 */
31991                 break;
31992            
31993            
31994             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31995                 // it was the old xcomponent building that caused this before.
31996                 // espeically if border is the top element in the tree.
31997                 ret = this;
31998                 break; 
31999                 
32000                     
32001                 
32002                 
32003                 
32004             default:
32005                 /*
32006                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32007                     
32008                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32009                     this.add(region, ret);
32010                 } else {
32011                 */
32012                     Roo.log(cfg);
32013                     throw "Can not add '" + cfg.xtype + "' to Border";
32014                     return null;
32015              
32016                                 
32017              
32018         }
32019         this.beginUpdate();
32020         // add children..
32021         var region = '';
32022         var abn = {};
32023         Roo.each(xitems, function(i)  {
32024             region = nb && i.region ? i.region : false;
32025             
32026             var add = ret.addxtype(i);
32027            
32028             if (region) {
32029                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32030                 if (!i.background) {
32031                     abn[region] = nb[region] ;
32032                 }
32033             }
32034             
32035         });
32036         this.endUpdate();
32037
32038         // make the last non-background panel active..
32039         //if (nb) { Roo.log(abn); }
32040         if (nb) {
32041             
32042             for(var r in abn) {
32043                 region = this.getRegion(r);
32044                 if (region) {
32045                     // tried using nb[r], but it does not work..
32046                      
32047                     region.showPanel(abn[r]);
32048                    
32049                 }
32050             }
32051         }
32052         return ret;
32053         
32054     },
32055     
32056     
32057 // private
32058     factory : function(cfg)
32059     {
32060         
32061         var validRegions = Roo.bootstrap.layout.Border.regions;
32062
32063         var target = cfg.region;
32064         cfg.mgr = this;
32065         
32066         var r = Roo.bootstrap.layout;
32067         Roo.log(target);
32068         switch(target){
32069             case "north":
32070                 return new r.North(cfg);
32071             case "south":
32072                 return new r.South(cfg);
32073             case "east":
32074                 return new r.East(cfg);
32075             case "west":
32076                 return new r.West(cfg);
32077             case "center":
32078                 return new r.Center(cfg);
32079         }
32080         throw 'Layout region "'+target+'" not supported.';
32081     }
32082     
32083     
32084 });
32085  /*
32086  * Based on:
32087  * Ext JS Library 1.1.1
32088  * Copyright(c) 2006-2007, Ext JS, LLC.
32089  *
32090  * Originally Released Under LGPL - original licence link has changed is not relivant.
32091  *
32092  * Fork - LGPL
32093  * <script type="text/javascript">
32094  */
32095  
32096 /**
32097  * @class Roo.bootstrap.layout.Basic
32098  * @extends Roo.util.Observable
32099  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32100  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32101  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32102  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32103  * @cfg {string}   region  the region that it inhabits..
32104  * @cfg {bool}   skipConfig skip config?
32105  * 
32106
32107  */
32108 Roo.bootstrap.layout.Basic = function(config){
32109     
32110     this.mgr = config.mgr;
32111     
32112     this.position = config.region;
32113     
32114     var skipConfig = config.skipConfig;
32115     
32116     this.events = {
32117         /**
32118          * @scope Roo.BasicLayoutRegion
32119          */
32120         
32121         /**
32122          * @event beforeremove
32123          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32124          * @param {Roo.LayoutRegion} this
32125          * @param {Roo.ContentPanel} panel The panel
32126          * @param {Object} e The cancel event object
32127          */
32128         "beforeremove" : true,
32129         /**
32130          * @event invalidated
32131          * Fires when the layout for this region is changed.
32132          * @param {Roo.LayoutRegion} this
32133          */
32134         "invalidated" : true,
32135         /**
32136          * @event visibilitychange
32137          * Fires when this region is shown or hidden 
32138          * @param {Roo.LayoutRegion} this
32139          * @param {Boolean} visibility true or false
32140          */
32141         "visibilitychange" : true,
32142         /**
32143          * @event paneladded
32144          * Fires when a panel is added. 
32145          * @param {Roo.LayoutRegion} this
32146          * @param {Roo.ContentPanel} panel The panel
32147          */
32148         "paneladded" : true,
32149         /**
32150          * @event panelremoved
32151          * Fires when a panel is removed. 
32152          * @param {Roo.LayoutRegion} this
32153          * @param {Roo.ContentPanel} panel The panel
32154          */
32155         "panelremoved" : true,
32156         /**
32157          * @event beforecollapse
32158          * Fires when this region before collapse.
32159          * @param {Roo.LayoutRegion} this
32160          */
32161         "beforecollapse" : true,
32162         /**
32163          * @event collapsed
32164          * Fires when this region is collapsed.
32165          * @param {Roo.LayoutRegion} this
32166          */
32167         "collapsed" : true,
32168         /**
32169          * @event expanded
32170          * Fires when this region is expanded.
32171          * @param {Roo.LayoutRegion} this
32172          */
32173         "expanded" : true,
32174         /**
32175          * @event slideshow
32176          * Fires when this region is slid into view.
32177          * @param {Roo.LayoutRegion} this
32178          */
32179         "slideshow" : true,
32180         /**
32181          * @event slidehide
32182          * Fires when this region slides out of view. 
32183          * @param {Roo.LayoutRegion} this
32184          */
32185         "slidehide" : true,
32186         /**
32187          * @event panelactivated
32188          * Fires when a panel is activated. 
32189          * @param {Roo.LayoutRegion} this
32190          * @param {Roo.ContentPanel} panel The activated panel
32191          */
32192         "panelactivated" : true,
32193         /**
32194          * @event resized
32195          * Fires when the user resizes this region. 
32196          * @param {Roo.LayoutRegion} this
32197          * @param {Number} newSize The new size (width for east/west, height for north/south)
32198          */
32199         "resized" : true
32200     };
32201     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32202     this.panels = new Roo.util.MixedCollection();
32203     this.panels.getKey = this.getPanelId.createDelegate(this);
32204     this.box = null;
32205     this.activePanel = null;
32206     // ensure listeners are added...
32207     
32208     if (config.listeners || config.events) {
32209         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32210             listeners : config.listeners || {},
32211             events : config.events || {}
32212         });
32213     }
32214     
32215     if(skipConfig !== true){
32216         this.applyConfig(config);
32217     }
32218 };
32219
32220 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32221 {
32222     getPanelId : function(p){
32223         return p.getId();
32224     },
32225     
32226     applyConfig : function(config){
32227         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32228         this.config = config;
32229         
32230     },
32231     
32232     /**
32233      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32234      * the width, for horizontal (north, south) the height.
32235      * @param {Number} newSize The new width or height
32236      */
32237     resizeTo : function(newSize){
32238         var el = this.el ? this.el :
32239                  (this.activePanel ? this.activePanel.getEl() : null);
32240         if(el){
32241             switch(this.position){
32242                 case "east":
32243                 case "west":
32244                     el.setWidth(newSize);
32245                     this.fireEvent("resized", this, newSize);
32246                 break;
32247                 case "north":
32248                 case "south":
32249                     el.setHeight(newSize);
32250                     this.fireEvent("resized", this, newSize);
32251                 break;                
32252             }
32253         }
32254     },
32255     
32256     getBox : function(){
32257         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32258     },
32259     
32260     getMargins : function(){
32261         return this.margins;
32262     },
32263     
32264     updateBox : function(box){
32265         this.box = box;
32266         var el = this.activePanel.getEl();
32267         el.dom.style.left = box.x + "px";
32268         el.dom.style.top = box.y + "px";
32269         this.activePanel.setSize(box.width, box.height);
32270     },
32271     
32272     /**
32273      * Returns the container element for this region.
32274      * @return {Roo.Element}
32275      */
32276     getEl : function(){
32277         return this.activePanel;
32278     },
32279     
32280     /**
32281      * Returns true if this region is currently visible.
32282      * @return {Boolean}
32283      */
32284     isVisible : function(){
32285         return this.activePanel ? true : false;
32286     },
32287     
32288     setActivePanel : function(panel){
32289         panel = this.getPanel(panel);
32290         if(this.activePanel && this.activePanel != panel){
32291             this.activePanel.setActiveState(false);
32292             this.activePanel.getEl().setLeftTop(-10000,-10000);
32293         }
32294         this.activePanel = panel;
32295         panel.setActiveState(true);
32296         if(this.box){
32297             panel.setSize(this.box.width, this.box.height);
32298         }
32299         this.fireEvent("panelactivated", this, panel);
32300         this.fireEvent("invalidated");
32301     },
32302     
32303     /**
32304      * Show the specified panel.
32305      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32306      * @return {Roo.ContentPanel} The shown panel or null
32307      */
32308     showPanel : function(panel){
32309         panel = this.getPanel(panel);
32310         if(panel){
32311             this.setActivePanel(panel);
32312         }
32313         return panel;
32314     },
32315     
32316     /**
32317      * Get the active panel for this region.
32318      * @return {Roo.ContentPanel} The active panel or null
32319      */
32320     getActivePanel : function(){
32321         return this.activePanel;
32322     },
32323     
32324     /**
32325      * Add the passed ContentPanel(s)
32326      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32327      * @return {Roo.ContentPanel} The panel added (if only one was added)
32328      */
32329     add : function(panel){
32330         if(arguments.length > 1){
32331             for(var i = 0, len = arguments.length; i < len; i++) {
32332                 this.add(arguments[i]);
32333             }
32334             return null;
32335         }
32336         if(this.hasPanel(panel)){
32337             this.showPanel(panel);
32338             return panel;
32339         }
32340         var el = panel.getEl();
32341         if(el.dom.parentNode != this.mgr.el.dom){
32342             this.mgr.el.dom.appendChild(el.dom);
32343         }
32344         if(panel.setRegion){
32345             panel.setRegion(this);
32346         }
32347         this.panels.add(panel);
32348         el.setStyle("position", "absolute");
32349         if(!panel.background){
32350             this.setActivePanel(panel);
32351             if(this.config.initialSize && this.panels.getCount()==1){
32352                 this.resizeTo(this.config.initialSize);
32353             }
32354         }
32355         this.fireEvent("paneladded", this, panel);
32356         return panel;
32357     },
32358     
32359     /**
32360      * Returns true if the panel is in this region.
32361      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32362      * @return {Boolean}
32363      */
32364     hasPanel : function(panel){
32365         if(typeof panel == "object"){ // must be panel obj
32366             panel = panel.getId();
32367         }
32368         return this.getPanel(panel) ? true : false;
32369     },
32370     
32371     /**
32372      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32373      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32374      * @param {Boolean} preservePanel Overrides the config preservePanel option
32375      * @return {Roo.ContentPanel} The panel that was removed
32376      */
32377     remove : function(panel, preservePanel){
32378         panel = this.getPanel(panel);
32379         if(!panel){
32380             return null;
32381         }
32382         var e = {};
32383         this.fireEvent("beforeremove", this, panel, e);
32384         if(e.cancel === true){
32385             return null;
32386         }
32387         var panelId = panel.getId();
32388         this.panels.removeKey(panelId);
32389         return panel;
32390     },
32391     
32392     /**
32393      * Returns the panel specified or null if it's not in this region.
32394      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32395      * @return {Roo.ContentPanel}
32396      */
32397     getPanel : function(id){
32398         if(typeof id == "object"){ // must be panel obj
32399             return id;
32400         }
32401         return this.panels.get(id);
32402     },
32403     
32404     /**
32405      * Returns this regions position (north/south/east/west/center).
32406      * @return {String} 
32407      */
32408     getPosition: function(){
32409         return this.position;    
32410     }
32411 });/*
32412  * Based on:
32413  * Ext JS Library 1.1.1
32414  * Copyright(c) 2006-2007, Ext JS, LLC.
32415  *
32416  * Originally Released Under LGPL - original licence link has changed is not relivant.
32417  *
32418  * Fork - LGPL
32419  * <script type="text/javascript">
32420  */
32421  
32422 /**
32423  * @class Roo.bootstrap.layout.Region
32424  * @extends Roo.bootstrap.layout.Basic
32425  * This class represents a region in a layout manager.
32426  
32427  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32428  * @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})
32429  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32430  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32431  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32432  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32433  * @cfg {String}    title           The title for the region (overrides panel titles)
32434  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32435  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32436  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32437  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32438  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32439  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32440  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32441  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32442  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32443  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32444
32445  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32446  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32447  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32448  * @cfg {Number}    width           For East/West panels
32449  * @cfg {Number}    height          For North/South panels
32450  * @cfg {Boolean}   split           To show the splitter
32451  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32452  * 
32453  * @cfg {string}   cls             Extra CSS classes to add to region
32454  * 
32455  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32456  * @cfg {string}   region  the region that it inhabits..
32457  *
32458
32459  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32460  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32461
32462  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32463  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32464  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32465  */
32466 Roo.bootstrap.layout.Region = function(config)
32467 {
32468     this.applyConfig(config);
32469
32470     var mgr = config.mgr;
32471     var pos = config.region;
32472     config.skipConfig = true;
32473     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32474     
32475     if (mgr.el) {
32476         this.onRender(mgr.el);   
32477     }
32478      
32479     this.visible = true;
32480     this.collapsed = false;
32481 };
32482
32483 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32484
32485     position: '', // set by wrapper (eg. north/south etc..)
32486
32487     createBody : function(){
32488         /** This region's body element 
32489         * @type Roo.Element */
32490         this.bodyEl = this.el.createChild({
32491                 tag: "div",
32492                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32493         });
32494     },
32495
32496     onRender: function(ctr, pos)
32497     {
32498         var dh = Roo.DomHelper;
32499         /** This region's container element 
32500         * @type Roo.Element */
32501         this.el = dh.append(ctr.dom, {
32502                 tag: "div",
32503                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32504             }, true);
32505         /** This region's title element 
32506         * @type Roo.Element */
32507     
32508         this.titleEl = dh.append(this.el.dom,
32509             {
32510                     tag: "div",
32511                     unselectable: "on",
32512                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32513                     children:[
32514                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32515                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32516                     ]}, true);
32517         
32518         this.titleEl.enableDisplayMode();
32519         /** This region's title text element 
32520         * @type HTMLElement */
32521         this.titleTextEl = this.titleEl.dom.firstChild;
32522         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32523         /*
32524         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32525         this.closeBtn.enableDisplayMode();
32526         this.closeBtn.on("click", this.closeClicked, this);
32527         this.closeBtn.hide();
32528     */
32529         this.createBody(this.config);
32530         if(this.config.hideWhenEmpty){
32531             this.hide();
32532             this.on("paneladded", this.validateVisibility, this);
32533             this.on("panelremoved", this.validateVisibility, this);
32534         }
32535         if(this.autoScroll){
32536             this.bodyEl.setStyle("overflow", "auto");
32537         }else{
32538             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32539         }
32540         //if(c.titlebar !== false){
32541             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32542                 this.titleEl.hide();
32543             }else{
32544                 this.titleEl.show();
32545                 if(this.config.title){
32546                     this.titleTextEl.innerHTML = this.config.title;
32547                 }
32548             }
32549         //}
32550         if(this.config.collapsed){
32551             this.collapse(true);
32552         }
32553         if(this.config.hidden){
32554             this.hide();
32555         }
32556     },
32557     
32558     applyConfig : function(c)
32559     {
32560         /*
32561          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32562             var dh = Roo.DomHelper;
32563             if(c.titlebar !== false){
32564                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32565                 this.collapseBtn.on("click", this.collapse, this);
32566                 this.collapseBtn.enableDisplayMode();
32567                 /*
32568                 if(c.showPin === true || this.showPin){
32569                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32570                     this.stickBtn.enableDisplayMode();
32571                     this.stickBtn.on("click", this.expand, this);
32572                     this.stickBtn.hide();
32573                 }
32574                 
32575             }
32576             */
32577             /** This region's collapsed element
32578             * @type Roo.Element */
32579             /*
32580              *
32581             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32582                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32583             ]}, true);
32584             
32585             if(c.floatable !== false){
32586                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32587                this.collapsedEl.on("click", this.collapseClick, this);
32588             }
32589
32590             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32591                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32592                    id: "message", unselectable: "on", style:{"float":"left"}});
32593                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32594              }
32595             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32596             this.expandBtn.on("click", this.expand, this);
32597             
32598         }
32599         
32600         if(this.collapseBtn){
32601             this.collapseBtn.setVisible(c.collapsible == true);
32602         }
32603         
32604         this.cmargins = c.cmargins || this.cmargins ||
32605                          (this.position == "west" || this.position == "east" ?
32606                              {top: 0, left: 2, right:2, bottom: 0} :
32607                              {top: 2, left: 0, right:0, bottom: 2});
32608         */
32609         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32610         
32611         
32612         this.bottomTabs = c.tabPosition != "top";
32613         
32614         this.autoScroll = c.autoScroll || false;
32615         
32616         
32617        
32618         
32619         this.duration = c.duration || .30;
32620         this.slideDuration = c.slideDuration || .45;
32621         this.config = c;
32622        
32623     },
32624     /**
32625      * Returns true if this region is currently visible.
32626      * @return {Boolean}
32627      */
32628     isVisible : function(){
32629         return this.visible;
32630     },
32631
32632     /**
32633      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32634      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32635      */
32636     //setCollapsedTitle : function(title){
32637     //    title = title || "&#160;";
32638      //   if(this.collapsedTitleTextEl){
32639       //      this.collapsedTitleTextEl.innerHTML = title;
32640        // }
32641     //},
32642
32643     getBox : function(){
32644         var b;
32645       //  if(!this.collapsed){
32646             b = this.el.getBox(false, true);
32647        // }else{
32648           //  b = this.collapsedEl.getBox(false, true);
32649         //}
32650         return b;
32651     },
32652
32653     getMargins : function(){
32654         return this.margins;
32655         //return this.collapsed ? this.cmargins : this.margins;
32656     },
32657 /*
32658     highlight : function(){
32659         this.el.addClass("x-layout-panel-dragover");
32660     },
32661
32662     unhighlight : function(){
32663         this.el.removeClass("x-layout-panel-dragover");
32664     },
32665 */
32666     updateBox : function(box)
32667     {
32668         this.box = box;
32669         if(!this.collapsed){
32670             this.el.dom.style.left = box.x + "px";
32671             this.el.dom.style.top = box.y + "px";
32672             this.updateBody(box.width, box.height);
32673         }else{
32674             this.collapsedEl.dom.style.left = box.x + "px";
32675             this.collapsedEl.dom.style.top = box.y + "px";
32676             this.collapsedEl.setSize(box.width, box.height);
32677         }
32678         if(this.tabs){
32679             this.tabs.autoSizeTabs();
32680         }
32681     },
32682
32683     updateBody : function(w, h)
32684     {
32685         if(w !== null){
32686             this.el.setWidth(w);
32687             w -= this.el.getBorderWidth("rl");
32688             if(this.config.adjustments){
32689                 w += this.config.adjustments[0];
32690             }
32691         }
32692         if(h !== null){
32693             this.el.setHeight(h);
32694             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32695             h -= this.el.getBorderWidth("tb");
32696             if(this.config.adjustments){
32697                 h += this.config.adjustments[1];
32698             }
32699             this.bodyEl.setHeight(h);
32700             if(this.tabs){
32701                 h = this.tabs.syncHeight(h);
32702             }
32703         }
32704         if(this.panelSize){
32705             w = w !== null ? w : this.panelSize.width;
32706             h = h !== null ? h : this.panelSize.height;
32707         }
32708         if(this.activePanel){
32709             var el = this.activePanel.getEl();
32710             w = w !== null ? w : el.getWidth();
32711             h = h !== null ? h : el.getHeight();
32712             this.panelSize = {width: w, height: h};
32713             this.activePanel.setSize(w, h);
32714         }
32715         if(Roo.isIE && this.tabs){
32716             this.tabs.el.repaint();
32717         }
32718     },
32719
32720     /**
32721      * Returns the container element for this region.
32722      * @return {Roo.Element}
32723      */
32724     getEl : function(){
32725         return this.el;
32726     },
32727
32728     /**
32729      * Hides this region.
32730      */
32731     hide : function(){
32732         //if(!this.collapsed){
32733             this.el.dom.style.left = "-2000px";
32734             this.el.hide();
32735         //}else{
32736          //   this.collapsedEl.dom.style.left = "-2000px";
32737          //   this.collapsedEl.hide();
32738        // }
32739         this.visible = false;
32740         this.fireEvent("visibilitychange", this, false);
32741     },
32742
32743     /**
32744      * Shows this region if it was previously hidden.
32745      */
32746     show : function(){
32747         //if(!this.collapsed){
32748             this.el.show();
32749         //}else{
32750         //    this.collapsedEl.show();
32751        // }
32752         this.visible = true;
32753         this.fireEvent("visibilitychange", this, true);
32754     },
32755 /*
32756     closeClicked : function(){
32757         if(this.activePanel){
32758             this.remove(this.activePanel);
32759         }
32760     },
32761
32762     collapseClick : function(e){
32763         if(this.isSlid){
32764            e.stopPropagation();
32765            this.slideIn();
32766         }else{
32767            e.stopPropagation();
32768            this.slideOut();
32769         }
32770     },
32771 */
32772     /**
32773      * Collapses this region.
32774      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32775      */
32776     /*
32777     collapse : function(skipAnim, skipCheck = false){
32778         if(this.collapsed) {
32779             return;
32780         }
32781         
32782         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32783             
32784             this.collapsed = true;
32785             if(this.split){
32786                 this.split.el.hide();
32787             }
32788             if(this.config.animate && skipAnim !== true){
32789                 this.fireEvent("invalidated", this);
32790                 this.animateCollapse();
32791             }else{
32792                 this.el.setLocation(-20000,-20000);
32793                 this.el.hide();
32794                 this.collapsedEl.show();
32795                 this.fireEvent("collapsed", this);
32796                 this.fireEvent("invalidated", this);
32797             }
32798         }
32799         
32800     },
32801 */
32802     animateCollapse : function(){
32803         // overridden
32804     },
32805
32806     /**
32807      * Expands this region if it was previously collapsed.
32808      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32809      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32810      */
32811     /*
32812     expand : function(e, skipAnim){
32813         if(e) {
32814             e.stopPropagation();
32815         }
32816         if(!this.collapsed || this.el.hasActiveFx()) {
32817             return;
32818         }
32819         if(this.isSlid){
32820             this.afterSlideIn();
32821             skipAnim = true;
32822         }
32823         this.collapsed = false;
32824         if(this.config.animate && skipAnim !== true){
32825             this.animateExpand();
32826         }else{
32827             this.el.show();
32828             if(this.split){
32829                 this.split.el.show();
32830             }
32831             this.collapsedEl.setLocation(-2000,-2000);
32832             this.collapsedEl.hide();
32833             this.fireEvent("invalidated", this);
32834             this.fireEvent("expanded", this);
32835         }
32836     },
32837 */
32838     animateExpand : function(){
32839         // overridden
32840     },
32841
32842     initTabs : function()
32843     {
32844         this.bodyEl.setStyle("overflow", "hidden");
32845         var ts = new Roo.bootstrap.panel.Tabs({
32846                 el: this.bodyEl.dom,
32847                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32848                 disableTooltips: this.config.disableTabTips,
32849                 toolbar : this.config.toolbar
32850             });
32851         
32852         if(this.config.hideTabs){
32853             ts.stripWrap.setDisplayed(false);
32854         }
32855         this.tabs = ts;
32856         ts.resizeTabs = this.config.resizeTabs === true;
32857         ts.minTabWidth = this.config.minTabWidth || 40;
32858         ts.maxTabWidth = this.config.maxTabWidth || 250;
32859         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32860         ts.monitorResize = false;
32861         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32862         ts.bodyEl.addClass('roo-layout-tabs-body');
32863         this.panels.each(this.initPanelAsTab, this);
32864     },
32865
32866     initPanelAsTab : function(panel){
32867         var ti = this.tabs.addTab(
32868                     panel.getEl().id,
32869                     panel.getTitle(), null,
32870                     this.config.closeOnTab && panel.isClosable()
32871             );
32872         if(panel.tabTip !== undefined){
32873             ti.setTooltip(panel.tabTip);
32874         }
32875         ti.on("activate", function(){
32876               this.setActivePanel(panel);
32877         }, this);
32878         
32879         if(this.config.closeOnTab){
32880             ti.on("beforeclose", function(t, e){
32881                 e.cancel = true;
32882                 this.remove(panel);
32883             }, this);
32884         }
32885         return ti;
32886     },
32887
32888     updatePanelTitle : function(panel, title)
32889     {
32890         if(this.activePanel == panel){
32891             this.updateTitle(title);
32892         }
32893         if(this.tabs){
32894             var ti = this.tabs.getTab(panel.getEl().id);
32895             ti.setText(title);
32896             if(panel.tabTip !== undefined){
32897                 ti.setTooltip(panel.tabTip);
32898             }
32899         }
32900     },
32901
32902     updateTitle : function(title){
32903         if(this.titleTextEl && !this.config.title){
32904             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32905         }
32906     },
32907
32908     setActivePanel : function(panel)
32909     {
32910         panel = this.getPanel(panel);
32911         if(this.activePanel && this.activePanel != panel){
32912             this.activePanel.setActiveState(false);
32913         }
32914         this.activePanel = panel;
32915         panel.setActiveState(true);
32916         if(this.panelSize){
32917             panel.setSize(this.panelSize.width, this.panelSize.height);
32918         }
32919         if(this.closeBtn){
32920             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32921         }
32922         this.updateTitle(panel.getTitle());
32923         if(this.tabs){
32924             this.fireEvent("invalidated", this);
32925         }
32926         this.fireEvent("panelactivated", this, panel);
32927     },
32928
32929     /**
32930      * Shows the specified panel.
32931      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32932      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32933      */
32934     showPanel : function(panel)
32935     {
32936         panel = this.getPanel(panel);
32937         if(panel){
32938             if(this.tabs){
32939                 var tab = this.tabs.getTab(panel.getEl().id);
32940                 if(tab.isHidden()){
32941                     this.tabs.unhideTab(tab.id);
32942                 }
32943                 tab.activate();
32944             }else{
32945                 this.setActivePanel(panel);
32946             }
32947         }
32948         return panel;
32949     },
32950
32951     /**
32952      * Get the active panel for this region.
32953      * @return {Roo.ContentPanel} The active panel or null
32954      */
32955     getActivePanel : function(){
32956         return this.activePanel;
32957     },
32958
32959     validateVisibility : function(){
32960         if(this.panels.getCount() < 1){
32961             this.updateTitle("&#160;");
32962             this.closeBtn.hide();
32963             this.hide();
32964         }else{
32965             if(!this.isVisible()){
32966                 this.show();
32967             }
32968         }
32969     },
32970
32971     /**
32972      * Adds the passed ContentPanel(s) to this region.
32973      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32974      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32975      */
32976     add : function(panel){
32977         if(arguments.length > 1){
32978             for(var i = 0, len = arguments.length; i < len; i++) {
32979                 this.add(arguments[i]);
32980             }
32981             return null;
32982         }
32983         if(this.hasPanel(panel)){
32984             this.showPanel(panel);
32985             return panel;
32986         }
32987         panel.setRegion(this);
32988         this.panels.add(panel);
32989         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32990             this.bodyEl.dom.appendChild(panel.getEl().dom);
32991             if(panel.background !== true){
32992                 this.setActivePanel(panel);
32993             }
32994             this.fireEvent("paneladded", this, panel);
32995             return panel;
32996         }
32997         if(!this.tabs){
32998             this.initTabs();
32999         }else{
33000             this.initPanelAsTab(panel);
33001         }
33002         
33003         
33004         if(panel.background !== true){
33005             this.tabs.activate(panel.getEl().id);
33006         }
33007         this.fireEvent("paneladded", this, panel);
33008         return panel;
33009     },
33010
33011     /**
33012      * Hides the tab for the specified panel.
33013      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33014      */
33015     hidePanel : function(panel){
33016         if(this.tabs && (panel = this.getPanel(panel))){
33017             this.tabs.hideTab(panel.getEl().id);
33018         }
33019     },
33020
33021     /**
33022      * Unhides the tab for a previously hidden panel.
33023      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33024      */
33025     unhidePanel : function(panel){
33026         if(this.tabs && (panel = this.getPanel(panel))){
33027             this.tabs.unhideTab(panel.getEl().id);
33028         }
33029     },
33030
33031     clearPanels : function(){
33032         while(this.panels.getCount() > 0){
33033              this.remove(this.panels.first());
33034         }
33035     },
33036
33037     /**
33038      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33039      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33040      * @param {Boolean} preservePanel Overrides the config preservePanel option
33041      * @return {Roo.ContentPanel} The panel that was removed
33042      */
33043     remove : function(panel, preservePanel)
33044     {
33045         panel = this.getPanel(panel);
33046         if(!panel){
33047             return null;
33048         }
33049         var e = {};
33050         this.fireEvent("beforeremove", this, panel, e);
33051         if(e.cancel === true){
33052             return null;
33053         }
33054         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33055         var panelId = panel.getId();
33056         this.panels.removeKey(panelId);
33057         if(preservePanel){
33058             document.body.appendChild(panel.getEl().dom);
33059         }
33060         if(this.tabs){
33061             this.tabs.removeTab(panel.getEl().id);
33062         }else if (!preservePanel){
33063             this.bodyEl.dom.removeChild(panel.getEl().dom);
33064         }
33065         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33066             var p = this.panels.first();
33067             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33068             tempEl.appendChild(p.getEl().dom);
33069             this.bodyEl.update("");
33070             this.bodyEl.dom.appendChild(p.getEl().dom);
33071             tempEl = null;
33072             this.updateTitle(p.getTitle());
33073             this.tabs = null;
33074             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33075             this.setActivePanel(p);
33076         }
33077         panel.setRegion(null);
33078         if(this.activePanel == panel){
33079             this.activePanel = null;
33080         }
33081         if(this.config.autoDestroy !== false && preservePanel !== true){
33082             try{panel.destroy();}catch(e){}
33083         }
33084         this.fireEvent("panelremoved", this, panel);
33085         return panel;
33086     },
33087
33088     /**
33089      * Returns the TabPanel component used by this region
33090      * @return {Roo.TabPanel}
33091      */
33092     getTabs : function(){
33093         return this.tabs;
33094     },
33095
33096     createTool : function(parentEl, className){
33097         var btn = Roo.DomHelper.append(parentEl, {
33098             tag: "div",
33099             cls: "x-layout-tools-button",
33100             children: [ {
33101                 tag: "div",
33102                 cls: "roo-layout-tools-button-inner " + className,
33103                 html: "&#160;"
33104             }]
33105         }, true);
33106         btn.addClassOnOver("roo-layout-tools-button-over");
33107         return btn;
33108     }
33109 });/*
33110  * Based on:
33111  * Ext JS Library 1.1.1
33112  * Copyright(c) 2006-2007, Ext JS, LLC.
33113  *
33114  * Originally Released Under LGPL - original licence link has changed is not relivant.
33115  *
33116  * Fork - LGPL
33117  * <script type="text/javascript">
33118  */
33119  
33120
33121
33122 /**
33123  * @class Roo.SplitLayoutRegion
33124  * @extends Roo.LayoutRegion
33125  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33126  */
33127 Roo.bootstrap.layout.Split = function(config){
33128     this.cursor = config.cursor;
33129     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33130 };
33131
33132 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33133 {
33134     splitTip : "Drag to resize.",
33135     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33136     useSplitTips : false,
33137
33138     applyConfig : function(config){
33139         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33140     },
33141     
33142     onRender : function(ctr,pos) {
33143         
33144         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33145         if(!this.config.split){
33146             return;
33147         }
33148         if(!this.split){
33149             
33150             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33151                             tag: "div",
33152                             id: this.el.id + "-split",
33153                             cls: "roo-layout-split roo-layout-split-"+this.position,
33154                             html: "&#160;"
33155             });
33156             /** The SplitBar for this region 
33157             * @type Roo.SplitBar */
33158             // does not exist yet...
33159             Roo.log([this.position, this.orientation]);
33160             
33161             this.split = new Roo.bootstrap.SplitBar({
33162                 dragElement : splitEl,
33163                 resizingElement: this.el,
33164                 orientation : this.orientation
33165             });
33166             
33167             this.split.on("moved", this.onSplitMove, this);
33168             this.split.useShim = this.config.useShim === true;
33169             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33170             if(this.useSplitTips){
33171                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33172             }
33173             //if(config.collapsible){
33174             //    this.split.el.on("dblclick", this.collapse,  this);
33175             //}
33176         }
33177         if(typeof this.config.minSize != "undefined"){
33178             this.split.minSize = this.config.minSize;
33179         }
33180         if(typeof this.config.maxSize != "undefined"){
33181             this.split.maxSize = this.config.maxSize;
33182         }
33183         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33184             this.hideSplitter();
33185         }
33186         
33187     },
33188
33189     getHMaxSize : function(){
33190          var cmax = this.config.maxSize || 10000;
33191          var center = this.mgr.getRegion("center");
33192          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33193     },
33194
33195     getVMaxSize : function(){
33196          var cmax = this.config.maxSize || 10000;
33197          var center = this.mgr.getRegion("center");
33198          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33199     },
33200
33201     onSplitMove : function(split, newSize){
33202         this.fireEvent("resized", this, newSize);
33203     },
33204     
33205     /** 
33206      * Returns the {@link Roo.SplitBar} for this region.
33207      * @return {Roo.SplitBar}
33208      */
33209     getSplitBar : function(){
33210         return this.split;
33211     },
33212     
33213     hide : function(){
33214         this.hideSplitter();
33215         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33216     },
33217
33218     hideSplitter : function(){
33219         if(this.split){
33220             this.split.el.setLocation(-2000,-2000);
33221             this.split.el.hide();
33222         }
33223     },
33224
33225     show : function(){
33226         if(this.split){
33227             this.split.el.show();
33228         }
33229         Roo.bootstrap.layout.Split.superclass.show.call(this);
33230     },
33231     
33232     beforeSlide: function(){
33233         if(Roo.isGecko){// firefox overflow auto bug workaround
33234             this.bodyEl.clip();
33235             if(this.tabs) {
33236                 this.tabs.bodyEl.clip();
33237             }
33238             if(this.activePanel){
33239                 this.activePanel.getEl().clip();
33240                 
33241                 if(this.activePanel.beforeSlide){
33242                     this.activePanel.beforeSlide();
33243                 }
33244             }
33245         }
33246     },
33247     
33248     afterSlide : function(){
33249         if(Roo.isGecko){// firefox overflow auto bug workaround
33250             this.bodyEl.unclip();
33251             if(this.tabs) {
33252                 this.tabs.bodyEl.unclip();
33253             }
33254             if(this.activePanel){
33255                 this.activePanel.getEl().unclip();
33256                 if(this.activePanel.afterSlide){
33257                     this.activePanel.afterSlide();
33258                 }
33259             }
33260         }
33261     },
33262
33263     initAutoHide : function(){
33264         if(this.autoHide !== false){
33265             if(!this.autoHideHd){
33266                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33267                 this.autoHideHd = {
33268                     "mouseout": function(e){
33269                         if(!e.within(this.el, true)){
33270                             st.delay(500);
33271                         }
33272                     },
33273                     "mouseover" : function(e){
33274                         st.cancel();
33275                     },
33276                     scope : this
33277                 };
33278             }
33279             this.el.on(this.autoHideHd);
33280         }
33281     },
33282
33283     clearAutoHide : function(){
33284         if(this.autoHide !== false){
33285             this.el.un("mouseout", this.autoHideHd.mouseout);
33286             this.el.un("mouseover", this.autoHideHd.mouseover);
33287         }
33288     },
33289
33290     clearMonitor : function(){
33291         Roo.get(document).un("click", this.slideInIf, this);
33292     },
33293
33294     // these names are backwards but not changed for compat
33295     slideOut : function(){
33296         if(this.isSlid || this.el.hasActiveFx()){
33297             return;
33298         }
33299         this.isSlid = true;
33300         if(this.collapseBtn){
33301             this.collapseBtn.hide();
33302         }
33303         this.closeBtnState = this.closeBtn.getStyle('display');
33304         this.closeBtn.hide();
33305         if(this.stickBtn){
33306             this.stickBtn.show();
33307         }
33308         this.el.show();
33309         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33310         this.beforeSlide();
33311         this.el.setStyle("z-index", 10001);
33312         this.el.slideIn(this.getSlideAnchor(), {
33313             callback: function(){
33314                 this.afterSlide();
33315                 this.initAutoHide();
33316                 Roo.get(document).on("click", this.slideInIf, this);
33317                 this.fireEvent("slideshow", this);
33318             },
33319             scope: this,
33320             block: true
33321         });
33322     },
33323
33324     afterSlideIn : function(){
33325         this.clearAutoHide();
33326         this.isSlid = false;
33327         this.clearMonitor();
33328         this.el.setStyle("z-index", "");
33329         if(this.collapseBtn){
33330             this.collapseBtn.show();
33331         }
33332         this.closeBtn.setStyle('display', this.closeBtnState);
33333         if(this.stickBtn){
33334             this.stickBtn.hide();
33335         }
33336         this.fireEvent("slidehide", this);
33337     },
33338
33339     slideIn : function(cb){
33340         if(!this.isSlid || this.el.hasActiveFx()){
33341             Roo.callback(cb);
33342             return;
33343         }
33344         this.isSlid = false;
33345         this.beforeSlide();
33346         this.el.slideOut(this.getSlideAnchor(), {
33347             callback: function(){
33348                 this.el.setLeftTop(-10000, -10000);
33349                 this.afterSlide();
33350                 this.afterSlideIn();
33351                 Roo.callback(cb);
33352             },
33353             scope: this,
33354             block: true
33355         });
33356     },
33357     
33358     slideInIf : function(e){
33359         if(!e.within(this.el)){
33360             this.slideIn();
33361         }
33362     },
33363
33364     animateCollapse : function(){
33365         this.beforeSlide();
33366         this.el.setStyle("z-index", 20000);
33367         var anchor = this.getSlideAnchor();
33368         this.el.slideOut(anchor, {
33369             callback : function(){
33370                 this.el.setStyle("z-index", "");
33371                 this.collapsedEl.slideIn(anchor, {duration:.3});
33372                 this.afterSlide();
33373                 this.el.setLocation(-10000,-10000);
33374                 this.el.hide();
33375                 this.fireEvent("collapsed", this);
33376             },
33377             scope: this,
33378             block: true
33379         });
33380     },
33381
33382     animateExpand : function(){
33383         this.beforeSlide();
33384         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33385         this.el.setStyle("z-index", 20000);
33386         this.collapsedEl.hide({
33387             duration:.1
33388         });
33389         this.el.slideIn(this.getSlideAnchor(), {
33390             callback : function(){
33391                 this.el.setStyle("z-index", "");
33392                 this.afterSlide();
33393                 if(this.split){
33394                     this.split.el.show();
33395                 }
33396                 this.fireEvent("invalidated", this);
33397                 this.fireEvent("expanded", this);
33398             },
33399             scope: this,
33400             block: true
33401         });
33402     },
33403
33404     anchors : {
33405         "west" : "left",
33406         "east" : "right",
33407         "north" : "top",
33408         "south" : "bottom"
33409     },
33410
33411     sanchors : {
33412         "west" : "l",
33413         "east" : "r",
33414         "north" : "t",
33415         "south" : "b"
33416     },
33417
33418     canchors : {
33419         "west" : "tl-tr",
33420         "east" : "tr-tl",
33421         "north" : "tl-bl",
33422         "south" : "bl-tl"
33423     },
33424
33425     getAnchor : function(){
33426         return this.anchors[this.position];
33427     },
33428
33429     getCollapseAnchor : function(){
33430         return this.canchors[this.position];
33431     },
33432
33433     getSlideAnchor : function(){
33434         return this.sanchors[this.position];
33435     },
33436
33437     getAlignAdj : function(){
33438         var cm = this.cmargins;
33439         switch(this.position){
33440             case "west":
33441                 return [0, 0];
33442             break;
33443             case "east":
33444                 return [0, 0];
33445             break;
33446             case "north":
33447                 return [0, 0];
33448             break;
33449             case "south":
33450                 return [0, 0];
33451             break;
33452         }
33453     },
33454
33455     getExpandAdj : function(){
33456         var c = this.collapsedEl, cm = this.cmargins;
33457         switch(this.position){
33458             case "west":
33459                 return [-(cm.right+c.getWidth()+cm.left), 0];
33460             break;
33461             case "east":
33462                 return [cm.right+c.getWidth()+cm.left, 0];
33463             break;
33464             case "north":
33465                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33466             break;
33467             case "south":
33468                 return [0, cm.top+cm.bottom+c.getHeight()];
33469             break;
33470         }
33471     }
33472 });/*
33473  * Based on:
33474  * Ext JS Library 1.1.1
33475  * Copyright(c) 2006-2007, Ext JS, LLC.
33476  *
33477  * Originally Released Under LGPL - original licence link has changed is not relivant.
33478  *
33479  * Fork - LGPL
33480  * <script type="text/javascript">
33481  */
33482 /*
33483  * These classes are private internal classes
33484  */
33485 Roo.bootstrap.layout.Center = function(config){
33486     config.region = "center";
33487     Roo.bootstrap.layout.Region.call(this, config);
33488     this.visible = true;
33489     this.minWidth = config.minWidth || 20;
33490     this.minHeight = config.minHeight || 20;
33491 };
33492
33493 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33494     hide : function(){
33495         // center panel can't be hidden
33496     },
33497     
33498     show : function(){
33499         // center panel can't be hidden
33500     },
33501     
33502     getMinWidth: function(){
33503         return this.minWidth;
33504     },
33505     
33506     getMinHeight: function(){
33507         return this.minHeight;
33508     }
33509 });
33510
33511
33512
33513
33514  
33515
33516
33517
33518
33519
33520 Roo.bootstrap.layout.North = function(config)
33521 {
33522     config.region = 'north';
33523     config.cursor = 'n-resize';
33524     
33525     Roo.bootstrap.layout.Split.call(this, config);
33526     
33527     
33528     if(this.split){
33529         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33530         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33531         this.split.el.addClass("roo-layout-split-v");
33532     }
33533     var size = config.initialSize || config.height;
33534     if(typeof size != "undefined"){
33535         this.el.setHeight(size);
33536     }
33537 };
33538 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33539 {
33540     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33541     
33542     
33543     
33544     getBox : function(){
33545         if(this.collapsed){
33546             return this.collapsedEl.getBox();
33547         }
33548         var box = this.el.getBox();
33549         if(this.split){
33550             box.height += this.split.el.getHeight();
33551         }
33552         return box;
33553     },
33554     
33555     updateBox : function(box){
33556         if(this.split && !this.collapsed){
33557             box.height -= this.split.el.getHeight();
33558             this.split.el.setLeft(box.x);
33559             this.split.el.setTop(box.y+box.height);
33560             this.split.el.setWidth(box.width);
33561         }
33562         if(this.collapsed){
33563             this.updateBody(box.width, null);
33564         }
33565         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33566     }
33567 });
33568
33569
33570
33571
33572
33573 Roo.bootstrap.layout.South = function(config){
33574     config.region = 'south';
33575     config.cursor = 's-resize';
33576     Roo.bootstrap.layout.Split.call(this, config);
33577     if(this.split){
33578         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33579         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33580         this.split.el.addClass("roo-layout-split-v");
33581     }
33582     var size = config.initialSize || config.height;
33583     if(typeof size != "undefined"){
33584         this.el.setHeight(size);
33585     }
33586 };
33587
33588 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33589     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33590     getBox : function(){
33591         if(this.collapsed){
33592             return this.collapsedEl.getBox();
33593         }
33594         var box = this.el.getBox();
33595         if(this.split){
33596             var sh = this.split.el.getHeight();
33597             box.height += sh;
33598             box.y -= sh;
33599         }
33600         return box;
33601     },
33602     
33603     updateBox : function(box){
33604         if(this.split && !this.collapsed){
33605             var sh = this.split.el.getHeight();
33606             box.height -= sh;
33607             box.y += sh;
33608             this.split.el.setLeft(box.x);
33609             this.split.el.setTop(box.y-sh);
33610             this.split.el.setWidth(box.width);
33611         }
33612         if(this.collapsed){
33613             this.updateBody(box.width, null);
33614         }
33615         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33616     }
33617 });
33618
33619 Roo.bootstrap.layout.East = function(config){
33620     config.region = "east";
33621     config.cursor = "e-resize";
33622     Roo.bootstrap.layout.Split.call(this, config);
33623     if(this.split){
33624         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33625         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33626         this.split.el.addClass("roo-layout-split-h");
33627     }
33628     var size = config.initialSize || config.width;
33629     if(typeof size != "undefined"){
33630         this.el.setWidth(size);
33631     }
33632 };
33633 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33634     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33635     getBox : function(){
33636         if(this.collapsed){
33637             return this.collapsedEl.getBox();
33638         }
33639         var box = this.el.getBox();
33640         if(this.split){
33641             var sw = this.split.el.getWidth();
33642             box.width += sw;
33643             box.x -= sw;
33644         }
33645         return box;
33646     },
33647
33648     updateBox : function(box){
33649         if(this.split && !this.collapsed){
33650             var sw = this.split.el.getWidth();
33651             box.width -= sw;
33652             this.split.el.setLeft(box.x);
33653             this.split.el.setTop(box.y);
33654             this.split.el.setHeight(box.height);
33655             box.x += sw;
33656         }
33657         if(this.collapsed){
33658             this.updateBody(null, box.height);
33659         }
33660         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33661     }
33662 });
33663
33664 Roo.bootstrap.layout.West = function(config){
33665     config.region = "west";
33666     config.cursor = "w-resize";
33667     
33668     Roo.bootstrap.layout.Split.call(this, config);
33669     if(this.split){
33670         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33671         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33672         this.split.el.addClass("roo-layout-split-h");
33673     }
33674     
33675 };
33676 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33677     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33678     
33679     onRender: function(ctr, pos)
33680     {
33681         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33682         var size = this.config.initialSize || this.config.width;
33683         if(typeof size != "undefined"){
33684             this.el.setWidth(size);
33685         }
33686     },
33687     
33688     getBox : function(){
33689         if(this.collapsed){
33690             return this.collapsedEl.getBox();
33691         }
33692         var box = this.el.getBox();
33693         if(this.split){
33694             box.width += this.split.el.getWidth();
33695         }
33696         return box;
33697     },
33698     
33699     updateBox : function(box){
33700         if(this.split && !this.collapsed){
33701             var sw = this.split.el.getWidth();
33702             box.width -= sw;
33703             this.split.el.setLeft(box.x+box.width);
33704             this.split.el.setTop(box.y);
33705             this.split.el.setHeight(box.height);
33706         }
33707         if(this.collapsed){
33708             this.updateBody(null, box.height);
33709         }
33710         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33711     }
33712 });
33713 Roo.namespace("Roo.bootstrap.panel");/*
33714  * Based on:
33715  * Ext JS Library 1.1.1
33716  * Copyright(c) 2006-2007, Ext JS, LLC.
33717  *
33718  * Originally Released Under LGPL - original licence link has changed is not relivant.
33719  *
33720  * Fork - LGPL
33721  * <script type="text/javascript">
33722  */
33723 /**
33724  * @class Roo.ContentPanel
33725  * @extends Roo.util.Observable
33726  * A basic ContentPanel element.
33727  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33728  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33729  * @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
33730  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33731  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33732  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33733  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33734  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33735  * @cfg {String} title          The title for this panel
33736  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33737  * @cfg {String} url            Calls {@link #setUrl} with this value
33738  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33739  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33740  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33741  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33742
33743  * @constructor
33744  * Create a new ContentPanel.
33745  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33746  * @param {String/Object} config A string to set only the title or a config object
33747  * @param {String} content (optional) Set the HTML content for this panel
33748  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33749  */
33750 Roo.bootstrap.panel.Content = function( config){
33751     
33752     var el = config.el;
33753     var content = config.content;
33754
33755     if(config.autoCreate){ // xtype is available if this is called from factory
33756         el = Roo.id();
33757     }
33758     this.el = Roo.get(el);
33759     if(!this.el && config && config.autoCreate){
33760         if(typeof config.autoCreate == "object"){
33761             if(!config.autoCreate.id){
33762                 config.autoCreate.id = config.id||el;
33763             }
33764             this.el = Roo.DomHelper.append(document.body,
33765                         config.autoCreate, true);
33766         }else{
33767             var elcfg =  {   tag: "div",
33768                             cls: "roo-layout-inactive-content",
33769                             id: config.id||el
33770                             };
33771             if (config.html) {
33772                 elcfg.html = config.html;
33773                 
33774             }
33775                         
33776             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33777         }
33778     } 
33779     this.closable = false;
33780     this.loaded = false;
33781     this.active = false;
33782    
33783       
33784     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33785         
33786         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33787         
33788         this.wrapEl = this.el.wrap();
33789         var ti = [];
33790         if (config.toolbar.items) {
33791             ti = config.toolbar.items ;
33792             delete config.toolbar.items ;
33793         }
33794         
33795         var nitems = [];
33796         this.toolbar.render(this.wrapEl, 'before');
33797         for(var i =0;i < ti.length;i++) {
33798           //  Roo.log(['add child', items[i]]);
33799             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33800         }
33801         this.toolbar.items = nitems;
33802         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33803         delete config.toolbar;
33804         
33805     }
33806     /*
33807     // xtype created footer. - not sure if will work as we normally have to render first..
33808     if (this.footer && !this.footer.el && this.footer.xtype) {
33809         if (!this.wrapEl) {
33810             this.wrapEl = this.el.wrap();
33811         }
33812     
33813         this.footer.container = this.wrapEl.createChild();
33814          
33815         this.footer = Roo.factory(this.footer, Roo);
33816         
33817     }
33818     */
33819     
33820      if(typeof config == "string"){
33821         this.title = config;
33822     }else{
33823         Roo.apply(this, config);
33824     }
33825     
33826     if(this.resizeEl){
33827         this.resizeEl = Roo.get(this.resizeEl, true);
33828     }else{
33829         this.resizeEl = this.el;
33830     }
33831     // handle view.xtype
33832     
33833  
33834     
33835     
33836     this.addEvents({
33837         /**
33838          * @event activate
33839          * Fires when this panel is activated. 
33840          * @param {Roo.ContentPanel} this
33841          */
33842         "activate" : true,
33843         /**
33844          * @event deactivate
33845          * Fires when this panel is activated. 
33846          * @param {Roo.ContentPanel} this
33847          */
33848         "deactivate" : true,
33849
33850         /**
33851          * @event resize
33852          * Fires when this panel is resized if fitToFrame is true.
33853          * @param {Roo.ContentPanel} this
33854          * @param {Number} width The width after any component adjustments
33855          * @param {Number} height The height after any component adjustments
33856          */
33857         "resize" : true,
33858         
33859          /**
33860          * @event render
33861          * Fires when this tab is created
33862          * @param {Roo.ContentPanel} this
33863          */
33864         "render" : true
33865         
33866         
33867         
33868     });
33869     
33870
33871     
33872     
33873     if(this.autoScroll){
33874         this.resizeEl.setStyle("overflow", "auto");
33875     } else {
33876         // fix randome scrolling
33877         //this.el.on('scroll', function() {
33878         //    Roo.log('fix random scolling');
33879         //    this.scrollTo('top',0); 
33880         //});
33881     }
33882     content = content || this.content;
33883     if(content){
33884         this.setContent(content);
33885     }
33886     if(config && config.url){
33887         this.setUrl(this.url, this.params, this.loadOnce);
33888     }
33889     
33890     
33891     
33892     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33893     
33894     if (this.view && typeof(this.view.xtype) != 'undefined') {
33895         this.view.el = this.el.appendChild(document.createElement("div"));
33896         this.view = Roo.factory(this.view); 
33897         this.view.render  &&  this.view.render(false, '');  
33898     }
33899     
33900     
33901     this.fireEvent('render', this);
33902 };
33903
33904 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33905     tabTip:'',
33906     setRegion : function(region){
33907         this.region = region;
33908         if(region){
33909            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33910         }else{
33911            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33912         } 
33913     },
33914     
33915     /**
33916      * Returns the toolbar for this Panel if one was configured. 
33917      * @return {Roo.Toolbar} 
33918      */
33919     getToolbar : function(){
33920         return this.toolbar;
33921     },
33922     
33923     setActiveState : function(active){
33924         this.active = active;
33925         if(!active){
33926             this.fireEvent("deactivate", this);
33927         }else{
33928             this.fireEvent("activate", this);
33929         }
33930     },
33931     /**
33932      * Updates this panel's element
33933      * @param {String} content The new content
33934      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33935     */
33936     setContent : function(content, loadScripts){
33937         this.el.update(content, loadScripts);
33938     },
33939
33940     ignoreResize : function(w, h){
33941         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33942             return true;
33943         }else{
33944             this.lastSize = {width: w, height: h};
33945             return false;
33946         }
33947     },
33948     /**
33949      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33950      * @return {Roo.UpdateManager} The UpdateManager
33951      */
33952     getUpdateManager : function(){
33953         return this.el.getUpdateManager();
33954     },
33955      /**
33956      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33957      * @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:
33958 <pre><code>
33959 panel.load({
33960     url: "your-url.php",
33961     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33962     callback: yourFunction,
33963     scope: yourObject, //(optional scope)
33964     discardUrl: false,
33965     nocache: false,
33966     text: "Loading...",
33967     timeout: 30,
33968     scripts: false
33969 });
33970 </code></pre>
33971      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33972      * 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.
33973      * @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}
33974      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33975      * @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.
33976      * @return {Roo.ContentPanel} this
33977      */
33978     load : function(){
33979         var um = this.el.getUpdateManager();
33980         um.update.apply(um, arguments);
33981         return this;
33982     },
33983
33984
33985     /**
33986      * 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.
33987      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33988      * @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)
33989      * @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)
33990      * @return {Roo.UpdateManager} The UpdateManager
33991      */
33992     setUrl : function(url, params, loadOnce){
33993         if(this.refreshDelegate){
33994             this.removeListener("activate", this.refreshDelegate);
33995         }
33996         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33997         this.on("activate", this.refreshDelegate);
33998         return this.el.getUpdateManager();
33999     },
34000     
34001     _handleRefresh : function(url, params, loadOnce){
34002         if(!loadOnce || !this.loaded){
34003             var updater = this.el.getUpdateManager();
34004             updater.update(url, params, this._setLoaded.createDelegate(this));
34005         }
34006     },
34007     
34008     _setLoaded : function(){
34009         this.loaded = true;
34010     }, 
34011     
34012     /**
34013      * Returns this panel's id
34014      * @return {String} 
34015      */
34016     getId : function(){
34017         return this.el.id;
34018     },
34019     
34020     /** 
34021      * Returns this panel's element - used by regiosn to add.
34022      * @return {Roo.Element} 
34023      */
34024     getEl : function(){
34025         return this.wrapEl || this.el;
34026     },
34027     
34028    
34029     
34030     adjustForComponents : function(width, height)
34031     {
34032         //Roo.log('adjustForComponents ');
34033         if(this.resizeEl != this.el){
34034             width -= this.el.getFrameWidth('lr');
34035             height -= this.el.getFrameWidth('tb');
34036         }
34037         if(this.toolbar){
34038             var te = this.toolbar.getEl();
34039             height -= te.getHeight();
34040             te.setWidth(width);
34041         }
34042         if(this.footer){
34043             var te = this.footer.getEl();
34044             Roo.log("footer:" + te.getHeight());
34045             
34046             height -= te.getHeight();
34047             te.setWidth(width);
34048         }
34049         
34050         
34051         if(this.adjustments){
34052             width += this.adjustments[0];
34053             height += this.adjustments[1];
34054         }
34055         return {"width": width, "height": height};
34056     },
34057     
34058     setSize : function(width, height){
34059         if(this.fitToFrame && !this.ignoreResize(width, height)){
34060             if(this.fitContainer && this.resizeEl != this.el){
34061                 this.el.setSize(width, height);
34062             }
34063             var size = this.adjustForComponents(width, height);
34064             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34065             this.fireEvent('resize', this, size.width, size.height);
34066         }
34067     },
34068     
34069     /**
34070      * Returns this panel's title
34071      * @return {String} 
34072      */
34073     getTitle : function(){
34074         return this.title;
34075     },
34076     
34077     /**
34078      * Set this panel's title
34079      * @param {String} title
34080      */
34081     setTitle : function(title){
34082         this.title = title;
34083         if(this.region){
34084             this.region.updatePanelTitle(this, title);
34085         }
34086     },
34087     
34088     /**
34089      * Returns true is this panel was configured to be closable
34090      * @return {Boolean} 
34091      */
34092     isClosable : function(){
34093         return this.closable;
34094     },
34095     
34096     beforeSlide : function(){
34097         this.el.clip();
34098         this.resizeEl.clip();
34099     },
34100     
34101     afterSlide : function(){
34102         this.el.unclip();
34103         this.resizeEl.unclip();
34104     },
34105     
34106     /**
34107      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34108      *   Will fail silently if the {@link #setUrl} method has not been called.
34109      *   This does not activate the panel, just updates its content.
34110      */
34111     refresh : function(){
34112         if(this.refreshDelegate){
34113            this.loaded = false;
34114            this.refreshDelegate();
34115         }
34116     },
34117     
34118     /**
34119      * Destroys this panel
34120      */
34121     destroy : function(){
34122         this.el.removeAllListeners();
34123         var tempEl = document.createElement("span");
34124         tempEl.appendChild(this.el.dom);
34125         tempEl.innerHTML = "";
34126         this.el.remove();
34127         this.el = null;
34128     },
34129     
34130     /**
34131      * form - if the content panel contains a form - this is a reference to it.
34132      * @type {Roo.form.Form}
34133      */
34134     form : false,
34135     /**
34136      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34137      *    This contains a reference to it.
34138      * @type {Roo.View}
34139      */
34140     view : false,
34141     
34142       /**
34143      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34144      * <pre><code>
34145
34146 layout.addxtype({
34147        xtype : 'Form',
34148        items: [ .... ]
34149    }
34150 );
34151
34152 </code></pre>
34153      * @param {Object} cfg Xtype definition of item to add.
34154      */
34155     
34156     
34157     getChildContainer: function () {
34158         return this.getEl();
34159     }
34160     
34161     
34162     /*
34163         var  ret = new Roo.factory(cfg);
34164         return ret;
34165         
34166         
34167         // add form..
34168         if (cfg.xtype.match(/^Form$/)) {
34169             
34170             var el;
34171             //if (this.footer) {
34172             //    el = this.footer.container.insertSibling(false, 'before');
34173             //} else {
34174                 el = this.el.createChild();
34175             //}
34176
34177             this.form = new  Roo.form.Form(cfg);
34178             
34179             
34180             if ( this.form.allItems.length) {
34181                 this.form.render(el.dom);
34182             }
34183             return this.form;
34184         }
34185         // should only have one of theses..
34186         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34187             // views.. should not be just added - used named prop 'view''
34188             
34189             cfg.el = this.el.appendChild(document.createElement("div"));
34190             // factory?
34191             
34192             var ret = new Roo.factory(cfg);
34193              
34194              ret.render && ret.render(false, ''); // render blank..
34195             this.view = ret;
34196             return ret;
34197         }
34198         return false;
34199     }
34200     \*/
34201 });
34202  
34203 /**
34204  * @class Roo.bootstrap.panel.Grid
34205  * @extends Roo.bootstrap.panel.Content
34206  * @constructor
34207  * Create a new GridPanel.
34208  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34209  * @param {Object} config A the config object
34210   
34211  */
34212
34213
34214
34215 Roo.bootstrap.panel.Grid = function(config)
34216 {
34217     
34218       
34219     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34220         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34221
34222     config.el = this.wrapper;
34223     //this.el = this.wrapper;
34224     
34225       if (config.container) {
34226         // ctor'ed from a Border/panel.grid
34227         
34228         
34229         this.wrapper.setStyle("overflow", "hidden");
34230         this.wrapper.addClass('roo-grid-container');
34231
34232     }
34233     
34234     
34235     if(config.toolbar){
34236         var tool_el = this.wrapper.createChild();    
34237         this.toolbar = Roo.factory(config.toolbar);
34238         var ti = [];
34239         if (config.toolbar.items) {
34240             ti = config.toolbar.items ;
34241             delete config.toolbar.items ;
34242         }
34243         
34244         var nitems = [];
34245         this.toolbar.render(tool_el);
34246         for(var i =0;i < ti.length;i++) {
34247           //  Roo.log(['add child', items[i]]);
34248             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34249         }
34250         this.toolbar.items = nitems;
34251         
34252         delete config.toolbar;
34253     }
34254     
34255     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34256     config.grid.scrollBody = true;;
34257     config.grid.monitorWindowResize = false; // turn off autosizing
34258     config.grid.autoHeight = false;
34259     config.grid.autoWidth = false;
34260     
34261     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34262     
34263     if (config.background) {
34264         // render grid on panel activation (if panel background)
34265         this.on('activate', function(gp) {
34266             if (!gp.grid.rendered) {
34267                 gp.grid.render(el);
34268                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34269
34270             }
34271         });
34272             
34273     } else {
34274         this.grid.render(this.wrapper);
34275         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34276
34277     }
34278     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34279     // ??? needed ??? config.el = this.wrapper;
34280     
34281     
34282     
34283   
34284     // xtype created footer. - not sure if will work as we normally have to render first..
34285     if (this.footer && !this.footer.el && this.footer.xtype) {
34286         
34287         var ctr = this.grid.getView().getFooterPanel(true);
34288         this.footer.dataSource = this.grid.dataSource;
34289         this.footer = Roo.factory(this.footer, Roo);
34290         this.footer.render(ctr);
34291         
34292     }
34293     
34294     
34295     
34296     
34297      
34298 };
34299
34300 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34301     getId : function(){
34302         return this.grid.id;
34303     },
34304     
34305     /**
34306      * Returns the grid for this panel
34307      * @return {Roo.bootstrap.Table} 
34308      */
34309     getGrid : function(){
34310         return this.grid;    
34311     },
34312     
34313     setSize : function(width, height){
34314         if(!this.ignoreResize(width, height)){
34315             var grid = this.grid;
34316             var size = this.adjustForComponents(width, height);
34317             var gridel = grid.getGridEl();
34318             gridel.setSize(size.width, size.height);
34319             /*
34320             var thd = grid.getGridEl().select('thead',true).first();
34321             var tbd = grid.getGridEl().select('tbody', true).first();
34322             if (tbd) {
34323                 tbd.setSize(width, height - thd.getHeight());
34324             }
34325             */
34326             grid.autoSize();
34327         }
34328     },
34329      
34330     
34331     
34332     beforeSlide : function(){
34333         this.grid.getView().scroller.clip();
34334     },
34335     
34336     afterSlide : function(){
34337         this.grid.getView().scroller.unclip();
34338     },
34339     
34340     destroy : function(){
34341         this.grid.destroy();
34342         delete this.grid;
34343         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34344     }
34345 });
34346
34347 /**
34348  * @class Roo.bootstrap.panel.Nest
34349  * @extends Roo.bootstrap.panel.Content
34350  * @constructor
34351  * Create a new Panel, that can contain a layout.Border.
34352  * 
34353  * 
34354  * @param {Roo.BorderLayout} layout The layout for this panel
34355  * @param {String/Object} config A string to set only the title or a config object
34356  */
34357 Roo.bootstrap.panel.Nest = function(config)
34358 {
34359     // construct with only one argument..
34360     /* FIXME - implement nicer consturctors
34361     if (layout.layout) {
34362         config = layout;
34363         layout = config.layout;
34364         delete config.layout;
34365     }
34366     if (layout.xtype && !layout.getEl) {
34367         // then layout needs constructing..
34368         layout = Roo.factory(layout, Roo);
34369     }
34370     */
34371     
34372     config.el =  config.layout.getEl();
34373     
34374     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34375     
34376     config.layout.monitorWindowResize = false; // turn off autosizing
34377     this.layout = config.layout;
34378     this.layout.getEl().addClass("roo-layout-nested-layout");
34379     
34380     
34381     
34382     
34383 };
34384
34385 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34386
34387     setSize : function(width, height){
34388         if(!this.ignoreResize(width, height)){
34389             var size = this.adjustForComponents(width, height);
34390             var el = this.layout.getEl();
34391             el.setSize(size.width, size.height);
34392             var touch = el.dom.offsetWidth;
34393             this.layout.layout();
34394             // ie requires a double layout on the first pass
34395             if(Roo.isIE && !this.initialized){
34396                 this.initialized = true;
34397                 this.layout.layout();
34398             }
34399         }
34400     },
34401     
34402     // activate all subpanels if not currently active..
34403     
34404     setActiveState : function(active){
34405         this.active = active;
34406         if(!active){
34407             this.fireEvent("deactivate", this);
34408             return;
34409         }
34410         
34411         this.fireEvent("activate", this);
34412         // not sure if this should happen before or after..
34413         if (!this.layout) {
34414             return; // should not happen..
34415         }
34416         var reg = false;
34417         for (var r in this.layout.regions) {
34418             reg = this.layout.getRegion(r);
34419             if (reg.getActivePanel()) {
34420                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34421                 reg.setActivePanel(reg.getActivePanel());
34422                 continue;
34423             }
34424             if (!reg.panels.length) {
34425                 continue;
34426             }
34427             reg.showPanel(reg.getPanel(0));
34428         }
34429         
34430         
34431         
34432         
34433     },
34434     
34435     /**
34436      * Returns the nested BorderLayout for this panel
34437      * @return {Roo.BorderLayout} 
34438      */
34439     getLayout : function(){
34440         return this.layout;
34441     },
34442     
34443      /**
34444      * Adds a xtype elements to the layout of the nested panel
34445      * <pre><code>
34446
34447 panel.addxtype({
34448        xtype : 'ContentPanel',
34449        region: 'west',
34450        items: [ .... ]
34451    }
34452 );
34453
34454 panel.addxtype({
34455         xtype : 'NestedLayoutPanel',
34456         region: 'west',
34457         layout: {
34458            center: { },
34459            west: { }   
34460         },
34461         items : [ ... list of content panels or nested layout panels.. ]
34462    }
34463 );
34464 </code></pre>
34465      * @param {Object} cfg Xtype definition of item to add.
34466      */
34467     addxtype : function(cfg) {
34468         return this.layout.addxtype(cfg);
34469     
34470     }
34471 });        /*
34472  * Based on:
34473  * Ext JS Library 1.1.1
34474  * Copyright(c) 2006-2007, Ext JS, LLC.
34475  *
34476  * Originally Released Under LGPL - original licence link has changed is not relivant.
34477  *
34478  * Fork - LGPL
34479  * <script type="text/javascript">
34480  */
34481 /**
34482  * @class Roo.TabPanel
34483  * @extends Roo.util.Observable
34484  * A lightweight tab container.
34485  * <br><br>
34486  * Usage:
34487  * <pre><code>
34488 // basic tabs 1, built from existing content
34489 var tabs = new Roo.TabPanel("tabs1");
34490 tabs.addTab("script", "View Script");
34491 tabs.addTab("markup", "View Markup");
34492 tabs.activate("script");
34493
34494 // more advanced tabs, built from javascript
34495 var jtabs = new Roo.TabPanel("jtabs");
34496 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34497
34498 // set up the UpdateManager
34499 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34500 var updater = tab2.getUpdateManager();
34501 updater.setDefaultUrl("ajax1.htm");
34502 tab2.on('activate', updater.refresh, updater, true);
34503
34504 // Use setUrl for Ajax loading
34505 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34506 tab3.setUrl("ajax2.htm", null, true);
34507
34508 // Disabled tab
34509 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34510 tab4.disable();
34511
34512 jtabs.activate("jtabs-1");
34513  * </code></pre>
34514  * @constructor
34515  * Create a new TabPanel.
34516  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34517  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34518  */
34519 Roo.bootstrap.panel.Tabs = function(config){
34520     /**
34521     * The container element for this TabPanel.
34522     * @type Roo.Element
34523     */
34524     this.el = Roo.get(config.el);
34525     delete config.el;
34526     if(config){
34527         if(typeof config == "boolean"){
34528             this.tabPosition = config ? "bottom" : "top";
34529         }else{
34530             Roo.apply(this, config);
34531         }
34532     }
34533     
34534     if(this.tabPosition == "bottom"){
34535         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34536         this.el.addClass("roo-tabs-bottom");
34537     }
34538     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34539     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34540     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34541     if(Roo.isIE){
34542         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34543     }
34544     if(this.tabPosition != "bottom"){
34545         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34546          * @type Roo.Element
34547          */
34548         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34549         this.el.addClass("roo-tabs-top");
34550     }
34551     this.items = [];
34552
34553     this.bodyEl.setStyle("position", "relative");
34554
34555     this.active = null;
34556     this.activateDelegate = this.activate.createDelegate(this);
34557
34558     this.addEvents({
34559         /**
34560          * @event tabchange
34561          * Fires when the active tab changes
34562          * @param {Roo.TabPanel} this
34563          * @param {Roo.TabPanelItem} activePanel The new active tab
34564          */
34565         "tabchange": true,
34566         /**
34567          * @event beforetabchange
34568          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34569          * @param {Roo.TabPanel} this
34570          * @param {Object} e Set cancel to true on this object to cancel the tab change
34571          * @param {Roo.TabPanelItem} tab The tab being changed to
34572          */
34573         "beforetabchange" : true
34574     });
34575
34576     Roo.EventManager.onWindowResize(this.onResize, this);
34577     this.cpad = this.el.getPadding("lr");
34578     this.hiddenCount = 0;
34579
34580
34581     // toolbar on the tabbar support...
34582     if (this.toolbar) {
34583         alert("no toolbar support yet");
34584         this.toolbar  = false;
34585         /*
34586         var tcfg = this.toolbar;
34587         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34588         this.toolbar = new Roo.Toolbar(tcfg);
34589         if (Roo.isSafari) {
34590             var tbl = tcfg.container.child('table', true);
34591             tbl.setAttribute('width', '100%');
34592         }
34593         */
34594         
34595     }
34596    
34597
34598
34599     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34600 };
34601
34602 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34603     /*
34604      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34605      */
34606     tabPosition : "top",
34607     /*
34608      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34609      */
34610     currentTabWidth : 0,
34611     /*
34612      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34613      */
34614     minTabWidth : 40,
34615     /*
34616      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34617      */
34618     maxTabWidth : 250,
34619     /*
34620      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34621      */
34622     preferredTabWidth : 175,
34623     /*
34624      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34625      */
34626     resizeTabs : false,
34627     /*
34628      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34629      */
34630     monitorResize : true,
34631     /*
34632      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34633      */
34634     toolbar : false,
34635
34636     /**
34637      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34638      * @param {String} id The id of the div to use <b>or create</b>
34639      * @param {String} text The text for the tab
34640      * @param {String} content (optional) Content to put in the TabPanelItem body
34641      * @param {Boolean} closable (optional) True to create a close icon on the tab
34642      * @return {Roo.TabPanelItem} The created TabPanelItem
34643      */
34644     addTab : function(id, text, content, closable)
34645     {
34646         var item = new Roo.bootstrap.panel.TabItem({
34647             panel: this,
34648             id : id,
34649             text : text,
34650             closable : closable
34651         });
34652         this.addTabItem(item);
34653         if(content){
34654             item.setContent(content);
34655         }
34656         return item;
34657     },
34658
34659     /**
34660      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34661      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34662      * @return {Roo.TabPanelItem}
34663      */
34664     getTab : function(id){
34665         return this.items[id];
34666     },
34667
34668     /**
34669      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34670      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34671      */
34672     hideTab : function(id){
34673         var t = this.items[id];
34674         if(!t.isHidden()){
34675            t.setHidden(true);
34676            this.hiddenCount++;
34677            this.autoSizeTabs();
34678         }
34679     },
34680
34681     /**
34682      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34683      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34684      */
34685     unhideTab : function(id){
34686         var t = this.items[id];
34687         if(t.isHidden()){
34688            t.setHidden(false);
34689            this.hiddenCount--;
34690            this.autoSizeTabs();
34691         }
34692     },
34693
34694     /**
34695      * Adds an existing {@link Roo.TabPanelItem}.
34696      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34697      */
34698     addTabItem : function(item){
34699         this.items[item.id] = item;
34700         this.items.push(item);
34701       //  if(this.resizeTabs){
34702     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34703   //         this.autoSizeTabs();
34704 //        }else{
34705 //            item.autoSize();
34706        // }
34707     },
34708
34709     /**
34710      * Removes a {@link Roo.TabPanelItem}.
34711      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34712      */
34713     removeTab : function(id){
34714         var items = this.items;
34715         var tab = items[id];
34716         if(!tab) { return; }
34717         var index = items.indexOf(tab);
34718         if(this.active == tab && items.length > 1){
34719             var newTab = this.getNextAvailable(index);
34720             if(newTab) {
34721                 newTab.activate();
34722             }
34723         }
34724         this.stripEl.dom.removeChild(tab.pnode.dom);
34725         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34726             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34727         }
34728         items.splice(index, 1);
34729         delete this.items[tab.id];
34730         tab.fireEvent("close", tab);
34731         tab.purgeListeners();
34732         this.autoSizeTabs();
34733     },
34734
34735     getNextAvailable : function(start){
34736         var items = this.items;
34737         var index = start;
34738         // look for a next tab that will slide over to
34739         // replace the one being removed
34740         while(index < items.length){
34741             var item = items[++index];
34742             if(item && !item.isHidden()){
34743                 return item;
34744             }
34745         }
34746         // if one isn't found select the previous tab (on the left)
34747         index = start;
34748         while(index >= 0){
34749             var item = items[--index];
34750             if(item && !item.isHidden()){
34751                 return item;
34752             }
34753         }
34754         return null;
34755     },
34756
34757     /**
34758      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34759      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34760      */
34761     disableTab : function(id){
34762         var tab = this.items[id];
34763         if(tab && this.active != tab){
34764             tab.disable();
34765         }
34766     },
34767
34768     /**
34769      * Enables a {@link Roo.TabPanelItem} that is disabled.
34770      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34771      */
34772     enableTab : function(id){
34773         var tab = this.items[id];
34774         tab.enable();
34775     },
34776
34777     /**
34778      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34779      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34780      * @return {Roo.TabPanelItem} The TabPanelItem.
34781      */
34782     activate : function(id){
34783         var tab = this.items[id];
34784         if(!tab){
34785             return null;
34786         }
34787         if(tab == this.active || tab.disabled){
34788             return tab;
34789         }
34790         var e = {};
34791         this.fireEvent("beforetabchange", this, e, tab);
34792         if(e.cancel !== true && !tab.disabled){
34793             if(this.active){
34794                 this.active.hide();
34795             }
34796             this.active = this.items[id];
34797             this.active.show();
34798             this.fireEvent("tabchange", this, this.active);
34799         }
34800         return tab;
34801     },
34802
34803     /**
34804      * Gets the active {@link Roo.TabPanelItem}.
34805      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34806      */
34807     getActiveTab : function(){
34808         return this.active;
34809     },
34810
34811     /**
34812      * Updates the tab body element to fit the height of the container element
34813      * for overflow scrolling
34814      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34815      */
34816     syncHeight : function(targetHeight){
34817         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34818         var bm = this.bodyEl.getMargins();
34819         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34820         this.bodyEl.setHeight(newHeight);
34821         return newHeight;
34822     },
34823
34824     onResize : function(){
34825         if(this.monitorResize){
34826             this.autoSizeTabs();
34827         }
34828     },
34829
34830     /**
34831      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34832      */
34833     beginUpdate : function(){
34834         this.updating = true;
34835     },
34836
34837     /**
34838      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34839      */
34840     endUpdate : function(){
34841         this.updating = false;
34842         this.autoSizeTabs();
34843     },
34844
34845     /**
34846      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34847      */
34848     autoSizeTabs : function(){
34849         var count = this.items.length;
34850         var vcount = count - this.hiddenCount;
34851         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34852             return;
34853         }
34854         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34855         var availWidth = Math.floor(w / vcount);
34856         var b = this.stripBody;
34857         if(b.getWidth() > w){
34858             var tabs = this.items;
34859             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34860             if(availWidth < this.minTabWidth){
34861                 /*if(!this.sleft){    // incomplete scrolling code
34862                     this.createScrollButtons();
34863                 }
34864                 this.showScroll();
34865                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34866             }
34867         }else{
34868             if(this.currentTabWidth < this.preferredTabWidth){
34869                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34870             }
34871         }
34872     },
34873
34874     /**
34875      * Returns the number of tabs in this TabPanel.
34876      * @return {Number}
34877      */
34878      getCount : function(){
34879          return this.items.length;
34880      },
34881
34882     /**
34883      * Resizes all the tabs to the passed width
34884      * @param {Number} The new width
34885      */
34886     setTabWidth : function(width){
34887         this.currentTabWidth = width;
34888         for(var i = 0, len = this.items.length; i < len; i++) {
34889                 if(!this.items[i].isHidden()) {
34890                 this.items[i].setWidth(width);
34891             }
34892         }
34893     },
34894
34895     /**
34896      * Destroys this TabPanel
34897      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34898      */
34899     destroy : function(removeEl){
34900         Roo.EventManager.removeResizeListener(this.onResize, this);
34901         for(var i = 0, len = this.items.length; i < len; i++){
34902             this.items[i].purgeListeners();
34903         }
34904         if(removeEl === true){
34905             this.el.update("");
34906             this.el.remove();
34907         }
34908     },
34909     
34910     createStrip : function(container)
34911     {
34912         var strip = document.createElement("nav");
34913         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34914         container.appendChild(strip);
34915         return strip;
34916     },
34917     
34918     createStripList : function(strip)
34919     {
34920         // div wrapper for retard IE
34921         // returns the "tr" element.
34922         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34923         //'<div class="x-tabs-strip-wrap">'+
34924           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34925           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34926         return strip.firstChild; //.firstChild.firstChild.firstChild;
34927     },
34928     createBody : function(container)
34929     {
34930         var body = document.createElement("div");
34931         Roo.id(body, "tab-body");
34932         //Roo.fly(body).addClass("x-tabs-body");
34933         Roo.fly(body).addClass("tab-content");
34934         container.appendChild(body);
34935         return body;
34936     },
34937     createItemBody :function(bodyEl, id){
34938         var body = Roo.getDom(id);
34939         if(!body){
34940             body = document.createElement("div");
34941             body.id = id;
34942         }
34943         //Roo.fly(body).addClass("x-tabs-item-body");
34944         Roo.fly(body).addClass("tab-pane");
34945          bodyEl.insertBefore(body, bodyEl.firstChild);
34946         return body;
34947     },
34948     /** @private */
34949     createStripElements :  function(stripEl, text, closable)
34950     {
34951         var td = document.createElement("li"); // was td..
34952         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34953         //stripEl.appendChild(td);
34954         /*if(closable){
34955             td.className = "x-tabs-closable";
34956             if(!this.closeTpl){
34957                 this.closeTpl = new Roo.Template(
34958                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34959                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34960                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34961                 );
34962             }
34963             var el = this.closeTpl.overwrite(td, {"text": text});
34964             var close = el.getElementsByTagName("div")[0];
34965             var inner = el.getElementsByTagName("em")[0];
34966             return {"el": el, "close": close, "inner": inner};
34967         } else {
34968         */
34969         // not sure what this is..
34970             if(!this.tabTpl){
34971                 //this.tabTpl = new Roo.Template(
34972                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34973                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34974                 //);
34975                 this.tabTpl = new Roo.Template(
34976                    '<a href="#">' +
34977                    '<span unselectable="on"' +
34978                             (this.disableTooltips ? '' : ' title="{text}"') +
34979                             ' >{text}</span></span></a>'
34980                 );
34981                 
34982             }
34983             var el = this.tabTpl.overwrite(td, {"text": text});
34984             var inner = el.getElementsByTagName("span")[0];
34985             return {"el": el, "inner": inner};
34986         //}
34987     }
34988         
34989     
34990 });
34991
34992 /**
34993  * @class Roo.TabPanelItem
34994  * @extends Roo.util.Observable
34995  * Represents an individual item (tab plus body) in a TabPanel.
34996  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34997  * @param {String} id The id of this TabPanelItem
34998  * @param {String} text The text for the tab of this TabPanelItem
34999  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35000  */
35001 Roo.bootstrap.panel.TabItem = function(config){
35002     /**
35003      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35004      * @type Roo.TabPanel
35005      */
35006     this.tabPanel = config.panel;
35007     /**
35008      * The id for this TabPanelItem
35009      * @type String
35010      */
35011     this.id = config.id;
35012     /** @private */
35013     this.disabled = false;
35014     /** @private */
35015     this.text = config.text;
35016     /** @private */
35017     this.loaded = false;
35018     this.closable = config.closable;
35019
35020     /**
35021      * The body element for this TabPanelItem.
35022      * @type Roo.Element
35023      */
35024     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35025     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35026     this.bodyEl.setStyle("display", "block");
35027     this.bodyEl.setStyle("zoom", "1");
35028     //this.hideAction();
35029
35030     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35031     /** @private */
35032     this.el = Roo.get(els.el);
35033     this.inner = Roo.get(els.inner, true);
35034     this.textEl = Roo.get(this.el.dom.firstChild, true);
35035     this.pnode = Roo.get(els.el.parentNode, true);
35036     this.el.on("mousedown", this.onTabMouseDown, this);
35037     this.el.on("click", this.onTabClick, this);
35038     /** @private */
35039     if(config.closable){
35040         var c = Roo.get(els.close, true);
35041         c.dom.title = this.closeText;
35042         c.addClassOnOver("close-over");
35043         c.on("click", this.closeClick, this);
35044      }
35045
35046     this.addEvents({
35047          /**
35048          * @event activate
35049          * Fires when this tab becomes the active tab.
35050          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35051          * @param {Roo.TabPanelItem} this
35052          */
35053         "activate": true,
35054         /**
35055          * @event beforeclose
35056          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35057          * @param {Roo.TabPanelItem} this
35058          * @param {Object} e Set cancel to true on this object to cancel the close.
35059          */
35060         "beforeclose": true,
35061         /**
35062          * @event close
35063          * Fires when this tab is closed.
35064          * @param {Roo.TabPanelItem} this
35065          */
35066          "close": true,
35067         /**
35068          * @event deactivate
35069          * Fires when this tab is no longer the active tab.
35070          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35071          * @param {Roo.TabPanelItem} this
35072          */
35073          "deactivate" : true
35074     });
35075     this.hidden = false;
35076
35077     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35078 };
35079
35080 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35081            {
35082     purgeListeners : function(){
35083        Roo.util.Observable.prototype.purgeListeners.call(this);
35084        this.el.removeAllListeners();
35085     },
35086     /**
35087      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35088      */
35089     show : function(){
35090         this.pnode.addClass("active");
35091         this.showAction();
35092         if(Roo.isOpera){
35093             this.tabPanel.stripWrap.repaint();
35094         }
35095         this.fireEvent("activate", this.tabPanel, this);
35096     },
35097
35098     /**
35099      * Returns true if this tab is the active tab.
35100      * @return {Boolean}
35101      */
35102     isActive : function(){
35103         return this.tabPanel.getActiveTab() == this;
35104     },
35105
35106     /**
35107      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35108      */
35109     hide : function(){
35110         this.pnode.removeClass("active");
35111         this.hideAction();
35112         this.fireEvent("deactivate", this.tabPanel, this);
35113     },
35114
35115     hideAction : function(){
35116         this.bodyEl.hide();
35117         this.bodyEl.setStyle("position", "absolute");
35118         this.bodyEl.setLeft("-20000px");
35119         this.bodyEl.setTop("-20000px");
35120     },
35121
35122     showAction : function(){
35123         this.bodyEl.setStyle("position", "relative");
35124         this.bodyEl.setTop("");
35125         this.bodyEl.setLeft("");
35126         this.bodyEl.show();
35127     },
35128
35129     /**
35130      * Set the tooltip for the tab.
35131      * @param {String} tooltip The tab's tooltip
35132      */
35133     setTooltip : function(text){
35134         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35135             this.textEl.dom.qtip = text;
35136             this.textEl.dom.removeAttribute('title');
35137         }else{
35138             this.textEl.dom.title = text;
35139         }
35140     },
35141
35142     onTabClick : function(e){
35143         e.preventDefault();
35144         this.tabPanel.activate(this.id);
35145     },
35146
35147     onTabMouseDown : function(e){
35148         e.preventDefault();
35149         this.tabPanel.activate(this.id);
35150     },
35151 /*
35152     getWidth : function(){
35153         return this.inner.getWidth();
35154     },
35155
35156     setWidth : function(width){
35157         var iwidth = width - this.pnode.getPadding("lr");
35158         this.inner.setWidth(iwidth);
35159         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35160         this.pnode.setWidth(width);
35161     },
35162 */
35163     /**
35164      * Show or hide the tab
35165      * @param {Boolean} hidden True to hide or false to show.
35166      */
35167     setHidden : function(hidden){
35168         this.hidden = hidden;
35169         this.pnode.setStyle("display", hidden ? "none" : "");
35170     },
35171
35172     /**
35173      * Returns true if this tab is "hidden"
35174      * @return {Boolean}
35175      */
35176     isHidden : function(){
35177         return this.hidden;
35178     },
35179
35180     /**
35181      * Returns the text for this tab
35182      * @return {String}
35183      */
35184     getText : function(){
35185         return this.text;
35186     },
35187     /*
35188     autoSize : function(){
35189         //this.el.beginMeasure();
35190         this.textEl.setWidth(1);
35191         /*
35192          *  #2804 [new] Tabs in Roojs
35193          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35194          */
35195         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35196         //this.el.endMeasure();
35197     //},
35198
35199     /**
35200      * Sets the text for the tab (Note: this also sets the tooltip text)
35201      * @param {String} text The tab's text and tooltip
35202      */
35203     setText : function(text){
35204         this.text = text;
35205         this.textEl.update(text);
35206         this.setTooltip(text);
35207         //if(!this.tabPanel.resizeTabs){
35208         //    this.autoSize();
35209         //}
35210     },
35211     /**
35212      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35213      */
35214     activate : function(){
35215         this.tabPanel.activate(this.id);
35216     },
35217
35218     /**
35219      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35220      */
35221     disable : function(){
35222         if(this.tabPanel.active != this){
35223             this.disabled = true;
35224             this.pnode.addClass("disabled");
35225         }
35226     },
35227
35228     /**
35229      * Enables this TabPanelItem if it was previously disabled.
35230      */
35231     enable : function(){
35232         this.disabled = false;
35233         this.pnode.removeClass("disabled");
35234     },
35235
35236     /**
35237      * Sets the content for this TabPanelItem.
35238      * @param {String} content The content
35239      * @param {Boolean} loadScripts true to look for and load scripts
35240      */
35241     setContent : function(content, loadScripts){
35242         this.bodyEl.update(content, loadScripts);
35243     },
35244
35245     /**
35246      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35247      * @return {Roo.UpdateManager} The UpdateManager
35248      */
35249     getUpdateManager : function(){
35250         return this.bodyEl.getUpdateManager();
35251     },
35252
35253     /**
35254      * Set a URL to be used to load the content for this TabPanelItem.
35255      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35256      * @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)
35257      * @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)
35258      * @return {Roo.UpdateManager} The UpdateManager
35259      */
35260     setUrl : function(url, params, loadOnce){
35261         if(this.refreshDelegate){
35262             this.un('activate', this.refreshDelegate);
35263         }
35264         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35265         this.on("activate", this.refreshDelegate);
35266         return this.bodyEl.getUpdateManager();
35267     },
35268
35269     /** @private */
35270     _handleRefresh : function(url, params, loadOnce){
35271         if(!loadOnce || !this.loaded){
35272             var updater = this.bodyEl.getUpdateManager();
35273             updater.update(url, params, this._setLoaded.createDelegate(this));
35274         }
35275     },
35276
35277     /**
35278      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35279      *   Will fail silently if the setUrl method has not been called.
35280      *   This does not activate the panel, just updates its content.
35281      */
35282     refresh : function(){
35283         if(this.refreshDelegate){
35284            this.loaded = false;
35285            this.refreshDelegate();
35286         }
35287     },
35288
35289     /** @private */
35290     _setLoaded : function(){
35291         this.loaded = true;
35292     },
35293
35294     /** @private */
35295     closeClick : function(e){
35296         var o = {};
35297         e.stopEvent();
35298         this.fireEvent("beforeclose", this, o);
35299         if(o.cancel !== true){
35300             this.tabPanel.removeTab(this.id);
35301         }
35302     },
35303     /**
35304      * The text displayed in the tooltip for the close icon.
35305      * @type String
35306      */
35307     closeText : "Close this tab"
35308 });