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 false
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) - 60;
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         
2744         this.resize();
2745         
2746         (function () {
2747             this.items.forEach( function(e) {
2748                 e.layout ? e.layout() : false;
2749                     
2750             });
2751         }).defer(100,this);
2752         
2753     },
2754     hide : function()
2755     {
2756         this.maskEl.hide();
2757         Roo.get(document.body).removeClass("x-body-masked");
2758         this.el.removeClass('in');
2759         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2760         
2761         if(this.animate){ // why
2762             var _this = this;
2763             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2764         }else{
2765             this.el.setStyle('display', 'none');
2766         }
2767         
2768         this.fireEvent('hide', this);
2769     },
2770     
2771     addButton : function(str, cb)
2772     {
2773          
2774         
2775         var b = Roo.apply({}, { html : str } );
2776         b.xns = b.xns || Roo.bootstrap;
2777         b.xtype = b.xtype || 'Button';
2778         if (typeof(b.listeners) == 'undefined') {
2779             b.listeners = { click : cb.createDelegate(this)  };
2780         }
2781         
2782         var btn = Roo.factory(b);
2783            
2784         btn.render(this.el.select('.modal-footer div').first());
2785         
2786         return btn;   
2787        
2788     },
2789     
2790     setDefaultButton : function(btn)
2791     {
2792         //this.el.select('.modal-footer').()
2793     },
2794     diff : false,
2795     
2796     resizeTo: function(w,h)
2797     {
2798         // skip.. ?? why??
2799         
2800         this.dialogEl.setWidth(w);
2801         if (this.diff === false) {
2802             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2803         }
2804         
2805         this.bodyEl.setHeight(h-this.diff);
2806         
2807         
2808     },
2809     setContentSize  : function(w, h)
2810     {
2811         
2812     },
2813     onButtonClick: function(btn,e)
2814     {
2815         //Roo.log([a,b,c]);
2816         this.fireEvent('btnclick', btn.name, e);
2817     },
2818      /**
2819      * Set the title of the Dialog
2820      * @param {String} str new Title
2821      */
2822     setTitle: function(str) {
2823         this.titleEl.dom.innerHTML = str;    
2824     },
2825     /**
2826      * Set the body of the Dialog
2827      * @param {String} str new Title
2828      */
2829     setBody: function(str) {
2830         this.bodyEl.dom.innerHTML = str;    
2831     },
2832     /**
2833      * Set the body of the Dialog using the template
2834      * @param {Obj} data - apply this data to the template and replace the body contents.
2835      */
2836     applyBody: function(obj)
2837     {
2838         if (!this.tmpl) {
2839             Roo.log("Error - using apply Body without a template");
2840             //code
2841         }
2842         this.tmpl.overwrite(this.bodyEl, obj);
2843     }
2844     
2845 });
2846
2847
2848 Roo.apply(Roo.bootstrap.Modal,  {
2849     /**
2850          * Button config that displays a single OK button
2851          * @type Object
2852          */
2853         OK :  [{
2854             name : 'ok',
2855             weight : 'primary',
2856             html : 'OK'
2857         }], 
2858         /**
2859          * Button config that displays Yes and No buttons
2860          * @type Object
2861          */
2862         YESNO : [
2863             {
2864                 name  : 'no',
2865                 html : 'No'
2866             },
2867             {
2868                 name  :'yes',
2869                 weight : 'primary',
2870                 html : 'Yes'
2871             }
2872         ],
2873         
2874         /**
2875          * Button config that displays OK and Cancel buttons
2876          * @type Object
2877          */
2878         OKCANCEL : [
2879             {
2880                name : 'cancel',
2881                 html : 'Cancel'
2882             },
2883             {
2884                 name : 'ok',
2885                 weight : 'primary',
2886                 html : 'OK'
2887             }
2888         ],
2889         /**
2890          * Button config that displays Yes, No and Cancel buttons
2891          * @type Object
2892          */
2893         YESNOCANCEL : [
2894             {
2895                 name : 'yes',
2896                 weight : 'primary',
2897                 html : 'Yes'
2898             },
2899             {
2900                 name : 'no',
2901                 html : 'No'
2902             },
2903             {
2904                 name : 'cancel',
2905                 html : 'Cancel'
2906             }
2907         ]
2908 });
2909  
2910  /*
2911  * - LGPL
2912  *
2913  * messagebox - can be used as a replace
2914  * 
2915  */
2916 /**
2917  * @class Roo.MessageBox
2918  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2919  * Example usage:
2920  *<pre><code>
2921 // Basic alert:
2922 Roo.Msg.alert('Status', 'Changes saved successfully.');
2923
2924 // Prompt for user data:
2925 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2926     if (btn == 'ok'){
2927         // process text value...
2928     }
2929 });
2930
2931 // Show a dialog using config options:
2932 Roo.Msg.show({
2933    title:'Save Changes?',
2934    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2935    buttons: Roo.Msg.YESNOCANCEL,
2936    fn: processResult,
2937    animEl: 'elId'
2938 });
2939 </code></pre>
2940  * @singleton
2941  */
2942 Roo.bootstrap.MessageBox = function(){
2943     var dlg, opt, mask, waitTimer;
2944     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2945     var buttons, activeTextEl, bwidth;
2946
2947     
2948     // private
2949     var handleButton = function(button){
2950         dlg.hide();
2951         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2952     };
2953
2954     // private
2955     var handleHide = function(){
2956         if(opt && opt.cls){
2957             dlg.el.removeClass(opt.cls);
2958         }
2959         //if(waitTimer){
2960         //    Roo.TaskMgr.stop(waitTimer);
2961         //    waitTimer = null;
2962         //}
2963     };
2964
2965     // private
2966     var updateButtons = function(b){
2967         var width = 0;
2968         if(!b){
2969             buttons["ok"].hide();
2970             buttons["cancel"].hide();
2971             buttons["yes"].hide();
2972             buttons["no"].hide();
2973             //dlg.footer.dom.style.display = 'none';
2974             return width;
2975         }
2976         dlg.footerEl.dom.style.display = '';
2977         for(var k in buttons){
2978             if(typeof buttons[k] != "function"){
2979                 if(b[k]){
2980                     buttons[k].show();
2981                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2982                     width += buttons[k].el.getWidth()+15;
2983                 }else{
2984                     buttons[k].hide();
2985                 }
2986             }
2987         }
2988         return width;
2989     };
2990
2991     // private
2992     var handleEsc = function(d, k, e){
2993         if(opt && opt.closable !== false){
2994             dlg.hide();
2995         }
2996         if(e){
2997             e.stopEvent();
2998         }
2999     };
3000
3001     return {
3002         /**
3003          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3004          * @return {Roo.BasicDialog} The BasicDialog element
3005          */
3006         getDialog : function(){
3007            if(!dlg){
3008                 dlg = new Roo.bootstrap.Modal( {
3009                     //draggable: true,
3010                     //resizable:false,
3011                     //constraintoviewport:false,
3012                     //fixedcenter:true,
3013                     //collapsible : false,
3014                     //shim:true,
3015                     //modal: true,
3016                   //  width:400,
3017                   //  height:100,
3018                     //buttonAlign:"center",
3019                     closeClick : function(){
3020                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3021                             handleButton("no");
3022                         }else{
3023                             handleButton("cancel");
3024                         }
3025                     }
3026                 });
3027                 dlg.render();
3028                 dlg.on("hide", handleHide);
3029                 mask = dlg.mask;
3030                 //dlg.addKeyListener(27, handleEsc);
3031                 buttons = {};
3032                 this.buttons = buttons;
3033                 var bt = this.buttonText;
3034                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3035                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3036                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3037                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3038                 //Roo.log(buttons);
3039                 bodyEl = dlg.bodyEl.createChild({
3040
3041                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3042                         '<textarea class="roo-mb-textarea"></textarea>' +
3043                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3044                 });
3045                 msgEl = bodyEl.dom.firstChild;
3046                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3047                 textboxEl.enableDisplayMode();
3048                 textboxEl.addKeyListener([10,13], function(){
3049                     if(dlg.isVisible() && opt && opt.buttons){
3050                         if(opt.buttons.ok){
3051                             handleButton("ok");
3052                         }else if(opt.buttons.yes){
3053                             handleButton("yes");
3054                         }
3055                     }
3056                 });
3057                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3058                 textareaEl.enableDisplayMode();
3059                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3060                 progressEl.enableDisplayMode();
3061                 var pf = progressEl.dom.firstChild;
3062                 if (pf) {
3063                     pp = Roo.get(pf.firstChild);
3064                     pp.setHeight(pf.offsetHeight);
3065                 }
3066                 
3067             }
3068             return dlg;
3069         },
3070
3071         /**
3072          * Updates the message box body text
3073          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3074          * the XHTML-compliant non-breaking space character '&amp;#160;')
3075          * @return {Roo.MessageBox} This message box
3076          */
3077         updateText : function(text){
3078             if(!dlg.isVisible() && !opt.width){
3079                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3080             }
3081             msgEl.innerHTML = text || '&#160;';
3082       
3083             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3084             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3085             var w = Math.max(
3086                     Math.min(opt.width || cw , this.maxWidth), 
3087                     Math.max(opt.minWidth || this.minWidth, bwidth)
3088             );
3089             if(opt.prompt){
3090                 activeTextEl.setWidth(w);
3091             }
3092             if(dlg.isVisible()){
3093                 dlg.fixedcenter = false;
3094             }
3095             // to big, make it scroll. = But as usual stupid IE does not support
3096             // !important..
3097             
3098             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3099                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3100                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3101             } else {
3102                 bodyEl.dom.style.height = '';
3103                 bodyEl.dom.style.overflowY = '';
3104             }
3105             if (cw > w) {
3106                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3107             } else {
3108                 bodyEl.dom.style.overflowX = '';
3109             }
3110             
3111             dlg.setContentSize(w, bodyEl.getHeight());
3112             if(dlg.isVisible()){
3113                 dlg.fixedcenter = true;
3114             }
3115             return this;
3116         },
3117
3118         /**
3119          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3120          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3121          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3122          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3123          * @return {Roo.MessageBox} This message box
3124          */
3125         updateProgress : function(value, text){
3126             if(text){
3127                 this.updateText(text);
3128             }
3129             if (pp) { // weird bug on my firefox - for some reason this is not defined
3130                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3131             }
3132             return this;
3133         },        
3134
3135         /**
3136          * Returns true if the message box is currently displayed
3137          * @return {Boolean} True if the message box is visible, else false
3138          */
3139         isVisible : function(){
3140             return dlg && dlg.isVisible();  
3141         },
3142
3143         /**
3144          * Hides the message box if it is displayed
3145          */
3146         hide : function(){
3147             if(this.isVisible()){
3148                 dlg.hide();
3149             }  
3150         },
3151
3152         /**
3153          * Displays a new message box, or reinitializes an existing message box, based on the config options
3154          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3155          * The following config object properties are supported:
3156          * <pre>
3157 Property    Type             Description
3158 ----------  ---------------  ------------------------------------------------------------------------------------
3159 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3160                                    closes (defaults to undefined)
3161 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3162                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3163 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3164                                    progress and wait dialogs will ignore this property and always hide the
3165                                    close button as they can only be closed programmatically.
3166 cls               String           A custom CSS class to apply to the message box element
3167 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3168                                    displayed (defaults to 75)
3169 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3170                                    function will be btn (the name of the button that was clicked, if applicable,
3171                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3172                                    Progress and wait dialogs will ignore this option since they do not respond to
3173                                    user actions and can only be closed programmatically, so any required function
3174                                    should be called by the same code after it closes the dialog.
3175 icon              String           A CSS class that provides a background image to be used as an icon for
3176                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3177 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3178 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3179 modal             Boolean          False to allow user interaction with the page while the message box is
3180                                    displayed (defaults to true)
3181 msg               String           A string that will replace the existing message box body text (defaults
3182                                    to the XHTML-compliant non-breaking space character '&#160;')
3183 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3184 progress          Boolean          True to display a progress bar (defaults to false)
3185 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3186 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3187 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3188 title             String           The title text
3189 value             String           The string value to set into the active textbox element if displayed
3190 wait              Boolean          True to display a progress bar (defaults to false)
3191 width             Number           The width of the dialog in pixels
3192 </pre>
3193          *
3194          * Example usage:
3195          * <pre><code>
3196 Roo.Msg.show({
3197    title: 'Address',
3198    msg: 'Please enter your address:',
3199    width: 300,
3200    buttons: Roo.MessageBox.OKCANCEL,
3201    multiline: true,
3202    fn: saveAddress,
3203    animEl: 'addAddressBtn'
3204 });
3205 </code></pre>
3206          * @param {Object} config Configuration options
3207          * @return {Roo.MessageBox} This message box
3208          */
3209         show : function(options)
3210         {
3211             
3212             // this causes nightmares if you show one dialog after another
3213             // especially on callbacks..
3214              
3215             if(this.isVisible()){
3216                 
3217                 this.hide();
3218                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3219                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3220                 Roo.log("New Dialog Message:" +  options.msg )
3221                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3222                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3223                 
3224             }
3225             var d = this.getDialog();
3226             opt = options;
3227             d.setTitle(opt.title || "&#160;");
3228             d.closeEl.setDisplayed(opt.closable !== false);
3229             activeTextEl = textboxEl;
3230             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3231             if(opt.prompt){
3232                 if(opt.multiline){
3233                     textboxEl.hide();
3234                     textareaEl.show();
3235                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3236                         opt.multiline : this.defaultTextHeight);
3237                     activeTextEl = textareaEl;
3238                 }else{
3239                     textboxEl.show();
3240                     textareaEl.hide();
3241                 }
3242             }else{
3243                 textboxEl.hide();
3244                 textareaEl.hide();
3245             }
3246             progressEl.setDisplayed(opt.progress === true);
3247             this.updateProgress(0);
3248             activeTextEl.dom.value = opt.value || "";
3249             if(opt.prompt){
3250                 dlg.setDefaultButton(activeTextEl);
3251             }else{
3252                 var bs = opt.buttons;
3253                 var db = null;
3254                 if(bs && bs.ok){
3255                     db = buttons["ok"];
3256                 }else if(bs && bs.yes){
3257                     db = buttons["yes"];
3258                 }
3259                 dlg.setDefaultButton(db);
3260             }
3261             bwidth = updateButtons(opt.buttons);
3262             this.updateText(opt.msg);
3263             if(opt.cls){
3264                 d.el.addClass(opt.cls);
3265             }
3266             d.proxyDrag = opt.proxyDrag === true;
3267             d.modal = opt.modal !== false;
3268             d.mask = opt.modal !== false ? mask : false;
3269             if(!d.isVisible()){
3270                 // force it to the end of the z-index stack so it gets a cursor in FF
3271                 document.body.appendChild(dlg.el.dom);
3272                 d.animateTarget = null;
3273                 d.show(options.animEl);
3274             }
3275             return this;
3276         },
3277
3278         /**
3279          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3280          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3281          * and closing the message box when the process is complete.
3282          * @param {String} title The title bar text
3283          * @param {String} msg The message box body text
3284          * @return {Roo.MessageBox} This message box
3285          */
3286         progress : function(title, msg){
3287             this.show({
3288                 title : title,
3289                 msg : msg,
3290                 buttons: false,
3291                 progress:true,
3292                 closable:false,
3293                 minWidth: this.minProgressWidth,
3294                 modal : true
3295             });
3296             return this;
3297         },
3298
3299         /**
3300          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3301          * If a callback function is passed it will be called after the user clicks the button, and the
3302          * id of the button that was clicked will be passed as the only parameter to the callback
3303          * (could also be the top-right close button).
3304          * @param {String} title The title bar text
3305          * @param {String} msg The message box body text
3306          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3307          * @param {Object} scope (optional) The scope of the callback function
3308          * @return {Roo.MessageBox} This message box
3309          */
3310         alert : function(title, msg, fn, scope){
3311             this.show({
3312                 title : title,
3313                 msg : msg,
3314                 buttons: this.OK,
3315                 fn: fn,
3316                 scope : scope,
3317                 modal : true
3318             });
3319             return this;
3320         },
3321
3322         /**
3323          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3324          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3325          * You are responsible for closing the message box when the process is complete.
3326          * @param {String} msg The message box body text
3327          * @param {String} title (optional) The title bar text
3328          * @return {Roo.MessageBox} This message box
3329          */
3330         wait : function(msg, title){
3331             this.show({
3332                 title : title,
3333                 msg : msg,
3334                 buttons: false,
3335                 closable:false,
3336                 progress:true,
3337                 modal:true,
3338                 width:300,
3339                 wait:true
3340             });
3341             waitTimer = Roo.TaskMgr.start({
3342                 run: function(i){
3343                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3344                 },
3345                 interval: 1000
3346             });
3347             return this;
3348         },
3349
3350         /**
3351          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3352          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3353          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3354          * @param {String} title The title bar text
3355          * @param {String} msg The message box body text
3356          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3357          * @param {Object} scope (optional) The scope of the callback function
3358          * @return {Roo.MessageBox} This message box
3359          */
3360         confirm : function(title, msg, fn, scope){
3361             this.show({
3362                 title : title,
3363                 msg : msg,
3364                 buttons: this.YESNO,
3365                 fn: fn,
3366                 scope : scope,
3367                 modal : true
3368             });
3369             return this;
3370         },
3371
3372         /**
3373          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3374          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3375          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3376          * (could also be the top-right close button) and the text that was entered will be passed as the two
3377          * parameters to the callback.
3378          * @param {String} title The title bar text
3379          * @param {String} msg The message box body text
3380          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3381          * @param {Object} scope (optional) The scope of the callback function
3382          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3383          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3384          * @return {Roo.MessageBox} This message box
3385          */
3386         prompt : function(title, msg, fn, scope, multiline){
3387             this.show({
3388                 title : title,
3389                 msg : msg,
3390                 buttons: this.OKCANCEL,
3391                 fn: fn,
3392                 minWidth:250,
3393                 scope : scope,
3394                 prompt:true,
3395                 multiline: multiline,
3396                 modal : true
3397             });
3398             return this;
3399         },
3400
3401         /**
3402          * Button config that displays a single OK button
3403          * @type Object
3404          */
3405         OK : {ok:true},
3406         /**
3407          * Button config that displays Yes and No buttons
3408          * @type Object
3409          */
3410         YESNO : {yes:true, no:true},
3411         /**
3412          * Button config that displays OK and Cancel buttons
3413          * @type Object
3414          */
3415         OKCANCEL : {ok:true, cancel:true},
3416         /**
3417          * Button config that displays Yes, No and Cancel buttons
3418          * @type Object
3419          */
3420         YESNOCANCEL : {yes:true, no:true, cancel:true},
3421
3422         /**
3423          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3424          * @type Number
3425          */
3426         defaultTextHeight : 75,
3427         /**
3428          * The maximum width in pixels of the message box (defaults to 600)
3429          * @type Number
3430          */
3431         maxWidth : 600,
3432         /**
3433          * The minimum width in pixels of the message box (defaults to 100)
3434          * @type Number
3435          */
3436         minWidth : 100,
3437         /**
3438          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3439          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3440          * @type Number
3441          */
3442         minProgressWidth : 250,
3443         /**
3444          * An object containing the default button text strings that can be overriden for localized language support.
3445          * Supported properties are: ok, cancel, yes and no.
3446          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3447          * @type Object
3448          */
3449         buttonText : {
3450             ok : "OK",
3451             cancel : "Cancel",
3452             yes : "Yes",
3453             no : "No"
3454         }
3455     };
3456 }();
3457
3458 /**
3459  * Shorthand for {@link Roo.MessageBox}
3460  */
3461 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3462 Roo.Msg = Roo.Msg || Roo.MessageBox;
3463 /*
3464  * - LGPL
3465  *
3466  * navbar
3467  * 
3468  */
3469
3470 /**
3471  * @class Roo.bootstrap.Navbar
3472  * @extends Roo.bootstrap.Component
3473  * Bootstrap Navbar class
3474
3475  * @constructor
3476  * Create a new Navbar
3477  * @param {Object} config The config object
3478  */
3479
3480
3481 Roo.bootstrap.Navbar = function(config){
3482     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3483     this.addEvents({
3484         // raw events
3485         /**
3486          * @event beforetoggle
3487          * Fire before toggle the menu
3488          * @param {Roo.EventObject} e
3489          */
3490         "beforetoggle" : true
3491     });
3492 };
3493
3494 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3495     
3496     
3497    
3498     // private
3499     navItems : false,
3500     loadMask : false,
3501     
3502     
3503     getAutoCreate : function(){
3504         
3505         
3506         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3507         
3508     },
3509     
3510     initEvents :function ()
3511     {
3512         //Roo.log(this.el.select('.navbar-toggle',true));
3513         this.el.select('.navbar-toggle',true).on('click', function() {
3514             if(this.fireEvent('beforetoggle', this) !== false){
3515                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3516             }
3517             
3518         }, this);
3519         
3520         var mark = {
3521             tag: "div",
3522             cls:"x-dlg-mask"
3523         };
3524         
3525         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3526         
3527         var size = this.el.getSize();
3528         this.maskEl.setSize(size.width, size.height);
3529         this.maskEl.enableDisplayMode("block");
3530         this.maskEl.hide();
3531         
3532         if(this.loadMask){
3533             this.maskEl.show();
3534         }
3535     },
3536     
3537     
3538     getChildContainer : function()
3539     {
3540         if (this.el.select('.collapse').getCount()) {
3541             return this.el.select('.collapse',true).first();
3542         }
3543         
3544         return this.el;
3545     },
3546     
3547     mask : function()
3548     {
3549         this.maskEl.show();
3550     },
3551     
3552     unmask : function()
3553     {
3554         this.maskEl.hide();
3555     } 
3556     
3557     
3558     
3559     
3560 });
3561
3562
3563
3564  
3565
3566  /*
3567  * - LGPL
3568  *
3569  * navbar
3570  * 
3571  */
3572
3573 /**
3574  * @class Roo.bootstrap.NavSimplebar
3575  * @extends Roo.bootstrap.Navbar
3576  * Bootstrap Sidebar class
3577  *
3578  * @cfg {Boolean} inverse is inverted color
3579  * 
3580  * @cfg {String} type (nav | pills | tabs)
3581  * @cfg {Boolean} arrangement stacked | justified
3582  * @cfg {String} align (left | right) alignment
3583  * 
3584  * @cfg {Boolean} main (true|false) main nav bar? default false
3585  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3586  * 
3587  * @cfg {String} tag (header|footer|nav|div) default is nav 
3588
3589  * 
3590  * 
3591  * 
3592  * @constructor
3593  * Create a new Sidebar
3594  * @param {Object} config The config object
3595  */
3596
3597
3598 Roo.bootstrap.NavSimplebar = function(config){
3599     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3600 };
3601
3602 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3603     
3604     inverse: false,
3605     
3606     type: false,
3607     arrangement: '',
3608     align : false,
3609     
3610     
3611     
3612     main : false,
3613     
3614     
3615     tag : false,
3616     
3617     
3618     getAutoCreate : function(){
3619         
3620         
3621         var cfg = {
3622             tag : this.tag || 'div',
3623             cls : 'navbar'
3624         };
3625           
3626         
3627         cfg.cn = [
3628             {
3629                 cls: 'nav',
3630                 tag : 'ul'
3631             }
3632         ];
3633         
3634          
3635         this.type = this.type || 'nav';
3636         if (['tabs','pills'].indexOf(this.type)!==-1) {
3637             cfg.cn[0].cls += ' nav-' + this.type
3638         
3639         
3640         } else {
3641             if (this.type!=='nav') {
3642                 Roo.log('nav type must be nav/tabs/pills')
3643             }
3644             cfg.cn[0].cls += ' navbar-nav'
3645         }
3646         
3647         
3648         
3649         
3650         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3651             cfg.cn[0].cls += ' nav-' + this.arrangement;
3652         }
3653         
3654         
3655         if (this.align === 'right') {
3656             cfg.cn[0].cls += ' navbar-right';
3657         }
3658         
3659         if (this.inverse) {
3660             cfg.cls += ' navbar-inverse';
3661             
3662         }
3663         
3664         
3665         return cfg;
3666     
3667         
3668     }
3669     
3670     
3671     
3672 });
3673
3674
3675
3676  
3677
3678  
3679        /*
3680  * - LGPL
3681  *
3682  * navbar
3683  * 
3684  */
3685
3686 /**
3687  * @class Roo.bootstrap.NavHeaderbar
3688  * @extends Roo.bootstrap.NavSimplebar
3689  * Bootstrap Sidebar class
3690  *
3691  * @cfg {String} brand what is brand
3692  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3693  * @cfg {String} brand_href href of the brand
3694  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3695  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3696  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3697  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3698  * 
3699  * @constructor
3700  * Create a new Sidebar
3701  * @param {Object} config The config object
3702  */
3703
3704
3705 Roo.bootstrap.NavHeaderbar = function(config){
3706     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3707       
3708 };
3709
3710 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3711     
3712     position: '',
3713     brand: '',
3714     brand_href: false,
3715     srButton : true,
3716     autohide : false,
3717     desktopCenter : false,
3718    
3719     
3720     getAutoCreate : function(){
3721         
3722         var   cfg = {
3723             tag: this.nav || 'nav',
3724             cls: 'navbar',
3725             role: 'navigation',
3726             cn: []
3727         };
3728         
3729         var cn = cfg.cn;
3730         if (this.desktopCenter) {
3731             cn.push({cls : 'container', cn : []});
3732             cn = cn[0].cn;
3733         }
3734         
3735         if(this.srButton){
3736             cn.push({
3737                 tag: 'div',
3738                 cls: 'navbar-header',
3739                 cn: [
3740                     {
3741                         tag: 'button',
3742                         type: 'button',
3743                         cls: 'navbar-toggle',
3744                         'data-toggle': 'collapse',
3745                         cn: [
3746                             {
3747                                 tag: 'span',
3748                                 cls: 'sr-only',
3749                                 html: 'Toggle navigation'
3750                             },
3751                             {
3752                                 tag: 'span',
3753                                 cls: 'icon-bar'
3754                             },
3755                             {
3756                                 tag: 'span',
3757                                 cls: 'icon-bar'
3758                             },
3759                             {
3760                                 tag: 'span',
3761                                 cls: 'icon-bar'
3762                             }
3763                         ]
3764                     }
3765                 ]
3766             });
3767         }
3768         
3769         cn.push({
3770             tag: 'div',
3771             cls: 'collapse navbar-collapse',
3772             cn : []
3773         });
3774         
3775         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3776         
3777         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3778             cfg.cls += ' navbar-' + this.position;
3779             
3780             // tag can override this..
3781             
3782             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3783         }
3784         
3785         if (this.brand !== '') {
3786             cn[0].cn.push({
3787                 tag: 'a',
3788                 href: this.brand_href ? this.brand_href : '#',
3789                 cls: 'navbar-brand',
3790                 cn: [
3791                 this.brand
3792                 ]
3793             });
3794         }
3795         
3796         if(this.main){
3797             cfg.cls += ' main-nav';
3798         }
3799         
3800         
3801         return cfg;
3802
3803         
3804     },
3805     getHeaderChildContainer : function()
3806     {
3807         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3808             return this.el.select('.navbar-header',true).first();
3809         }
3810         
3811         return this.getChildContainer();
3812     },
3813     
3814     
3815     initEvents : function()
3816     {
3817         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3818         
3819         if (this.autohide) {
3820             
3821             var prevScroll = 0;
3822             var ft = this.el;
3823             
3824             Roo.get(document).on('scroll',function(e) {
3825                 var ns = Roo.get(document).getScroll().top;
3826                 var os = prevScroll;
3827                 prevScroll = ns;
3828                 
3829                 if(ns > os){
3830                     ft.removeClass('slideDown');
3831                     ft.addClass('slideUp');
3832                     return;
3833                 }
3834                 ft.removeClass('slideUp');
3835                 ft.addClass('slideDown');
3836                  
3837               
3838           },this);
3839         }
3840     }    
3841     
3842 });
3843
3844
3845
3846  
3847
3848  /*
3849  * - LGPL
3850  *
3851  * navbar
3852  * 
3853  */
3854
3855 /**
3856  * @class Roo.bootstrap.NavSidebar
3857  * @extends Roo.bootstrap.Navbar
3858  * Bootstrap Sidebar class
3859  * 
3860  * @constructor
3861  * Create a new Sidebar
3862  * @param {Object} config The config object
3863  */
3864
3865
3866 Roo.bootstrap.NavSidebar = function(config){
3867     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3868 };
3869
3870 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3871     
3872     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3873     
3874     getAutoCreate : function(){
3875         
3876         
3877         return  {
3878             tag: 'div',
3879             cls: 'sidebar sidebar-nav'
3880         };
3881     
3882         
3883     }
3884     
3885     
3886     
3887 });
3888
3889
3890
3891  
3892
3893  /*
3894  * - LGPL
3895  *
3896  * nav group
3897  * 
3898  */
3899
3900 /**
3901  * @class Roo.bootstrap.NavGroup
3902  * @extends Roo.bootstrap.Component
3903  * Bootstrap NavGroup class
3904  * @cfg {String} align (left|right)
3905  * @cfg {Boolean} inverse
3906  * @cfg {String} type (nav|pills|tab) default nav
3907  * @cfg {String} navId - reference Id for navbar.
3908
3909  * 
3910  * @constructor
3911  * Create a new nav group
3912  * @param {Object} config The config object
3913  */
3914
3915 Roo.bootstrap.NavGroup = function(config){
3916     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3917     this.navItems = [];
3918    
3919     Roo.bootstrap.NavGroup.register(this);
3920      this.addEvents({
3921         /**
3922              * @event changed
3923              * Fires when the active item changes
3924              * @param {Roo.bootstrap.NavGroup} this
3925              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3926              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3927          */
3928         'changed': true
3929      });
3930     
3931 };
3932
3933 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3934     
3935     align: '',
3936     inverse: false,
3937     form: false,
3938     type: 'nav',
3939     navId : '',
3940     // private
3941     
3942     navItems : false, 
3943     
3944     getAutoCreate : function()
3945     {
3946         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3947         
3948         cfg = {
3949             tag : 'ul',
3950             cls: 'nav' 
3951         };
3952         
3953         if (['tabs','pills'].indexOf(this.type)!==-1) {
3954             cfg.cls += ' nav-' + this.type
3955         } else {
3956             if (this.type!=='nav') {
3957                 Roo.log('nav type must be nav/tabs/pills')
3958             }
3959             cfg.cls += ' navbar-nav'
3960         }
3961         
3962         if (this.parent().sidebar) {
3963             cfg = {
3964                 tag: 'ul',
3965                 cls: 'dashboard-menu sidebar-menu'
3966             };
3967             
3968             return cfg;
3969         }
3970         
3971         if (this.form === true) {
3972             cfg = {
3973                 tag: 'form',
3974                 cls: 'navbar-form'
3975             };
3976             
3977             if (this.align === 'right') {
3978                 cfg.cls += ' navbar-right';
3979             } else {
3980                 cfg.cls += ' navbar-left';
3981             }
3982         }
3983         
3984         if (this.align === 'right') {
3985             cfg.cls += ' navbar-right';
3986         }
3987         
3988         if (this.inverse) {
3989             cfg.cls += ' navbar-inverse';
3990             
3991         }
3992         
3993         
3994         return cfg;
3995     },
3996     /**
3997     * sets the active Navigation item
3998     * @param {Roo.bootstrap.NavItem} the new current navitem
3999     */
4000     setActiveItem : function(item)
4001     {
4002         var prev = false;
4003         Roo.each(this.navItems, function(v){
4004             if (v == item) {
4005                 return ;
4006             }
4007             if (v.isActive()) {
4008                 v.setActive(false, true);
4009                 prev = v;
4010                 
4011             }
4012             
4013         });
4014
4015         item.setActive(true, true);
4016         this.fireEvent('changed', this, item, prev);
4017         
4018         
4019     },
4020     /**
4021     * gets the active Navigation item
4022     * @return {Roo.bootstrap.NavItem} the current navitem
4023     */
4024     getActive : function()
4025     {
4026         
4027         var prev = false;
4028         Roo.each(this.navItems, function(v){
4029             
4030             if (v.isActive()) {
4031                 prev = v;
4032                 
4033             }
4034             
4035         });
4036         return prev;
4037     },
4038     
4039     indexOfNav : function()
4040     {
4041         
4042         var prev = false;
4043         Roo.each(this.navItems, function(v,i){
4044             
4045             if (v.isActive()) {
4046                 prev = i;
4047                 
4048             }
4049             
4050         });
4051         return prev;
4052     },
4053     /**
4054     * adds a Navigation item
4055     * @param {Roo.bootstrap.NavItem} the navitem to add
4056     */
4057     addItem : function(cfg)
4058     {
4059         var cn = new Roo.bootstrap.NavItem(cfg);
4060         this.register(cn);
4061         cn.parentId = this.id;
4062         cn.onRender(this.el, null);
4063         return cn;
4064     },
4065     /**
4066     * register a Navigation item
4067     * @param {Roo.bootstrap.NavItem} the navitem to add
4068     */
4069     register : function(item)
4070     {
4071         this.navItems.push( item);
4072         item.navId = this.navId;
4073     
4074     },
4075     
4076     /**
4077     * clear all the Navigation item
4078     */
4079    
4080     clearAll : function()
4081     {
4082         this.navItems = [];
4083         this.el.dom.innerHTML = '';
4084     },
4085     
4086     getNavItem: function(tabId)
4087     {
4088         var ret = false;
4089         Roo.each(this.navItems, function(e) {
4090             if (e.tabId == tabId) {
4091                ret =  e;
4092                return false;
4093             }
4094             return true;
4095             
4096         });
4097         return ret;
4098     },
4099     
4100     setActiveNext : function()
4101     {
4102         var i = this.indexOfNav(this.getActive());
4103         if (i > this.navItems.length) {
4104             return;
4105         }
4106         this.setActiveItem(this.navItems[i+1]);
4107     },
4108     setActivePrev : function()
4109     {
4110         var i = this.indexOfNav(this.getActive());
4111         if (i  < 1) {
4112             return;
4113         }
4114         this.setActiveItem(this.navItems[i-1]);
4115     },
4116     clearWasActive : function(except) {
4117         Roo.each(this.navItems, function(e) {
4118             if (e.tabId != except.tabId && e.was_active) {
4119                e.was_active = false;
4120                return false;
4121             }
4122             return true;
4123             
4124         });
4125     },
4126     getWasActive : function ()
4127     {
4128         var r = false;
4129         Roo.each(this.navItems, function(e) {
4130             if (e.was_active) {
4131                r = e;
4132                return false;
4133             }
4134             return true;
4135             
4136         });
4137         return r;
4138     }
4139     
4140     
4141 });
4142
4143  
4144 Roo.apply(Roo.bootstrap.NavGroup, {
4145     
4146     groups: {},
4147      /**
4148     * register a Navigation Group
4149     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4150     */
4151     register : function(navgrp)
4152     {
4153         this.groups[navgrp.navId] = navgrp;
4154         
4155     },
4156     /**
4157     * fetch a Navigation Group based on the navigation ID
4158     * @param {string} the navgroup to add
4159     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4160     */
4161     get: function(navId) {
4162         if (typeof(this.groups[navId]) == 'undefined') {
4163             return false;
4164             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4165         }
4166         return this.groups[navId] ;
4167     }
4168     
4169     
4170     
4171 });
4172
4173  /*
4174  * - LGPL
4175  *
4176  * row
4177  * 
4178  */
4179
4180 /**
4181  * @class Roo.bootstrap.NavItem
4182  * @extends Roo.bootstrap.Component
4183  * Bootstrap Navbar.NavItem class
4184  * @cfg {String} href  link to
4185  * @cfg {String} html content of button
4186  * @cfg {String} badge text inside badge
4187  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4188  * @cfg {String} glyphicon name of glyphicon
4189  * @cfg {String} icon name of font awesome icon
4190  * @cfg {Boolean} active Is item active
4191  * @cfg {Boolean} disabled Is item disabled
4192  
4193  * @cfg {Boolean} preventDefault (true | false) default false
4194  * @cfg {String} tabId the tab that this item activates.
4195  * @cfg {String} tagtype (a|span) render as a href or span?
4196  * @cfg {Boolean} animateRef (true|false) link to element default false  
4197   
4198  * @constructor
4199  * Create a new Navbar Item
4200  * @param {Object} config The config object
4201  */
4202 Roo.bootstrap.NavItem = function(config){
4203     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4204     this.addEvents({
4205         // raw events
4206         /**
4207          * @event click
4208          * The raw click event for the entire grid.
4209          * @param {Roo.EventObject} e
4210          */
4211         "click" : true,
4212          /**
4213             * @event changed
4214             * Fires when the active item active state changes
4215             * @param {Roo.bootstrap.NavItem} this
4216             * @param {boolean} state the new state
4217              
4218          */
4219         'changed': true,
4220         /**
4221             * @event scrollto
4222             * Fires when scroll to element
4223             * @param {Roo.bootstrap.NavItem} this
4224             * @param {Object} options
4225             * @param {Roo.EventObject} e
4226              
4227          */
4228         'scrollto': true
4229     });
4230    
4231 };
4232
4233 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4234     
4235     href: false,
4236     html: '',
4237     badge: '',
4238     icon: false,
4239     glyphicon: false,
4240     active: false,
4241     preventDefault : false,
4242     tabId : false,
4243     tagtype : 'a',
4244     disabled : false,
4245     animateRef : false,
4246     was_active : false,
4247     
4248     getAutoCreate : function(){
4249          
4250         var cfg = {
4251             tag: 'li',
4252             cls: 'nav-item'
4253             
4254         };
4255         
4256         if (this.active) {
4257             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4258         }
4259         if (this.disabled) {
4260             cfg.cls += ' disabled';
4261         }
4262         
4263         if (this.href || this.html || this.glyphicon || this.icon) {
4264             cfg.cn = [
4265                 {
4266                     tag: this.tagtype,
4267                     href : this.href || "#",
4268                     html: this.html || ''
4269                 }
4270             ];
4271             
4272             if (this.icon) {
4273                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4274             }
4275
4276             if(this.glyphicon) {
4277                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4278             }
4279             
4280             if (this.menu) {
4281                 
4282                 cfg.cn[0].html += " <span class='caret'></span>";
4283              
4284             }
4285             
4286             if (this.badge !== '') {
4287                  
4288                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4289             }
4290         }
4291         
4292         
4293         
4294         return cfg;
4295     },
4296     initEvents: function() 
4297     {
4298         if (typeof (this.menu) != 'undefined') {
4299             this.menu.parentType = this.xtype;
4300             this.menu.triggerEl = this.el;
4301             this.menu = this.addxtype(Roo.apply({}, this.menu));
4302         }
4303         
4304         this.el.select('a',true).on('click', this.onClick, this);
4305         
4306         if(this.tagtype == 'span'){
4307             this.el.select('span',true).on('click', this.onClick, this);
4308         }
4309        
4310         // at this point parent should be available..
4311         this.parent().register(this);
4312     },
4313     
4314     onClick : function(e)
4315     {
4316         if (e.getTarget('.dropdown-menu-item')) {
4317             // did you click on a menu itemm.... - then don't trigger onclick..
4318             return;
4319         }
4320         
4321         if(
4322                 this.preventDefault || 
4323                 this.href == '#' 
4324         ){
4325             Roo.log("NavItem - prevent Default?");
4326             e.preventDefault();
4327         }
4328         
4329         if (this.disabled) {
4330             return;
4331         }
4332         
4333         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4334         if (tg && tg.transition) {
4335             Roo.log("waiting for the transitionend");
4336             return;
4337         }
4338         
4339         
4340         
4341         //Roo.log("fire event clicked");
4342         if(this.fireEvent('click', this, e) === false){
4343             return;
4344         };
4345         
4346         if(this.tagtype == 'span'){
4347             return;
4348         }
4349         
4350         //Roo.log(this.href);
4351         var ael = this.el.select('a',true).first();
4352         //Roo.log(ael);
4353         
4354         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4355             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4356             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4357                 return; // ignore... - it's a 'hash' to another page.
4358             }
4359             Roo.log("NavItem - prevent Default?");
4360             e.preventDefault();
4361             this.scrollToElement(e);
4362         }
4363         
4364         
4365         var p =  this.parent();
4366    
4367         if (['tabs','pills'].indexOf(p.type)!==-1) {
4368             if (typeof(p.setActiveItem) !== 'undefined') {
4369                 p.setActiveItem(this);
4370             }
4371         }
4372         
4373         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4374         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4375             // remove the collapsed menu expand...
4376             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4377         }
4378     },
4379     
4380     isActive: function () {
4381         return this.active
4382     },
4383     setActive : function(state, fire, is_was_active)
4384     {
4385         if (this.active && !state && this.navId) {
4386             this.was_active = true;
4387             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4388             if (nv) {
4389                 nv.clearWasActive(this);
4390             }
4391             
4392         }
4393         this.active = state;
4394         
4395         if (!state ) {
4396             this.el.removeClass('active');
4397         } else if (!this.el.hasClass('active')) {
4398             this.el.addClass('active');
4399         }
4400         if (fire) {
4401             this.fireEvent('changed', this, state);
4402         }
4403         
4404         // show a panel if it's registered and related..
4405         
4406         if (!this.navId || !this.tabId || !state || is_was_active) {
4407             return;
4408         }
4409         
4410         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4411         if (!tg) {
4412             return;
4413         }
4414         var pan = tg.getPanelByName(this.tabId);
4415         if (!pan) {
4416             return;
4417         }
4418         // if we can not flip to new panel - go back to old nav highlight..
4419         if (false == tg.showPanel(pan)) {
4420             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4421             if (nv) {
4422                 var onav = nv.getWasActive();
4423                 if (onav) {
4424                     onav.setActive(true, false, true);
4425                 }
4426             }
4427             
4428         }
4429         
4430         
4431         
4432     },
4433      // this should not be here...
4434     setDisabled : function(state)
4435     {
4436         this.disabled = state;
4437         if (!state ) {
4438             this.el.removeClass('disabled');
4439         } else if (!this.el.hasClass('disabled')) {
4440             this.el.addClass('disabled');
4441         }
4442         
4443     },
4444     
4445     /**
4446      * Fetch the element to display the tooltip on.
4447      * @return {Roo.Element} defaults to this.el
4448      */
4449     tooltipEl : function()
4450     {
4451         return this.el.select('' + this.tagtype + '', true).first();
4452     },
4453     
4454     scrollToElement : function(e)
4455     {
4456         var c = document.body;
4457         
4458         /*
4459          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4460          */
4461         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4462             c = document.documentElement;
4463         }
4464         
4465         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4466         
4467         if(!target){
4468             return;
4469         }
4470
4471         var o = target.calcOffsetsTo(c);
4472         
4473         var options = {
4474             target : target,
4475             value : o[1]
4476         };
4477         
4478         this.fireEvent('scrollto', this, options, e);
4479         
4480         Roo.get(c).scrollTo('top', options.value, true);
4481         
4482         return;
4483     }
4484 });
4485  
4486
4487  /*
4488  * - LGPL
4489  *
4490  * sidebar item
4491  *
4492  *  li
4493  *    <span> icon </span>
4494  *    <span> text </span>
4495  *    <span>badge </span>
4496  */
4497
4498 /**
4499  * @class Roo.bootstrap.NavSidebarItem
4500  * @extends Roo.bootstrap.NavItem
4501  * Bootstrap Navbar.NavSidebarItem class
4502  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4503  * {bool} open is the menu open
4504  * @constructor
4505  * Create a new Navbar Button
4506  * @param {Object} config The config object
4507  */
4508 Roo.bootstrap.NavSidebarItem = function(config){
4509     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4510     this.addEvents({
4511         // raw events
4512         /**
4513          * @event click
4514          * The raw click event for the entire grid.
4515          * @param {Roo.EventObject} e
4516          */
4517         "click" : true,
4518          /**
4519             * @event changed
4520             * Fires when the active item active state changes
4521             * @param {Roo.bootstrap.NavSidebarItem} this
4522             * @param {boolean} state the new state
4523              
4524          */
4525         'changed': true
4526     });
4527    
4528 };
4529
4530 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4531     
4532     badgeWeight : 'default',
4533     
4534     open: false,
4535     
4536     getAutoCreate : function(){
4537         
4538         
4539         var a = {
4540                 tag: 'a',
4541                 href : this.href || '#',
4542                 cls: '',
4543                 html : '',
4544                 cn : []
4545         };
4546         var cfg = {
4547             tag: 'li',
4548             cls: '',
4549             cn: [ a ]
4550         };
4551         var span = {
4552             tag: 'span',
4553             html : this.html || ''
4554         };
4555         
4556         
4557         if (this.active) {
4558             cfg.cls += ' active';
4559         }
4560         
4561         if (this.disabled) {
4562             cfg.cls += ' disabled';
4563         }
4564         if (this.open) {
4565             cfg.cls += ' open x-open';
4566         }
4567         // left icon..
4568         if (this.glyphicon || this.icon) {
4569             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4570             a.cn.push({ tag : 'i', cls : c }) ;
4571         }
4572         // html..
4573         a.cn.push(span);
4574         // then badge..
4575         if (this.badge !== '') {
4576             
4577             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4578         }
4579         // fi
4580         if (this.menu) {
4581             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4582             a.cls += 'dropdown-toggle treeview' ;
4583         }
4584         
4585         return cfg;
4586          
4587            
4588     },
4589     
4590     initEvents : function()
4591     { 
4592         if (typeof (this.menu) != 'undefined') {
4593             this.menu.parentType = this.xtype;
4594             this.menu.triggerEl = this.el;
4595             this.menu = this.addxtype(Roo.apply({}, this.menu));
4596         }
4597         
4598         this.el.on('click', this.onClick, this);
4599        
4600     
4601         if(this.badge !== ''){
4602  
4603             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4604         }
4605         
4606     },
4607     
4608     onClick : function(e)
4609     {
4610         if(this.disabled){
4611             e.preventDefault();
4612             return;
4613         }
4614         
4615         if(this.preventDefault){
4616             e.preventDefault();
4617         }
4618         
4619         this.fireEvent('click', this);
4620     },
4621     
4622     disable : function()
4623     {
4624         this.setDisabled(true);
4625     },
4626     
4627     enable : function()
4628     {
4629         this.setDisabled(false);
4630     },
4631     
4632     setDisabled : function(state)
4633     {
4634         if(this.disabled == state){
4635             return;
4636         }
4637         
4638         this.disabled = state;
4639         
4640         if (state) {
4641             this.el.addClass('disabled');
4642             return;
4643         }
4644         
4645         this.el.removeClass('disabled');
4646         
4647         return;
4648     },
4649     
4650     setActive : function(state)
4651     {
4652         if(this.active == state){
4653             return;
4654         }
4655         
4656         this.active = state;
4657         
4658         if (state) {
4659             this.el.addClass('active');
4660             return;
4661         }
4662         
4663         this.el.removeClass('active');
4664         
4665         return;
4666     },
4667     
4668     isActive: function () 
4669     {
4670         return this.active;
4671     },
4672     
4673     setBadge : function(str)
4674     {
4675         if(!this.badgeEl){
4676             return;
4677         }
4678         
4679         this.badgeEl.dom.innerHTML = str;
4680     }
4681     
4682    
4683      
4684  
4685 });
4686  
4687
4688  /*
4689  * - LGPL
4690  *
4691  * row
4692  * 
4693  */
4694
4695 /**
4696  * @class Roo.bootstrap.Row
4697  * @extends Roo.bootstrap.Component
4698  * Bootstrap Row class (contains columns...)
4699  * 
4700  * @constructor
4701  * Create a new Row
4702  * @param {Object} config The config object
4703  */
4704
4705 Roo.bootstrap.Row = function(config){
4706     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4707 };
4708
4709 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4710     
4711     getAutoCreate : function(){
4712        return {
4713             cls: 'row clearfix'
4714        };
4715     }
4716     
4717     
4718 });
4719
4720  
4721
4722  /*
4723  * - LGPL
4724  *
4725  * element
4726  * 
4727  */
4728
4729 /**
4730  * @class Roo.bootstrap.Element
4731  * @extends Roo.bootstrap.Component
4732  * Bootstrap Element class
4733  * @cfg {String} html contents of the element
4734  * @cfg {String} tag tag of the element
4735  * @cfg {String} cls class of the element
4736  * @cfg {Boolean} preventDefault (true|false) default false
4737  * @cfg {Boolean} clickable (true|false) default false
4738  * 
4739  * @constructor
4740  * Create a new Element
4741  * @param {Object} config The config object
4742  */
4743
4744 Roo.bootstrap.Element = function(config){
4745     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4746     
4747     this.addEvents({
4748         // raw events
4749         /**
4750          * @event click
4751          * When a element is chick
4752          * @param {Roo.bootstrap.Element} this
4753          * @param {Roo.EventObject} e
4754          */
4755         "click" : true
4756     });
4757 };
4758
4759 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4760     
4761     tag: 'div',
4762     cls: '',
4763     html: '',
4764     preventDefault: false, 
4765     clickable: false,
4766     
4767     getAutoCreate : function(){
4768         
4769         var cfg = {
4770             tag: this.tag,
4771             cls: this.cls,
4772             html: this.html
4773         };
4774         
4775         return cfg;
4776     },
4777     
4778     initEvents: function() 
4779     {
4780         Roo.bootstrap.Element.superclass.initEvents.call(this);
4781         
4782         if(this.clickable){
4783             this.el.on('click', this.onClick, this);
4784         }
4785         
4786     },
4787     
4788     onClick : function(e)
4789     {
4790         if(this.preventDefault){
4791             e.preventDefault();
4792         }
4793         
4794         this.fireEvent('click', this, e);
4795     },
4796     
4797     getValue : function()
4798     {
4799         return this.el.dom.innerHTML;
4800     },
4801     
4802     setValue : function(value)
4803     {
4804         this.el.dom.innerHTML = value;
4805     }
4806    
4807 });
4808
4809  
4810
4811  /*
4812  * - LGPL
4813  *
4814  * pagination
4815  * 
4816  */
4817
4818 /**
4819  * @class Roo.bootstrap.Pagination
4820  * @extends Roo.bootstrap.Component
4821  * Bootstrap Pagination class
4822  * @cfg {String} size xs | sm | md | lg
4823  * @cfg {Boolean} inverse false | true
4824  * 
4825  * @constructor
4826  * Create a new Pagination
4827  * @param {Object} config The config object
4828  */
4829
4830 Roo.bootstrap.Pagination = function(config){
4831     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4832 };
4833
4834 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4835     
4836     cls: false,
4837     size: false,
4838     inverse: false,
4839     
4840     getAutoCreate : function(){
4841         var cfg = {
4842             tag: 'ul',
4843                 cls: 'pagination'
4844         };
4845         if (this.inverse) {
4846             cfg.cls += ' inverse';
4847         }
4848         if (this.html) {
4849             cfg.html=this.html;
4850         }
4851         if (this.cls) {
4852             cfg.cls += " " + this.cls;
4853         }
4854         return cfg;
4855     }
4856    
4857 });
4858
4859  
4860
4861  /*
4862  * - LGPL
4863  *
4864  * Pagination item
4865  * 
4866  */
4867
4868
4869 /**
4870  * @class Roo.bootstrap.PaginationItem
4871  * @extends Roo.bootstrap.Component
4872  * Bootstrap PaginationItem class
4873  * @cfg {String} html text
4874  * @cfg {String} href the link
4875  * @cfg {Boolean} preventDefault (true | false) default true
4876  * @cfg {Boolean} active (true | false) default false
4877  * @cfg {Boolean} disabled default false
4878  * 
4879  * 
4880  * @constructor
4881  * Create a new PaginationItem
4882  * @param {Object} config The config object
4883  */
4884
4885
4886 Roo.bootstrap.PaginationItem = function(config){
4887     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4888     this.addEvents({
4889         // raw events
4890         /**
4891          * @event click
4892          * The raw click event for the entire grid.
4893          * @param {Roo.EventObject} e
4894          */
4895         "click" : true
4896     });
4897 };
4898
4899 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4900     
4901     href : false,
4902     html : false,
4903     preventDefault: true,
4904     active : false,
4905     cls : false,
4906     disabled: false,
4907     
4908     getAutoCreate : function(){
4909         var cfg= {
4910             tag: 'li',
4911             cn: [
4912                 {
4913                     tag : 'a',
4914                     href : this.href ? this.href : '#',
4915                     html : this.html ? this.html : ''
4916                 }
4917             ]
4918         };
4919         
4920         if(this.cls){
4921             cfg.cls = this.cls;
4922         }
4923         
4924         if(this.disabled){
4925             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4926         }
4927         
4928         if(this.active){
4929             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4930         }
4931         
4932         return cfg;
4933     },
4934     
4935     initEvents: function() {
4936         
4937         this.el.on('click', this.onClick, this);
4938         
4939     },
4940     onClick : function(e)
4941     {
4942         Roo.log('PaginationItem on click ');
4943         if(this.preventDefault){
4944             e.preventDefault();
4945         }
4946         
4947         if(this.disabled){
4948             return;
4949         }
4950         
4951         this.fireEvent('click', this, e);
4952     }
4953    
4954 });
4955
4956  
4957
4958  /*
4959  * - LGPL
4960  *
4961  * slider
4962  * 
4963  */
4964
4965
4966 /**
4967  * @class Roo.bootstrap.Slider
4968  * @extends Roo.bootstrap.Component
4969  * Bootstrap Slider class
4970  *    
4971  * @constructor
4972  * Create a new Slider
4973  * @param {Object} config The config object
4974  */
4975
4976 Roo.bootstrap.Slider = function(config){
4977     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4978 };
4979
4980 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4981     
4982     getAutoCreate : function(){
4983         
4984         var cfg = {
4985             tag: 'div',
4986             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4987             cn: [
4988                 {
4989                     tag: 'a',
4990                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4991                 }
4992             ]
4993         };
4994         
4995         return cfg;
4996     }
4997    
4998 });
4999
5000  /*
5001  * Based on:
5002  * Ext JS Library 1.1.1
5003  * Copyright(c) 2006-2007, Ext JS, LLC.
5004  *
5005  * Originally Released Under LGPL - original licence link has changed is not relivant.
5006  *
5007  * Fork - LGPL
5008  * <script type="text/javascript">
5009  */
5010  
5011
5012 /**
5013  * @class Roo.grid.ColumnModel
5014  * @extends Roo.util.Observable
5015  * This is the default implementation of a ColumnModel used by the Grid. It defines
5016  * the columns in the grid.
5017  * <br>Usage:<br>
5018  <pre><code>
5019  var colModel = new Roo.grid.ColumnModel([
5020         {header: "Ticker", width: 60, sortable: true, locked: true},
5021         {header: "Company Name", width: 150, sortable: true},
5022         {header: "Market Cap.", width: 100, sortable: true},
5023         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5024         {header: "Employees", width: 100, sortable: true, resizable: false}
5025  ]);
5026  </code></pre>
5027  * <p>
5028  
5029  * The config options listed for this class are options which may appear in each
5030  * individual column definition.
5031  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5032  * @constructor
5033  * @param {Object} config An Array of column config objects. See this class's
5034  * config objects for details.
5035 */
5036 Roo.grid.ColumnModel = function(config){
5037         /**
5038      * The config passed into the constructor
5039      */
5040     this.config = config;
5041     this.lookup = {};
5042
5043     // if no id, create one
5044     // if the column does not have a dataIndex mapping,
5045     // map it to the order it is in the config
5046     for(var i = 0, len = config.length; i < len; i++){
5047         var c = config[i];
5048         if(typeof c.dataIndex == "undefined"){
5049             c.dataIndex = i;
5050         }
5051         if(typeof c.renderer == "string"){
5052             c.renderer = Roo.util.Format[c.renderer];
5053         }
5054         if(typeof c.id == "undefined"){
5055             c.id = Roo.id();
5056         }
5057         if(c.editor && c.editor.xtype){
5058             c.editor  = Roo.factory(c.editor, Roo.grid);
5059         }
5060         if(c.editor && c.editor.isFormField){
5061             c.editor = new Roo.grid.GridEditor(c.editor);
5062         }
5063         this.lookup[c.id] = c;
5064     }
5065
5066     /**
5067      * The width of columns which have no width specified (defaults to 100)
5068      * @type Number
5069      */
5070     this.defaultWidth = 100;
5071
5072     /**
5073      * Default sortable of columns which have no sortable specified (defaults to false)
5074      * @type Boolean
5075      */
5076     this.defaultSortable = false;
5077
5078     this.addEvents({
5079         /**
5080              * @event widthchange
5081              * Fires when the width of a column changes.
5082              * @param {ColumnModel} this
5083              * @param {Number} columnIndex The column index
5084              * @param {Number} newWidth The new width
5085              */
5086             "widthchange": true,
5087         /**
5088              * @event headerchange
5089              * Fires when the text of a header changes.
5090              * @param {ColumnModel} this
5091              * @param {Number} columnIndex The column index
5092              * @param {Number} newText The new header text
5093              */
5094             "headerchange": true,
5095         /**
5096              * @event hiddenchange
5097              * Fires when a column is hidden or "unhidden".
5098              * @param {ColumnModel} this
5099              * @param {Number} columnIndex The column index
5100              * @param {Boolean} hidden true if hidden, false otherwise
5101              */
5102             "hiddenchange": true,
5103             /**
5104          * @event columnmoved
5105          * Fires when a column is moved.
5106          * @param {ColumnModel} this
5107          * @param {Number} oldIndex
5108          * @param {Number} newIndex
5109          */
5110         "columnmoved" : true,
5111         /**
5112          * @event columlockchange
5113          * Fires when a column's locked state is changed
5114          * @param {ColumnModel} this
5115          * @param {Number} colIndex
5116          * @param {Boolean} locked true if locked
5117          */
5118         "columnlockchange" : true
5119     });
5120     Roo.grid.ColumnModel.superclass.constructor.call(this);
5121 };
5122 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5123     /**
5124      * @cfg {String} header The header text to display in the Grid view.
5125      */
5126     /**
5127      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5128      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5129      * specified, the column's index is used as an index into the Record's data Array.
5130      */
5131     /**
5132      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5133      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5134      */
5135     /**
5136      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5137      * Defaults to the value of the {@link #defaultSortable} property.
5138      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5139      */
5140     /**
5141      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5142      */
5143     /**
5144      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5145      */
5146     /**
5147      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5148      */
5149     /**
5150      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5151      */
5152     /**
5153      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5154      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5155      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5156      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5157      */
5158        /**
5159      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5160      */
5161     /**
5162      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5163      */
5164     /**
5165      * @cfg {String} cursor (Optional)
5166      */
5167     /**
5168      * @cfg {String} tooltip (Optional)
5169      */
5170     /**
5171      * @cfg {Number} xs (Optional)
5172      */
5173     /**
5174      * @cfg {Number} sm (Optional)
5175      */
5176     /**
5177      * @cfg {Number} md (Optional)
5178      */
5179     /**
5180      * @cfg {Number} lg (Optional)
5181      */
5182     /**
5183      * Returns the id of the column at the specified index.
5184      * @param {Number} index The column index
5185      * @return {String} the id
5186      */
5187     getColumnId : function(index){
5188         return this.config[index].id;
5189     },
5190
5191     /**
5192      * Returns the column for a specified id.
5193      * @param {String} id The column id
5194      * @return {Object} the column
5195      */
5196     getColumnById : function(id){
5197         return this.lookup[id];
5198     },
5199
5200     
5201     /**
5202      * Returns the column for a specified dataIndex.
5203      * @param {String} dataIndex The column dataIndex
5204      * @return {Object|Boolean} the column or false if not found
5205      */
5206     getColumnByDataIndex: function(dataIndex){
5207         var index = this.findColumnIndex(dataIndex);
5208         return index > -1 ? this.config[index] : false;
5209     },
5210     
5211     /**
5212      * Returns the index for a specified column id.
5213      * @param {String} id The column id
5214      * @return {Number} the index, or -1 if not found
5215      */
5216     getIndexById : function(id){
5217         for(var i = 0, len = this.config.length; i < len; i++){
5218             if(this.config[i].id == id){
5219                 return i;
5220             }
5221         }
5222         return -1;
5223     },
5224     
5225     /**
5226      * Returns the index for a specified column dataIndex.
5227      * @param {String} dataIndex The column dataIndex
5228      * @return {Number} the index, or -1 if not found
5229      */
5230     
5231     findColumnIndex : function(dataIndex){
5232         for(var i = 0, len = this.config.length; i < len; i++){
5233             if(this.config[i].dataIndex == dataIndex){
5234                 return i;
5235             }
5236         }
5237         return -1;
5238     },
5239     
5240     
5241     moveColumn : function(oldIndex, newIndex){
5242         var c = this.config[oldIndex];
5243         this.config.splice(oldIndex, 1);
5244         this.config.splice(newIndex, 0, c);
5245         this.dataMap = null;
5246         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5247     },
5248
5249     isLocked : function(colIndex){
5250         return this.config[colIndex].locked === true;
5251     },
5252
5253     setLocked : function(colIndex, value, suppressEvent){
5254         if(this.isLocked(colIndex) == value){
5255             return;
5256         }
5257         this.config[colIndex].locked = value;
5258         if(!suppressEvent){
5259             this.fireEvent("columnlockchange", this, colIndex, value);
5260         }
5261     },
5262
5263     getTotalLockedWidth : function(){
5264         var totalWidth = 0;
5265         for(var i = 0; i < this.config.length; i++){
5266             if(this.isLocked(i) && !this.isHidden(i)){
5267                 this.totalWidth += this.getColumnWidth(i);
5268             }
5269         }
5270         return totalWidth;
5271     },
5272
5273     getLockedCount : function(){
5274         for(var i = 0, len = this.config.length; i < len; i++){
5275             if(!this.isLocked(i)){
5276                 return i;
5277             }
5278         }
5279         
5280         return this.config.length;
5281     },
5282
5283     /**
5284      * Returns the number of columns.
5285      * @return {Number}
5286      */
5287     getColumnCount : function(visibleOnly){
5288         if(visibleOnly === true){
5289             var c = 0;
5290             for(var i = 0, len = this.config.length; i < len; i++){
5291                 if(!this.isHidden(i)){
5292                     c++;
5293                 }
5294             }
5295             return c;
5296         }
5297         return this.config.length;
5298     },
5299
5300     /**
5301      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5302      * @param {Function} fn
5303      * @param {Object} scope (optional)
5304      * @return {Array} result
5305      */
5306     getColumnsBy : function(fn, scope){
5307         var r = [];
5308         for(var i = 0, len = this.config.length; i < len; i++){
5309             var c = this.config[i];
5310             if(fn.call(scope||this, c, i) === true){
5311                 r[r.length] = c;
5312             }
5313         }
5314         return r;
5315     },
5316
5317     /**
5318      * Returns true if the specified column is sortable.
5319      * @param {Number} col The column index
5320      * @return {Boolean}
5321      */
5322     isSortable : function(col){
5323         if(typeof this.config[col].sortable == "undefined"){
5324             return this.defaultSortable;
5325         }
5326         return this.config[col].sortable;
5327     },
5328
5329     /**
5330      * Returns the rendering (formatting) function defined for the column.
5331      * @param {Number} col The column index.
5332      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5333      */
5334     getRenderer : function(col){
5335         if(!this.config[col].renderer){
5336             return Roo.grid.ColumnModel.defaultRenderer;
5337         }
5338         return this.config[col].renderer;
5339     },
5340
5341     /**
5342      * Sets the rendering (formatting) function for a column.
5343      * @param {Number} col The column index
5344      * @param {Function} fn The function to use to process the cell's raw data
5345      * to return HTML markup for the grid view. The render function is called with
5346      * the following parameters:<ul>
5347      * <li>Data value.</li>
5348      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5349      * <li>css A CSS style string to apply to the table cell.</li>
5350      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5351      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5352      * <li>Row index</li>
5353      * <li>Column index</li>
5354      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5355      */
5356     setRenderer : function(col, fn){
5357         this.config[col].renderer = fn;
5358     },
5359
5360     /**
5361      * Returns the width for the specified column.
5362      * @param {Number} col The column index
5363      * @return {Number}
5364      */
5365     getColumnWidth : function(col){
5366         return this.config[col].width * 1 || this.defaultWidth;
5367     },
5368
5369     /**
5370      * Sets the width for a column.
5371      * @param {Number} col The column index
5372      * @param {Number} width The new width
5373      */
5374     setColumnWidth : function(col, width, suppressEvent){
5375         this.config[col].width = width;
5376         this.totalWidth = null;
5377         if(!suppressEvent){
5378              this.fireEvent("widthchange", this, col, width);
5379         }
5380     },
5381
5382     /**
5383      * Returns the total width of all columns.
5384      * @param {Boolean} includeHidden True to include hidden column widths
5385      * @return {Number}
5386      */
5387     getTotalWidth : function(includeHidden){
5388         if(!this.totalWidth){
5389             this.totalWidth = 0;
5390             for(var i = 0, len = this.config.length; i < len; i++){
5391                 if(includeHidden || !this.isHidden(i)){
5392                     this.totalWidth += this.getColumnWidth(i);
5393                 }
5394             }
5395         }
5396         return this.totalWidth;
5397     },
5398
5399     /**
5400      * Returns the header for the specified column.
5401      * @param {Number} col The column index
5402      * @return {String}
5403      */
5404     getColumnHeader : function(col){
5405         return this.config[col].header;
5406     },
5407
5408     /**
5409      * Sets the header for a column.
5410      * @param {Number} col The column index
5411      * @param {String} header The new header
5412      */
5413     setColumnHeader : function(col, header){
5414         this.config[col].header = header;
5415         this.fireEvent("headerchange", this, col, header);
5416     },
5417
5418     /**
5419      * Returns the tooltip for the specified column.
5420      * @param {Number} col The column index
5421      * @return {String}
5422      */
5423     getColumnTooltip : function(col){
5424             return this.config[col].tooltip;
5425     },
5426     /**
5427      * Sets the tooltip for a column.
5428      * @param {Number} col The column index
5429      * @param {String} tooltip The new tooltip
5430      */
5431     setColumnTooltip : function(col, tooltip){
5432             this.config[col].tooltip = tooltip;
5433     },
5434
5435     /**
5436      * Returns the dataIndex for the specified column.
5437      * @param {Number} col The column index
5438      * @return {Number}
5439      */
5440     getDataIndex : function(col){
5441         return this.config[col].dataIndex;
5442     },
5443
5444     /**
5445      * Sets the dataIndex for a column.
5446      * @param {Number} col The column index
5447      * @param {Number} dataIndex The new dataIndex
5448      */
5449     setDataIndex : function(col, dataIndex){
5450         this.config[col].dataIndex = dataIndex;
5451     },
5452
5453     
5454     
5455     /**
5456      * Returns true if the cell is editable.
5457      * @param {Number} colIndex The column index
5458      * @param {Number} rowIndex The row index - this is nto actually used..?
5459      * @return {Boolean}
5460      */
5461     isCellEditable : function(colIndex, rowIndex){
5462         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5463     },
5464
5465     /**
5466      * Returns the editor defined for the cell/column.
5467      * return false or null to disable editing.
5468      * @param {Number} colIndex The column index
5469      * @param {Number} rowIndex The row index
5470      * @return {Object}
5471      */
5472     getCellEditor : function(colIndex, rowIndex){
5473         return this.config[colIndex].editor;
5474     },
5475
5476     /**
5477      * Sets if a column is editable.
5478      * @param {Number} col The column index
5479      * @param {Boolean} editable True if the column is editable
5480      */
5481     setEditable : function(col, editable){
5482         this.config[col].editable = editable;
5483     },
5484
5485
5486     /**
5487      * Returns true if the column is hidden.
5488      * @param {Number} colIndex The column index
5489      * @return {Boolean}
5490      */
5491     isHidden : function(colIndex){
5492         return this.config[colIndex].hidden;
5493     },
5494
5495
5496     /**
5497      * Returns true if the column width cannot be changed
5498      */
5499     isFixed : function(colIndex){
5500         return this.config[colIndex].fixed;
5501     },
5502
5503     /**
5504      * Returns true if the column can be resized
5505      * @return {Boolean}
5506      */
5507     isResizable : function(colIndex){
5508         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5509     },
5510     /**
5511      * Sets if a column is hidden.
5512      * @param {Number} colIndex The column index
5513      * @param {Boolean} hidden True if the column is hidden
5514      */
5515     setHidden : function(colIndex, hidden){
5516         this.config[colIndex].hidden = hidden;
5517         this.totalWidth = null;
5518         this.fireEvent("hiddenchange", this, colIndex, hidden);
5519     },
5520
5521     /**
5522      * Sets the editor for a column.
5523      * @param {Number} col The column index
5524      * @param {Object} editor The editor object
5525      */
5526     setEditor : function(col, editor){
5527         this.config[col].editor = editor;
5528     }
5529 });
5530
5531 Roo.grid.ColumnModel.defaultRenderer = function(value){
5532         if(typeof value == "string" && value.length < 1){
5533             return "&#160;";
5534         }
5535         return value;
5536 };
5537
5538 // Alias for backwards compatibility
5539 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5540 /*
5541  * Based on:
5542  * Ext JS Library 1.1.1
5543  * Copyright(c) 2006-2007, Ext JS, LLC.
5544  *
5545  * Originally Released Under LGPL - original licence link has changed is not relivant.
5546  *
5547  * Fork - LGPL
5548  * <script type="text/javascript">
5549  */
5550  
5551 /**
5552  * @class Roo.LoadMask
5553  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5554  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5555  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5556  * element's UpdateManager load indicator and will be destroyed after the initial load.
5557  * @constructor
5558  * Create a new LoadMask
5559  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5560  * @param {Object} config The config object
5561  */
5562 Roo.LoadMask = function(el, config){
5563     this.el = Roo.get(el);
5564     Roo.apply(this, config);
5565     if(this.store){
5566         this.store.on('beforeload', this.onBeforeLoad, this);
5567         this.store.on('load', this.onLoad, this);
5568         this.store.on('loadexception', this.onLoadException, this);
5569         this.removeMask = false;
5570     }else{
5571         var um = this.el.getUpdateManager();
5572         um.showLoadIndicator = false; // disable the default indicator
5573         um.on('beforeupdate', this.onBeforeLoad, this);
5574         um.on('update', this.onLoad, this);
5575         um.on('failure', this.onLoad, this);
5576         this.removeMask = true;
5577     }
5578 };
5579
5580 Roo.LoadMask.prototype = {
5581     /**
5582      * @cfg {Boolean} removeMask
5583      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5584      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5585      */
5586     /**
5587      * @cfg {String} msg
5588      * The text to display in a centered loading message box (defaults to 'Loading...')
5589      */
5590     msg : 'Loading...',
5591     /**
5592      * @cfg {String} msgCls
5593      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5594      */
5595     msgCls : 'x-mask-loading',
5596
5597     /**
5598      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5599      * @type Boolean
5600      */
5601     disabled: false,
5602
5603     /**
5604      * Disables the mask to prevent it from being displayed
5605      */
5606     disable : function(){
5607        this.disabled = true;
5608     },
5609
5610     /**
5611      * Enables the mask so that it can be displayed
5612      */
5613     enable : function(){
5614         this.disabled = false;
5615     },
5616     
5617     onLoadException : function()
5618     {
5619         Roo.log(arguments);
5620         
5621         if (typeof(arguments[3]) != 'undefined') {
5622             Roo.MessageBox.alert("Error loading",arguments[3]);
5623         } 
5624         /*
5625         try {
5626             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5627                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5628             }   
5629         } catch(e) {
5630             
5631         }
5632         */
5633     
5634         
5635         
5636         this.el.unmask(this.removeMask);
5637     },
5638     // private
5639     onLoad : function()
5640     {
5641         this.el.unmask(this.removeMask);
5642     },
5643
5644     // private
5645     onBeforeLoad : function(){
5646         if(!this.disabled){
5647             this.el.mask(this.msg, this.msgCls);
5648         }
5649     },
5650
5651     // private
5652     destroy : function(){
5653         if(this.store){
5654             this.store.un('beforeload', this.onBeforeLoad, this);
5655             this.store.un('load', this.onLoad, this);
5656             this.store.un('loadexception', this.onLoadException, this);
5657         }else{
5658             var um = this.el.getUpdateManager();
5659             um.un('beforeupdate', this.onBeforeLoad, this);
5660             um.un('update', this.onLoad, this);
5661             um.un('failure', this.onLoad, this);
5662         }
5663     }
5664 };/*
5665  * - LGPL
5666  *
5667  * table
5668  * 
5669  */
5670
5671 /**
5672  * @class Roo.bootstrap.Table
5673  * @extends Roo.bootstrap.Component
5674  * Bootstrap Table class
5675  * @cfg {String} cls table class
5676  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5677  * @cfg {String} bgcolor Specifies the background color for a table
5678  * @cfg {Number} border Specifies whether the table cells should have borders or not
5679  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5680  * @cfg {Number} cellspacing Specifies the space between cells
5681  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5682  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5683  * @cfg {String} sortable Specifies that the table should be sortable
5684  * @cfg {String} summary Specifies a summary of the content of a table
5685  * @cfg {Number} width Specifies the width of a table
5686  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5687  * 
5688  * @cfg {boolean} striped Should the rows be alternative striped
5689  * @cfg {boolean} bordered Add borders to the table
5690  * @cfg {boolean} hover Add hover highlighting
5691  * @cfg {boolean} condensed Format condensed
5692  * @cfg {boolean} responsive Format condensed
5693  * @cfg {Boolean} loadMask (true|false) default false
5694  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5695  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5696  * @cfg {Boolean} rowSelection (true|false) default false
5697  * @cfg {Boolean} cellSelection (true|false) default false
5698  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5699  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5700  
5701  * 
5702  * @constructor
5703  * Create a new Table
5704  * @param {Object} config The config object
5705  */
5706
5707 Roo.bootstrap.Table = function(config){
5708     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5709     
5710   
5711     
5712     // BC...
5713     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5714     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5715     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5716     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5717     
5718     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5719     if (this.sm) {
5720         this.sm.grid = this;
5721         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5722         this.sm = this.selModel;
5723         this.sm.xmodule = this.xmodule || false;
5724     }
5725     
5726     if (this.cm && typeof(this.cm.config) == 'undefined') {
5727         this.colModel = new Roo.grid.ColumnModel(this.cm);
5728         this.cm = this.colModel;
5729         this.cm.xmodule = this.xmodule || false;
5730     }
5731     if (this.store) {
5732         this.store= Roo.factory(this.store, Roo.data);
5733         this.ds = this.store;
5734         this.ds.xmodule = this.xmodule || false;
5735          
5736     }
5737     if (this.footer && this.store) {
5738         this.footer.dataSource = this.ds;
5739         this.footer = Roo.factory(this.footer);
5740     }
5741     
5742     /** @private */
5743     this.addEvents({
5744         /**
5745          * @event cellclick
5746          * Fires when a cell is clicked
5747          * @param {Roo.bootstrap.Table} this
5748          * @param {Roo.Element} el
5749          * @param {Number} rowIndex
5750          * @param {Number} columnIndex
5751          * @param {Roo.EventObject} e
5752          */
5753         "cellclick" : true,
5754         /**
5755          * @event celldblclick
5756          * Fires when a cell is double clicked
5757          * @param {Roo.bootstrap.Table} this
5758          * @param {Roo.Element} el
5759          * @param {Number} rowIndex
5760          * @param {Number} columnIndex
5761          * @param {Roo.EventObject} e
5762          */
5763         "celldblclick" : true,
5764         /**
5765          * @event rowclick
5766          * Fires when a row is clicked
5767          * @param {Roo.bootstrap.Table} this
5768          * @param {Roo.Element} el
5769          * @param {Number} rowIndex
5770          * @param {Roo.EventObject} e
5771          */
5772         "rowclick" : true,
5773         /**
5774          * @event rowdblclick
5775          * Fires when a row is double clicked
5776          * @param {Roo.bootstrap.Table} this
5777          * @param {Roo.Element} el
5778          * @param {Number} rowIndex
5779          * @param {Roo.EventObject} e
5780          */
5781         "rowdblclick" : true,
5782         /**
5783          * @event mouseover
5784          * Fires when a mouseover occur
5785          * @param {Roo.bootstrap.Table} this
5786          * @param {Roo.Element} el
5787          * @param {Number} rowIndex
5788          * @param {Number} columnIndex
5789          * @param {Roo.EventObject} e
5790          */
5791         "mouseover" : true,
5792         /**
5793          * @event mouseout
5794          * Fires when a mouseout occur
5795          * @param {Roo.bootstrap.Table} this
5796          * @param {Roo.Element} el
5797          * @param {Number} rowIndex
5798          * @param {Number} columnIndex
5799          * @param {Roo.EventObject} e
5800          */
5801         "mouseout" : true,
5802         /**
5803          * @event rowclass
5804          * Fires when a row is rendered, so you can change add a style to it.
5805          * @param {Roo.bootstrap.Table} this
5806          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5807          */
5808         'rowclass' : true,
5809           /**
5810          * @event rowsrendered
5811          * Fires when all the  rows have been rendered
5812          * @param {Roo.bootstrap.Table} this
5813          */
5814         'rowsrendered' : true,
5815         /**
5816          * @event contextmenu
5817          * The raw contextmenu event for the entire grid.
5818          * @param {Roo.EventObject} e
5819          */
5820         "contextmenu" : true,
5821         /**
5822          * @event rowcontextmenu
5823          * Fires when a row is right clicked
5824          * @param {Roo.bootstrap.Table} this
5825          * @param {Number} rowIndex
5826          * @param {Roo.EventObject} e
5827          */
5828         "rowcontextmenu" : true,
5829         /**
5830          * @event cellcontextmenu
5831          * Fires when a cell is right clicked
5832          * @param {Roo.bootstrap.Table} this
5833          * @param {Number} rowIndex
5834          * @param {Number} cellIndex
5835          * @param {Roo.EventObject} e
5836          */
5837          "cellcontextmenu" : true,
5838          /**
5839          * @event headercontextmenu
5840          * Fires when a header is right clicked
5841          * @param {Roo.bootstrap.Table} this
5842          * @param {Number} columnIndex
5843          * @param {Roo.EventObject} e
5844          */
5845         "headercontextmenu" : true
5846     });
5847 };
5848
5849 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5850     
5851     cls: false,
5852     align: false,
5853     bgcolor: false,
5854     border: false,
5855     cellpadding: false,
5856     cellspacing: false,
5857     frame: false,
5858     rules: false,
5859     sortable: false,
5860     summary: false,
5861     width: false,
5862     striped : false,
5863     scrollBody : false,
5864     bordered: false,
5865     hover:  false,
5866     condensed : false,
5867     responsive : false,
5868     sm : false,
5869     cm : false,
5870     store : false,
5871     loadMask : false,
5872     footerShow : true,
5873     headerShow : true,
5874   
5875     rowSelection : false,
5876     cellSelection : false,
5877     layout : false,
5878     
5879     // Roo.Element - the tbody
5880     mainBody: false,
5881     // Roo.Element - thead element
5882     mainHead: false,
5883     
5884     container: false, // used by gridpanel...
5885     
5886     getAutoCreate : function()
5887     {
5888         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5889         
5890         cfg = {
5891             tag: 'table',
5892             cls : 'table',
5893             cn : []
5894         };
5895         if (this.scrollBody) {
5896             cfg.cls += ' table-body-fixed';
5897         }    
5898         if (this.striped) {
5899             cfg.cls += ' table-striped';
5900         }
5901         
5902         if (this.hover) {
5903             cfg.cls += ' table-hover';
5904         }
5905         if (this.bordered) {
5906             cfg.cls += ' table-bordered';
5907         }
5908         if (this.condensed) {
5909             cfg.cls += ' table-condensed';
5910         }
5911         if (this.responsive) {
5912             cfg.cls += ' table-responsive';
5913         }
5914         
5915         if (this.cls) {
5916             cfg.cls+=  ' ' +this.cls;
5917         }
5918         
5919         // this lot should be simplifed...
5920         
5921         if (this.align) {
5922             cfg.align=this.align;
5923         }
5924         if (this.bgcolor) {
5925             cfg.bgcolor=this.bgcolor;
5926         }
5927         if (this.border) {
5928             cfg.border=this.border;
5929         }
5930         if (this.cellpadding) {
5931             cfg.cellpadding=this.cellpadding;
5932         }
5933         if (this.cellspacing) {
5934             cfg.cellspacing=this.cellspacing;
5935         }
5936         if (this.frame) {
5937             cfg.frame=this.frame;
5938         }
5939         if (this.rules) {
5940             cfg.rules=this.rules;
5941         }
5942         if (this.sortable) {
5943             cfg.sortable=this.sortable;
5944         }
5945         if (this.summary) {
5946             cfg.summary=this.summary;
5947         }
5948         if (this.width) {
5949             cfg.width=this.width;
5950         }
5951         if (this.layout) {
5952             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5953         }
5954         
5955         if(this.store || this.cm){
5956             if(this.headerShow){
5957                 cfg.cn.push(this.renderHeader());
5958             }
5959             
5960             cfg.cn.push(this.renderBody());
5961             
5962             if(this.footerShow){
5963                 cfg.cn.push(this.renderFooter());
5964             }
5965             // where does this come from?
5966             //cfg.cls+=  ' TableGrid';
5967         }
5968         
5969         return { cn : [ cfg ] };
5970     },
5971     
5972     initEvents : function()
5973     {   
5974         if(!this.store || !this.cm){
5975             return;
5976         }
5977         if (this.selModel) {
5978             this.selModel.initEvents();
5979         }
5980         
5981         
5982         //Roo.log('initEvents with ds!!!!');
5983         
5984         this.mainBody = this.el.select('tbody', true).first();
5985         this.mainHead = this.el.select('thead', true).first();
5986         
5987         
5988         
5989         
5990         var _this = this;
5991         
5992         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5993             e.on('click', _this.sort, _this);
5994         });
5995         
5996         this.el.on("click", this.onClick, this);
5997         this.el.on("dblclick", this.onDblClick, this);
5998         
5999         // why is this done????? = it breaks dialogs??
6000         //this.parent().el.setStyle('position', 'relative');
6001         
6002         
6003         if (this.footer) {
6004             this.footer.parentId = this.id;
6005             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6006         }
6007         
6008         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6009         
6010         this.store.on('load', this.onLoad, this);
6011         this.store.on('beforeload', this.onBeforeLoad, this);
6012         this.store.on('update', this.onUpdate, this);
6013         this.store.on('add', this.onAdd, this);
6014         this.store.on("clear", this.clear, this);
6015         
6016         this.el.on("contextmenu", this.onContextMenu, this);
6017         
6018         this.mainBody.on('scroll', this.onBodyScroll, this);
6019         
6020         
6021     },
6022     
6023     onContextMenu : function(e, t)
6024     {
6025         this.processEvent("contextmenu", e);
6026     },
6027     
6028     processEvent : function(name, e)
6029     {
6030         if (name != 'touchstart' ) {
6031             this.fireEvent(name, e);    
6032         }
6033         
6034         var t = e.getTarget();
6035         
6036         var cell = Roo.get(t);
6037         
6038         if(!cell){
6039             return;
6040         }
6041         
6042         if(cell.findParent('tfoot', false, true)){
6043             return;
6044         }
6045         
6046         if(cell.findParent('thead', false, true)){
6047             
6048             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6049                 cell = Roo.get(t).findParent('th', false, true);
6050                 if (!cell) {
6051                     Roo.log("failed to find th in thead?");
6052                     Roo.log(e.getTarget());
6053                     return;
6054                 }
6055             }
6056             
6057             var cellIndex = cell.dom.cellIndex;
6058             
6059             var ename = name == 'touchstart' ? 'click' : name;
6060             this.fireEvent("header" + ename, this, cellIndex, e);
6061             
6062             return;
6063         }
6064         
6065         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6066             cell = Roo.get(t).findParent('td', false, true);
6067             if (!cell) {
6068                 Roo.log("failed to find th in tbody?");
6069                 Roo.log(e.getTarget());
6070                 return;
6071             }
6072         }
6073         
6074         var row = cell.findParent('tr', false, true);
6075         var cellIndex = cell.dom.cellIndex;
6076         var rowIndex = row.dom.rowIndex - 1;
6077         
6078         if(row !== false){
6079             
6080             this.fireEvent("row" + name, this, rowIndex, e);
6081             
6082             if(cell !== false){
6083             
6084                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6085             }
6086         }
6087         
6088     },
6089     
6090     onMouseover : function(e, el)
6091     {
6092         var cell = Roo.get(el);
6093         
6094         if(!cell){
6095             return;
6096         }
6097         
6098         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6099             cell = cell.findParent('td', false, true);
6100         }
6101         
6102         var row = cell.findParent('tr', false, true);
6103         var cellIndex = cell.dom.cellIndex;
6104         var rowIndex = row.dom.rowIndex - 1; // start from 0
6105         
6106         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6107         
6108     },
6109     
6110     onMouseout : function(e, el)
6111     {
6112         var cell = Roo.get(el);
6113         
6114         if(!cell){
6115             return;
6116         }
6117         
6118         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6119             cell = cell.findParent('td', false, true);
6120         }
6121         
6122         var row = cell.findParent('tr', false, true);
6123         var cellIndex = cell.dom.cellIndex;
6124         var rowIndex = row.dom.rowIndex - 1; // start from 0
6125         
6126         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6127         
6128     },
6129     
6130     onClick : function(e, el)
6131     {
6132         var cell = Roo.get(el);
6133         
6134         if(!cell || (!this.cellSelection && !this.rowSelection)){
6135             return;
6136         }
6137         
6138         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6139             cell = cell.findParent('td', false, true);
6140         }
6141         
6142         if(!cell || typeof(cell) == 'undefined'){
6143             return;
6144         }
6145         
6146         var row = cell.findParent('tr', false, true);
6147         
6148         if(!row || typeof(row) == 'undefined'){
6149             return;
6150         }
6151         
6152         var cellIndex = cell.dom.cellIndex;
6153         var rowIndex = this.getRowIndex(row);
6154         
6155         // why??? - should these not be based on SelectionModel?
6156         if(this.cellSelection){
6157             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6158         }
6159         
6160         if(this.rowSelection){
6161             this.fireEvent('rowclick', this, row, rowIndex, e);
6162         }
6163         
6164         
6165     },
6166         
6167     onDblClick : function(e,el)
6168     {
6169         var cell = Roo.get(el);
6170         
6171         if(!cell || (!this.cellSelection && !this.rowSelection)){
6172             return;
6173         }
6174         
6175         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6176             cell = cell.findParent('td', false, true);
6177         }
6178         
6179         if(!cell || typeof(cell) == 'undefined'){
6180             return;
6181         }
6182         
6183         var row = cell.findParent('tr', false, true);
6184         
6185         if(!row || typeof(row) == 'undefined'){
6186             return;
6187         }
6188         
6189         var cellIndex = cell.dom.cellIndex;
6190         var rowIndex = this.getRowIndex(row);
6191         
6192         if(this.cellSelection){
6193             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6194         }
6195         
6196         if(this.rowSelection){
6197             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6198         }
6199     },
6200     
6201     sort : function(e,el)
6202     {
6203         var col = Roo.get(el);
6204         
6205         if(!col.hasClass('sortable')){
6206             return;
6207         }
6208         
6209         var sort = col.attr('sort');
6210         var dir = 'ASC';
6211         
6212         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6213             dir = 'DESC';
6214         }
6215         
6216         this.store.sortInfo = {field : sort, direction : dir};
6217         
6218         if (this.footer) {
6219             Roo.log("calling footer first");
6220             this.footer.onClick('first');
6221         } else {
6222         
6223             this.store.load({ params : { start : 0 } });
6224         }
6225     },
6226     
6227     renderHeader : function()
6228     {
6229         var header = {
6230             tag: 'thead',
6231             cn : []
6232         };
6233         
6234         var cm = this.cm;
6235         this.totalWidth = 0;
6236         
6237         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6238             
6239             var config = cm.config[i];
6240             
6241             var c = {
6242                 tag: 'th',
6243                 style : '',
6244                 html: cm.getColumnHeader(i)
6245             };
6246             
6247             var hh = '';
6248             
6249             if(typeof(config.sortable) != 'undefined' && config.sortable){
6250                 c.cls = 'sortable';
6251                 c.html = '<i class="glyphicon"></i>' + c.html;
6252             }
6253             
6254             if(typeof(config.lgHeader) != 'undefined'){
6255                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6256             }
6257             
6258             if(typeof(config.mdHeader) != 'undefined'){
6259                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6260             }
6261             
6262             if(typeof(config.smHeader) != 'undefined'){
6263                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6264             }
6265             
6266             if(typeof(config.xsHeader) != 'undefined'){
6267                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6268             }
6269             
6270             if(hh.length){
6271                 c.html = hh;
6272             }
6273             
6274             if(typeof(config.tooltip) != 'undefined'){
6275                 c.tooltip = config.tooltip;
6276             }
6277             
6278             if(typeof(config.colspan) != 'undefined'){
6279                 c.colspan = config.colspan;
6280             }
6281             
6282             if(typeof(config.hidden) != 'undefined' && config.hidden){
6283                 c.style += ' display:none;';
6284             }
6285             
6286             if(typeof(config.dataIndex) != 'undefined'){
6287                 c.sort = config.dataIndex;
6288             }
6289             
6290            
6291             
6292             if(typeof(config.align) != 'undefined' && config.align.length){
6293                 c.style += ' text-align:' + config.align + ';';
6294             }
6295             
6296             if(typeof(config.width) != 'undefined'){
6297                 c.style += ' width:' + config.width + 'px;';
6298                 this.totalWidth += config.width;
6299             } else {
6300                 this.totalWidth += 100; // assume minimum of 100 per column?
6301             }
6302             
6303             if(typeof(config.cls) != 'undefined'){
6304                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6305             }
6306             
6307             ['xs','sm','md','lg'].map(function(size){
6308                 
6309                 if(typeof(config[size]) == 'undefined'){
6310                     return;
6311                 }
6312                 
6313                 if (!config[size]) { // 0 = hidden
6314                     c.cls += ' hidden-' + size;
6315                     return;
6316                 }
6317                 
6318                 c.cls += ' col-' + size + '-' + config[size];
6319
6320             });
6321             
6322             header.cn.push(c)
6323         }
6324         
6325         return header;
6326     },
6327     
6328     renderBody : function()
6329     {
6330         var body = {
6331             tag: 'tbody',
6332             cn : [
6333                 {
6334                     tag: 'tr',
6335                     cn : [
6336                         {
6337                             tag : 'td',
6338                             colspan :  this.cm.getColumnCount()
6339                         }
6340                     ]
6341                 }
6342             ]
6343         };
6344         
6345         return body;
6346     },
6347     
6348     renderFooter : function()
6349     {
6350         var footer = {
6351             tag: 'tfoot',
6352             cn : [
6353                 {
6354                     tag: 'tr',
6355                     cn : [
6356                         {
6357                             tag : 'td',
6358                             colspan :  this.cm.getColumnCount()
6359                         }
6360                     ]
6361                 }
6362             ]
6363         };
6364         
6365         return footer;
6366     },
6367     
6368     
6369     
6370     onLoad : function()
6371     {
6372 //        Roo.log('ds onload');
6373         this.clear();
6374         
6375         var _this = this;
6376         var cm = this.cm;
6377         var ds = this.store;
6378         
6379         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6380             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6381             if (_this.store.sortInfo) {
6382                     
6383                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6384                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6385                 }
6386                 
6387                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6388                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6389                 }
6390             }
6391         });
6392         
6393         var tbody =  this.mainBody;
6394               
6395         if(ds.getCount() > 0){
6396             ds.data.each(function(d,rowIndex){
6397                 var row =  this.renderRow(cm, ds, rowIndex);
6398                 
6399                 tbody.createChild(row);
6400                 
6401                 var _this = this;
6402                 
6403                 if(row.cellObjects.length){
6404                     Roo.each(row.cellObjects, function(r){
6405                         _this.renderCellObject(r);
6406                     })
6407                 }
6408                 
6409             }, this);
6410         }
6411         
6412         Roo.each(this.el.select('tbody td', true).elements, function(e){
6413             e.on('mouseover', _this.onMouseover, _this);
6414         });
6415         
6416         Roo.each(this.el.select('tbody td', true).elements, function(e){
6417             e.on('mouseout', _this.onMouseout, _this);
6418         });
6419         this.fireEvent('rowsrendered', this);
6420         //if(this.loadMask){
6421         //    this.maskEl.hide();
6422         //}
6423         
6424         this.autoSize();
6425     },
6426     
6427     
6428     onUpdate : function(ds,record)
6429     {
6430         this.refreshRow(record);
6431         this.autoSize();
6432     },
6433     
6434     onRemove : function(ds, record, index, isUpdate){
6435         if(isUpdate !== true){
6436             this.fireEvent("beforerowremoved", this, index, record);
6437         }
6438         var bt = this.mainBody.dom;
6439         
6440         var rows = this.el.select('tbody > tr', true).elements;
6441         
6442         if(typeof(rows[index]) != 'undefined'){
6443             bt.removeChild(rows[index].dom);
6444         }
6445         
6446 //        if(bt.rows[index]){
6447 //            bt.removeChild(bt.rows[index]);
6448 //        }
6449         
6450         if(isUpdate !== true){
6451             //this.stripeRows(index);
6452             //this.syncRowHeights(index, index);
6453             //this.layout();
6454             this.fireEvent("rowremoved", this, index, record);
6455         }
6456     },
6457     
6458     onAdd : function(ds, records, rowIndex)
6459     {
6460         //Roo.log('on Add called');
6461         // - note this does not handle multiple adding very well..
6462         var bt = this.mainBody.dom;
6463         for (var i =0 ; i < records.length;i++) {
6464             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6465             //Roo.log(records[i]);
6466             //Roo.log(this.store.getAt(rowIndex+i));
6467             this.insertRow(this.store, rowIndex + i, false);
6468             return;
6469         }
6470         
6471     },
6472     
6473     
6474     refreshRow : function(record){
6475         var ds = this.store, index;
6476         if(typeof record == 'number'){
6477             index = record;
6478             record = ds.getAt(index);
6479         }else{
6480             index = ds.indexOf(record);
6481         }
6482         this.insertRow(ds, index, true);
6483         this.autoSize();
6484         this.onRemove(ds, record, index+1, true);
6485         this.autoSize();
6486         //this.syncRowHeights(index, index);
6487         //this.layout();
6488         this.fireEvent("rowupdated", this, index, record);
6489     },
6490     
6491     insertRow : function(dm, rowIndex, isUpdate){
6492         
6493         if(!isUpdate){
6494             this.fireEvent("beforerowsinserted", this, rowIndex);
6495         }
6496             //var s = this.getScrollState();
6497         var row = this.renderRow(this.cm, this.store, rowIndex);
6498         // insert before rowIndex..
6499         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6500         
6501         var _this = this;
6502                 
6503         if(row.cellObjects.length){
6504             Roo.each(row.cellObjects, function(r){
6505                 _this.renderCellObject(r);
6506             })
6507         }
6508             
6509         if(!isUpdate){
6510             this.fireEvent("rowsinserted", this, rowIndex);
6511             //this.syncRowHeights(firstRow, lastRow);
6512             //this.stripeRows(firstRow);
6513             //this.layout();
6514         }
6515         
6516     },
6517     
6518     
6519     getRowDom : function(rowIndex)
6520     {
6521         var rows = this.el.select('tbody > tr', true).elements;
6522         
6523         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6524         
6525     },
6526     // returns the object tree for a tr..
6527   
6528     
6529     renderRow : function(cm, ds, rowIndex) 
6530     {
6531         
6532         var d = ds.getAt(rowIndex);
6533         
6534         var row = {
6535             tag : 'tr',
6536             cn : []
6537         };
6538             
6539         var cellObjects = [];
6540         
6541         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6542             var config = cm.config[i];
6543             
6544             var renderer = cm.getRenderer(i);
6545             var value = '';
6546             var id = false;
6547             
6548             if(typeof(renderer) !== 'undefined'){
6549                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6550             }
6551             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6552             // and are rendered into the cells after the row is rendered - using the id for the element.
6553             
6554             if(typeof(value) === 'object'){
6555                 id = Roo.id();
6556                 cellObjects.push({
6557                     container : id,
6558                     cfg : value 
6559                 })
6560             }
6561             
6562             var rowcfg = {
6563                 record: d,
6564                 rowIndex : rowIndex,
6565                 colIndex : i,
6566                 rowClass : ''
6567             };
6568
6569             this.fireEvent('rowclass', this, rowcfg);
6570             
6571             var td = {
6572                 tag: 'td',
6573                 cls : rowcfg.rowClass,
6574                 style: '',
6575                 html: (typeof(value) === 'object') ? '' : value
6576             };
6577             
6578             if (id) {
6579                 td.id = id;
6580             }
6581             
6582             if(typeof(config.colspan) != 'undefined'){
6583                 td.colspan = config.colspan;
6584             }
6585             
6586             if(typeof(config.hidden) != 'undefined' && config.hidden){
6587                 td.style += ' display:none;';
6588             }
6589             
6590             if(typeof(config.align) != 'undefined' && config.align.length){
6591                 td.style += ' text-align:' + config.align + ';';
6592             }
6593             
6594             if(typeof(config.width) != 'undefined'){
6595                 td.style += ' width:' +  config.width + 'px;';
6596             }
6597             
6598             if(typeof(config.cursor) != 'undefined'){
6599                 td.style += ' cursor:' +  config.cursor + ';';
6600             }
6601             
6602             if(typeof(config.cls) != 'undefined'){
6603                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6604             }
6605             
6606             ['xs','sm','md','lg'].map(function(size){
6607                 
6608                 if(typeof(config[size]) == 'undefined'){
6609                     return;
6610                 }
6611                 
6612                 if (!config[size]) { // 0 = hidden
6613                     td.cls += ' hidden-' + size;
6614                     return;
6615                 }
6616                 
6617                 td.cls += ' col-' + size + '-' + config[size];
6618
6619             });
6620              
6621             row.cn.push(td);
6622            
6623         }
6624         
6625         row.cellObjects = cellObjects;
6626         
6627         return row;
6628           
6629     },
6630     
6631     
6632     
6633     onBeforeLoad : function()
6634     {
6635         //Roo.log('ds onBeforeLoad');
6636         
6637         //this.clear();
6638         
6639         //if(this.loadMask){
6640         //    this.maskEl.show();
6641         //}
6642     },
6643      /**
6644      * Remove all rows
6645      */
6646     clear : function()
6647     {
6648         this.el.select('tbody', true).first().dom.innerHTML = '';
6649     },
6650     /**
6651      * Show or hide a row.
6652      * @param {Number} rowIndex to show or hide
6653      * @param {Boolean} state hide
6654      */
6655     setRowVisibility : function(rowIndex, state)
6656     {
6657         var bt = this.mainBody.dom;
6658         
6659         var rows = this.el.select('tbody > tr', true).elements;
6660         
6661         if(typeof(rows[rowIndex]) == 'undefined'){
6662             return;
6663         }
6664         rows[rowIndex].dom.style.display = state ? '' : 'none';
6665     },
6666     
6667     
6668     getSelectionModel : function(){
6669         if(!this.selModel){
6670             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6671         }
6672         return this.selModel;
6673     },
6674     /*
6675      * Render the Roo.bootstrap object from renderder
6676      */
6677     renderCellObject : function(r)
6678     {
6679         var _this = this;
6680         
6681         var t = r.cfg.render(r.container);
6682         
6683         if(r.cfg.cn){
6684             Roo.each(r.cfg.cn, function(c){
6685                 var child = {
6686                     container: t.getChildContainer(),
6687                     cfg: c
6688                 };
6689                 _this.renderCellObject(child);
6690             })
6691         }
6692     },
6693     
6694     getRowIndex : function(row)
6695     {
6696         var rowIndex = -1;
6697         
6698         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6699             if(el != row){
6700                 return;
6701             }
6702             
6703             rowIndex = index;
6704         });
6705         
6706         return rowIndex;
6707     },
6708      /**
6709      * Returns the grid's underlying element = used by panel.Grid
6710      * @return {Element} The element
6711      */
6712     getGridEl : function(){
6713         return this.el;
6714     },
6715      /**
6716      * Forces a resize - used by panel.Grid
6717      * @return {Element} The element
6718      */
6719     autoSize : function()
6720     {
6721         //var ctr = Roo.get(this.container.dom.parentElement);
6722         var ctr = Roo.get(this.el.dom);
6723         
6724         var thd = this.getGridEl().select('thead',true).first();
6725         var tbd = this.getGridEl().select('tbody', true).first();
6726         
6727         
6728         var cw = ctr.getWidth();
6729         
6730         if (tbd) {
6731             
6732             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6733             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6734             cw -= barsize;
6735         }
6736         cw = Math.max(cw, this.totalWidth);
6737         this.getGridEl().select('tr',true).setWidth(cw);
6738         // resize 'expandable coloumn?
6739         
6740         return; // we doe not have a view in this design..
6741         
6742     },
6743     onBodyScroll: function()
6744     {
6745         
6746         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6747         this.mainHead.setStyle({
6748                     'position' : 'relative',
6749                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6750         });
6751         
6752         
6753     }
6754 });
6755
6756  
6757
6758  /*
6759  * - LGPL
6760  *
6761  * table cell
6762  * 
6763  */
6764
6765 /**
6766  * @class Roo.bootstrap.TableCell
6767  * @extends Roo.bootstrap.Component
6768  * Bootstrap TableCell class
6769  * @cfg {String} html cell contain text
6770  * @cfg {String} cls cell class
6771  * @cfg {String} tag cell tag (td|th) default td
6772  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6773  * @cfg {String} align Aligns the content in a cell
6774  * @cfg {String} axis Categorizes cells
6775  * @cfg {String} bgcolor Specifies the background color of a cell
6776  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6777  * @cfg {Number} colspan Specifies the number of columns a cell should span
6778  * @cfg {String} headers Specifies one or more header cells a cell is related to
6779  * @cfg {Number} height Sets the height of a cell
6780  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6781  * @cfg {Number} rowspan Sets the number of rows a cell should span
6782  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6783  * @cfg {String} valign Vertical aligns the content in a cell
6784  * @cfg {Number} width Specifies the width of a cell
6785  * 
6786  * @constructor
6787  * Create a new TableCell
6788  * @param {Object} config The config object
6789  */
6790
6791 Roo.bootstrap.TableCell = function(config){
6792     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6793 };
6794
6795 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6796     
6797     html: false,
6798     cls: false,
6799     tag: false,
6800     abbr: false,
6801     align: false,
6802     axis: false,
6803     bgcolor: false,
6804     charoff: false,
6805     colspan: false,
6806     headers: false,
6807     height: false,
6808     nowrap: false,
6809     rowspan: false,
6810     scope: false,
6811     valign: false,
6812     width: false,
6813     
6814     
6815     getAutoCreate : function(){
6816         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6817         
6818         cfg = {
6819             tag: 'td'
6820         };
6821         
6822         if(this.tag){
6823             cfg.tag = this.tag;
6824         }
6825         
6826         if (this.html) {
6827             cfg.html=this.html
6828         }
6829         if (this.cls) {
6830             cfg.cls=this.cls
6831         }
6832         if (this.abbr) {
6833             cfg.abbr=this.abbr
6834         }
6835         if (this.align) {
6836             cfg.align=this.align
6837         }
6838         if (this.axis) {
6839             cfg.axis=this.axis
6840         }
6841         if (this.bgcolor) {
6842             cfg.bgcolor=this.bgcolor
6843         }
6844         if (this.charoff) {
6845             cfg.charoff=this.charoff
6846         }
6847         if (this.colspan) {
6848             cfg.colspan=this.colspan
6849         }
6850         if (this.headers) {
6851             cfg.headers=this.headers
6852         }
6853         if (this.height) {
6854             cfg.height=this.height
6855         }
6856         if (this.nowrap) {
6857             cfg.nowrap=this.nowrap
6858         }
6859         if (this.rowspan) {
6860             cfg.rowspan=this.rowspan
6861         }
6862         if (this.scope) {
6863             cfg.scope=this.scope
6864         }
6865         if (this.valign) {
6866             cfg.valign=this.valign
6867         }
6868         if (this.width) {
6869             cfg.width=this.width
6870         }
6871         
6872         
6873         return cfg;
6874     }
6875    
6876 });
6877
6878  
6879
6880  /*
6881  * - LGPL
6882  *
6883  * table row
6884  * 
6885  */
6886
6887 /**
6888  * @class Roo.bootstrap.TableRow
6889  * @extends Roo.bootstrap.Component
6890  * Bootstrap TableRow class
6891  * @cfg {String} cls row class
6892  * @cfg {String} align Aligns the content in a table row
6893  * @cfg {String} bgcolor Specifies a background color for a table row
6894  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6895  * @cfg {String} valign Vertical aligns the content in a table row
6896  * 
6897  * @constructor
6898  * Create a new TableRow
6899  * @param {Object} config The config object
6900  */
6901
6902 Roo.bootstrap.TableRow = function(config){
6903     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6904 };
6905
6906 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6907     
6908     cls: false,
6909     align: false,
6910     bgcolor: false,
6911     charoff: false,
6912     valign: false,
6913     
6914     getAutoCreate : function(){
6915         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6916         
6917         cfg = {
6918             tag: 'tr'
6919         };
6920             
6921         if(this.cls){
6922             cfg.cls = this.cls;
6923         }
6924         if(this.align){
6925             cfg.align = this.align;
6926         }
6927         if(this.bgcolor){
6928             cfg.bgcolor = this.bgcolor;
6929         }
6930         if(this.charoff){
6931             cfg.charoff = this.charoff;
6932         }
6933         if(this.valign){
6934             cfg.valign = this.valign;
6935         }
6936         
6937         return cfg;
6938     }
6939    
6940 });
6941
6942  
6943
6944  /*
6945  * - LGPL
6946  *
6947  * table body
6948  * 
6949  */
6950
6951 /**
6952  * @class Roo.bootstrap.TableBody
6953  * @extends Roo.bootstrap.Component
6954  * Bootstrap TableBody class
6955  * @cfg {String} cls element class
6956  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6957  * @cfg {String} align Aligns the content inside the element
6958  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6959  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6960  * 
6961  * @constructor
6962  * Create a new TableBody
6963  * @param {Object} config The config object
6964  */
6965
6966 Roo.bootstrap.TableBody = function(config){
6967     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6968 };
6969
6970 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6971     
6972     cls: false,
6973     tag: false,
6974     align: false,
6975     charoff: false,
6976     valign: false,
6977     
6978     getAutoCreate : function(){
6979         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6980         
6981         cfg = {
6982             tag: 'tbody'
6983         };
6984             
6985         if (this.cls) {
6986             cfg.cls=this.cls
6987         }
6988         if(this.tag){
6989             cfg.tag = this.tag;
6990         }
6991         
6992         if(this.align){
6993             cfg.align = this.align;
6994         }
6995         if(this.charoff){
6996             cfg.charoff = this.charoff;
6997         }
6998         if(this.valign){
6999             cfg.valign = this.valign;
7000         }
7001         
7002         return cfg;
7003     }
7004     
7005     
7006 //    initEvents : function()
7007 //    {
7008 //        
7009 //        if(!this.store){
7010 //            return;
7011 //        }
7012 //        
7013 //        this.store = Roo.factory(this.store, Roo.data);
7014 //        this.store.on('load', this.onLoad, this);
7015 //        
7016 //        this.store.load();
7017 //        
7018 //    },
7019 //    
7020 //    onLoad: function () 
7021 //    {   
7022 //        this.fireEvent('load', this);
7023 //    }
7024 //    
7025 //   
7026 });
7027
7028  
7029
7030  /*
7031  * Based on:
7032  * Ext JS Library 1.1.1
7033  * Copyright(c) 2006-2007, Ext JS, LLC.
7034  *
7035  * Originally Released Under LGPL - original licence link has changed is not relivant.
7036  *
7037  * Fork - LGPL
7038  * <script type="text/javascript">
7039  */
7040
7041 // as we use this in bootstrap.
7042 Roo.namespace('Roo.form');
7043  /**
7044  * @class Roo.form.Action
7045  * Internal Class used to handle form actions
7046  * @constructor
7047  * @param {Roo.form.BasicForm} el The form element or its id
7048  * @param {Object} config Configuration options
7049  */
7050
7051  
7052  
7053 // define the action interface
7054 Roo.form.Action = function(form, options){
7055     this.form = form;
7056     this.options = options || {};
7057 };
7058 /**
7059  * Client Validation Failed
7060  * @const 
7061  */
7062 Roo.form.Action.CLIENT_INVALID = 'client';
7063 /**
7064  * Server Validation Failed
7065  * @const 
7066  */
7067 Roo.form.Action.SERVER_INVALID = 'server';
7068  /**
7069  * Connect to Server Failed
7070  * @const 
7071  */
7072 Roo.form.Action.CONNECT_FAILURE = 'connect';
7073 /**
7074  * Reading Data from Server Failed
7075  * @const 
7076  */
7077 Roo.form.Action.LOAD_FAILURE = 'load';
7078
7079 Roo.form.Action.prototype = {
7080     type : 'default',
7081     failureType : undefined,
7082     response : undefined,
7083     result : undefined,
7084
7085     // interface method
7086     run : function(options){
7087
7088     },
7089
7090     // interface method
7091     success : function(response){
7092
7093     },
7094
7095     // interface method
7096     handleResponse : function(response){
7097
7098     },
7099
7100     // default connection failure
7101     failure : function(response){
7102         
7103         this.response = response;
7104         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7105         this.form.afterAction(this, false);
7106     },
7107
7108     processResponse : function(response){
7109         this.response = response;
7110         if(!response.responseText){
7111             return true;
7112         }
7113         this.result = this.handleResponse(response);
7114         return this.result;
7115     },
7116
7117     // utility functions used internally
7118     getUrl : function(appendParams){
7119         var url = this.options.url || this.form.url || this.form.el.dom.action;
7120         if(appendParams){
7121             var p = this.getParams();
7122             if(p){
7123                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7124             }
7125         }
7126         return url;
7127     },
7128
7129     getMethod : function(){
7130         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7131     },
7132
7133     getParams : function(){
7134         var bp = this.form.baseParams;
7135         var p = this.options.params;
7136         if(p){
7137             if(typeof p == "object"){
7138                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7139             }else if(typeof p == 'string' && bp){
7140                 p += '&' + Roo.urlEncode(bp);
7141             }
7142         }else if(bp){
7143             p = Roo.urlEncode(bp);
7144         }
7145         return p;
7146     },
7147
7148     createCallback : function(){
7149         return {
7150             success: this.success,
7151             failure: this.failure,
7152             scope: this,
7153             timeout: (this.form.timeout*1000),
7154             upload: this.form.fileUpload ? this.success : undefined
7155         };
7156     }
7157 };
7158
7159 Roo.form.Action.Submit = function(form, options){
7160     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7161 };
7162
7163 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7164     type : 'submit',
7165
7166     haveProgress : false,
7167     uploadComplete : false,
7168     
7169     // uploadProgress indicator.
7170     uploadProgress : function()
7171     {
7172         if (!this.form.progressUrl) {
7173             return;
7174         }
7175         
7176         if (!this.haveProgress) {
7177             Roo.MessageBox.progress("Uploading", "Uploading");
7178         }
7179         if (this.uploadComplete) {
7180            Roo.MessageBox.hide();
7181            return;
7182         }
7183         
7184         this.haveProgress = true;
7185    
7186         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7187         
7188         var c = new Roo.data.Connection();
7189         c.request({
7190             url : this.form.progressUrl,
7191             params: {
7192                 id : uid
7193             },
7194             method: 'GET',
7195             success : function(req){
7196                //console.log(data);
7197                 var rdata = false;
7198                 var edata;
7199                 try  {
7200                    rdata = Roo.decode(req.responseText)
7201                 } catch (e) {
7202                     Roo.log("Invalid data from server..");
7203                     Roo.log(edata);
7204                     return;
7205                 }
7206                 if (!rdata || !rdata.success) {
7207                     Roo.log(rdata);
7208                     Roo.MessageBox.alert(Roo.encode(rdata));
7209                     return;
7210                 }
7211                 var data = rdata.data;
7212                 
7213                 if (this.uploadComplete) {
7214                    Roo.MessageBox.hide();
7215                    return;
7216                 }
7217                    
7218                 if (data){
7219                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7220                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7221                     );
7222                 }
7223                 this.uploadProgress.defer(2000,this);
7224             },
7225        
7226             failure: function(data) {
7227                 Roo.log('progress url failed ');
7228                 Roo.log(data);
7229             },
7230             scope : this
7231         });
7232            
7233     },
7234     
7235     
7236     run : function()
7237     {
7238         // run get Values on the form, so it syncs any secondary forms.
7239         this.form.getValues();
7240         
7241         var o = this.options;
7242         var method = this.getMethod();
7243         var isPost = method == 'POST';
7244         if(o.clientValidation === false || this.form.isValid()){
7245             
7246             if (this.form.progressUrl) {
7247                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7248                     (new Date() * 1) + '' + Math.random());
7249                     
7250             } 
7251             
7252             
7253             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7254                 form:this.form.el.dom,
7255                 url:this.getUrl(!isPost),
7256                 method: method,
7257                 params:isPost ? this.getParams() : null,
7258                 isUpload: this.form.fileUpload
7259             }));
7260             
7261             this.uploadProgress();
7262
7263         }else if (o.clientValidation !== false){ // client validation failed
7264             this.failureType = Roo.form.Action.CLIENT_INVALID;
7265             this.form.afterAction(this, false);
7266         }
7267     },
7268
7269     success : function(response)
7270     {
7271         this.uploadComplete= true;
7272         if (this.haveProgress) {
7273             Roo.MessageBox.hide();
7274         }
7275         
7276         
7277         var result = this.processResponse(response);
7278         if(result === true || result.success){
7279             this.form.afterAction(this, true);
7280             return;
7281         }
7282         if(result.errors){
7283             this.form.markInvalid(result.errors);
7284             this.failureType = Roo.form.Action.SERVER_INVALID;
7285         }
7286         this.form.afterAction(this, false);
7287     },
7288     failure : function(response)
7289     {
7290         this.uploadComplete= true;
7291         if (this.haveProgress) {
7292             Roo.MessageBox.hide();
7293         }
7294         
7295         this.response = response;
7296         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7297         this.form.afterAction(this, false);
7298     },
7299     
7300     handleResponse : function(response){
7301         if(this.form.errorReader){
7302             var rs = this.form.errorReader.read(response);
7303             var errors = [];
7304             if(rs.records){
7305                 for(var i = 0, len = rs.records.length; i < len; i++) {
7306                     var r = rs.records[i];
7307                     errors[i] = r.data;
7308                 }
7309             }
7310             if(errors.length < 1){
7311                 errors = null;
7312             }
7313             return {
7314                 success : rs.success,
7315                 errors : errors
7316             };
7317         }
7318         var ret = false;
7319         try {
7320             ret = Roo.decode(response.responseText);
7321         } catch (e) {
7322             ret = {
7323                 success: false,
7324                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7325                 errors : []
7326             };
7327         }
7328         return ret;
7329         
7330     }
7331 });
7332
7333
7334 Roo.form.Action.Load = function(form, options){
7335     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7336     this.reader = this.form.reader;
7337 };
7338
7339 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7340     type : 'load',
7341
7342     run : function(){
7343         
7344         Roo.Ajax.request(Roo.apply(
7345                 this.createCallback(), {
7346                     method:this.getMethod(),
7347                     url:this.getUrl(false),
7348                     params:this.getParams()
7349         }));
7350     },
7351
7352     success : function(response){
7353         
7354         var result = this.processResponse(response);
7355         if(result === true || !result.success || !result.data){
7356             this.failureType = Roo.form.Action.LOAD_FAILURE;
7357             this.form.afterAction(this, false);
7358             return;
7359         }
7360         this.form.clearInvalid();
7361         this.form.setValues(result.data);
7362         this.form.afterAction(this, true);
7363     },
7364
7365     handleResponse : function(response){
7366         if(this.form.reader){
7367             var rs = this.form.reader.read(response);
7368             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7369             return {
7370                 success : rs.success,
7371                 data : data
7372             };
7373         }
7374         return Roo.decode(response.responseText);
7375     }
7376 });
7377
7378 Roo.form.Action.ACTION_TYPES = {
7379     'load' : Roo.form.Action.Load,
7380     'submit' : Roo.form.Action.Submit
7381 };/*
7382  * - LGPL
7383  *
7384  * form
7385  * 
7386  */
7387
7388 /**
7389  * @class Roo.bootstrap.Form
7390  * @extends Roo.bootstrap.Component
7391  * Bootstrap Form class
7392  * @cfg {String} method  GET | POST (default POST)
7393  * @cfg {String} labelAlign top | left (default top)
7394  * @cfg {String} align left  | right - for navbars
7395  * @cfg {Boolean} loadMask load mask when submit (default true)
7396
7397  * 
7398  * @constructor
7399  * Create a new Form
7400  * @param {Object} config The config object
7401  */
7402
7403
7404 Roo.bootstrap.Form = function(config){
7405     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7406     this.addEvents({
7407         /**
7408          * @event clientvalidation
7409          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7410          * @param {Form} this
7411          * @param {Boolean} valid true if the form has passed client-side validation
7412          */
7413         clientvalidation: true,
7414         /**
7415          * @event beforeaction
7416          * Fires before any action is performed. Return false to cancel the action.
7417          * @param {Form} this
7418          * @param {Action} action The action to be performed
7419          */
7420         beforeaction: true,
7421         /**
7422          * @event actionfailed
7423          * Fires when an action fails.
7424          * @param {Form} this
7425          * @param {Action} action The action that failed
7426          */
7427         actionfailed : true,
7428         /**
7429          * @event actioncomplete
7430          * Fires when an action is completed.
7431          * @param {Form} this
7432          * @param {Action} action The action that completed
7433          */
7434         actioncomplete : true
7435     });
7436     
7437 };
7438
7439 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7440       
7441      /**
7442      * @cfg {String} method
7443      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7444      */
7445     method : 'POST',
7446     /**
7447      * @cfg {String} url
7448      * The URL to use for form actions if one isn't supplied in the action options.
7449      */
7450     /**
7451      * @cfg {Boolean} fileUpload
7452      * Set to true if this form is a file upload.
7453      */
7454      
7455     /**
7456      * @cfg {Object} baseParams
7457      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7458      */
7459       
7460     /**
7461      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7462      */
7463     timeout: 30,
7464     /**
7465      * @cfg {Sting} align (left|right) for navbar forms
7466      */
7467     align : 'left',
7468
7469     // private
7470     activeAction : null,
7471  
7472     /**
7473      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7474      * element by passing it or its id or mask the form itself by passing in true.
7475      * @type Mixed
7476      */
7477     waitMsgTarget : false,
7478     
7479     loadMask : true,
7480     
7481     getAutoCreate : function(){
7482         
7483         var cfg = {
7484             tag: 'form',
7485             method : this.method || 'POST',
7486             id : this.id || Roo.id(),
7487             cls : ''
7488         };
7489         if (this.parent().xtype.match(/^Nav/)) {
7490             cfg.cls = 'navbar-form navbar-' + this.align;
7491             
7492         }
7493         
7494         if (this.labelAlign == 'left' ) {
7495             cfg.cls += ' form-horizontal';
7496         }
7497         
7498         
7499         return cfg;
7500     },
7501     initEvents : function()
7502     {
7503         this.el.on('submit', this.onSubmit, this);
7504         // this was added as random key presses on the form where triggering form submit.
7505         this.el.on('keypress', function(e) {
7506             if (e.getCharCode() != 13) {
7507                 return true;
7508             }
7509             // we might need to allow it for textareas.. and some other items.
7510             // check e.getTarget().
7511             
7512             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7513                 return true;
7514             }
7515         
7516             Roo.log("keypress blocked");
7517             
7518             e.preventDefault();
7519             return false;
7520         });
7521         
7522     },
7523     // private
7524     onSubmit : function(e){
7525         e.stopEvent();
7526     },
7527     
7528      /**
7529      * Returns true if client-side validation on the form is successful.
7530      * @return Boolean
7531      */
7532     isValid : function(){
7533         var items = this.getItems();
7534         var valid = true;
7535         items.each(function(f){
7536            if(!f.validate()){
7537                valid = false;
7538                
7539            }
7540         });
7541         return valid;
7542     },
7543     /**
7544      * Returns true if any fields in this form have changed since their original load.
7545      * @return Boolean
7546      */
7547     isDirty : function(){
7548         var dirty = false;
7549         var items = this.getItems();
7550         items.each(function(f){
7551            if(f.isDirty()){
7552                dirty = true;
7553                return false;
7554            }
7555            return true;
7556         });
7557         return dirty;
7558     },
7559      /**
7560      * Performs a predefined action (submit or load) or custom actions you define on this form.
7561      * @param {String} actionName The name of the action type
7562      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7563      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7564      * accept other config options):
7565      * <pre>
7566 Property          Type             Description
7567 ----------------  ---------------  ----------------------------------------------------------------------------------
7568 url               String           The url for the action (defaults to the form's url)
7569 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7570 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7571 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7572                                    validate the form on the client (defaults to false)
7573      * </pre>
7574      * @return {BasicForm} this
7575      */
7576     doAction : function(action, options){
7577         if(typeof action == 'string'){
7578             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7579         }
7580         if(this.fireEvent('beforeaction', this, action) !== false){
7581             this.beforeAction(action);
7582             action.run.defer(100, action);
7583         }
7584         return this;
7585     },
7586     
7587     // private
7588     beforeAction : function(action){
7589         var o = action.options;
7590         
7591         if(this.loadMask){
7592             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7593         }
7594         // not really supported yet.. ??
7595         
7596         //if(this.waitMsgTarget === true){
7597         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7598         //}else if(this.waitMsgTarget){
7599         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7600         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7601         //}else {
7602         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7603        // }
7604          
7605     },
7606
7607     // private
7608     afterAction : function(action, success){
7609         this.activeAction = null;
7610         var o = action.options;
7611         
7612         //if(this.waitMsgTarget === true){
7613             this.el.unmask();
7614         //}else if(this.waitMsgTarget){
7615         //    this.waitMsgTarget.unmask();
7616         //}else{
7617         //    Roo.MessageBox.updateProgress(1);
7618         //    Roo.MessageBox.hide();
7619        // }
7620         // 
7621         if(success){
7622             if(o.reset){
7623                 this.reset();
7624             }
7625             Roo.callback(o.success, o.scope, [this, action]);
7626             this.fireEvent('actioncomplete', this, action);
7627             
7628         }else{
7629             
7630             // failure condition..
7631             // we have a scenario where updates need confirming.
7632             // eg. if a locking scenario exists..
7633             // we look for { errors : { needs_confirm : true }} in the response.
7634             if (
7635                 (typeof(action.result) != 'undefined')  &&
7636                 (typeof(action.result.errors) != 'undefined')  &&
7637                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7638            ){
7639                 var _t = this;
7640                 Roo.log("not supported yet");
7641                  /*
7642                 
7643                 Roo.MessageBox.confirm(
7644                     "Change requires confirmation",
7645                     action.result.errorMsg,
7646                     function(r) {
7647                         if (r != 'yes') {
7648                             return;
7649                         }
7650                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7651                     }
7652                     
7653                 );
7654                 */
7655                 
7656                 
7657                 return;
7658             }
7659             
7660             Roo.callback(o.failure, o.scope, [this, action]);
7661             // show an error message if no failed handler is set..
7662             if (!this.hasListener('actionfailed')) {
7663                 Roo.log("need to add dialog support");
7664                 /*
7665                 Roo.MessageBox.alert("Error",
7666                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7667                         action.result.errorMsg :
7668                         "Saving Failed, please check your entries or try again"
7669                 );
7670                 */
7671             }
7672             
7673             this.fireEvent('actionfailed', this, action);
7674         }
7675         
7676     },
7677     /**
7678      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7679      * @param {String} id The value to search for
7680      * @return Field
7681      */
7682     findField : function(id){
7683         var items = this.getItems();
7684         var field = items.get(id);
7685         if(!field){
7686              items.each(function(f){
7687                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7688                     field = f;
7689                     return false;
7690                 }
7691                 return true;
7692             });
7693         }
7694         return field || null;
7695     },
7696      /**
7697      * Mark fields in this form invalid in bulk.
7698      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7699      * @return {BasicForm} this
7700      */
7701     markInvalid : function(errors){
7702         if(errors instanceof Array){
7703             for(var i = 0, len = errors.length; i < len; i++){
7704                 var fieldError = errors[i];
7705                 var f = this.findField(fieldError.id);
7706                 if(f){
7707                     f.markInvalid(fieldError.msg);
7708                 }
7709             }
7710         }else{
7711             var field, id;
7712             for(id in errors){
7713                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7714                     field.markInvalid(errors[id]);
7715                 }
7716             }
7717         }
7718         //Roo.each(this.childForms || [], function (f) {
7719         //    f.markInvalid(errors);
7720         //});
7721         
7722         return this;
7723     },
7724
7725     /**
7726      * Set values for fields in this form in bulk.
7727      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7728      * @return {BasicForm} this
7729      */
7730     setValues : function(values){
7731         if(values instanceof Array){ // array of objects
7732             for(var i = 0, len = values.length; i < len; i++){
7733                 var v = values[i];
7734                 var f = this.findField(v.id);
7735                 if(f){
7736                     f.setValue(v.value);
7737                     if(this.trackResetOnLoad){
7738                         f.originalValue = f.getValue();
7739                     }
7740                 }
7741             }
7742         }else{ // object hash
7743             var field, id;
7744             for(id in values){
7745                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7746                     
7747                     if (field.setFromData && 
7748                         field.valueField && 
7749                         field.displayField &&
7750                         // combos' with local stores can 
7751                         // be queried via setValue()
7752                         // to set their value..
7753                         (field.store && !field.store.isLocal)
7754                         ) {
7755                         // it's a combo
7756                         var sd = { };
7757                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7758                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7759                         field.setFromData(sd);
7760                         
7761                     } else {
7762                         field.setValue(values[id]);
7763                     }
7764                     
7765                     
7766                     if(this.trackResetOnLoad){
7767                         field.originalValue = field.getValue();
7768                     }
7769                 }
7770             }
7771         }
7772          
7773         //Roo.each(this.childForms || [], function (f) {
7774         //    f.setValues(values);
7775         //});
7776                 
7777         return this;
7778     },
7779
7780     /**
7781      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7782      * they are returned as an array.
7783      * @param {Boolean} asString
7784      * @return {Object}
7785      */
7786     getValues : function(asString){
7787         //if (this.childForms) {
7788             // copy values from the child forms
7789         //    Roo.each(this.childForms, function (f) {
7790         //        this.setValues(f.getValues());
7791         //    }, this);
7792         //}
7793         
7794         
7795         
7796         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7797         if(asString === true){
7798             return fs;
7799         }
7800         return Roo.urlDecode(fs);
7801     },
7802     
7803     /**
7804      * Returns the fields in this form as an object with key/value pairs. 
7805      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7806      * @return {Object}
7807      */
7808     getFieldValues : function(with_hidden)
7809     {
7810         var items = this.getItems();
7811         var ret = {};
7812         items.each(function(f){
7813             if (!f.getName()) {
7814                 return;
7815             }
7816             var v = f.getValue();
7817             if (f.inputType =='radio') {
7818                 if (typeof(ret[f.getName()]) == 'undefined') {
7819                     ret[f.getName()] = ''; // empty..
7820                 }
7821                 
7822                 if (!f.el.dom.checked) {
7823                     return;
7824                     
7825                 }
7826                 v = f.el.dom.value;
7827                 
7828             }
7829             
7830             // not sure if this supported any more..
7831             if ((typeof(v) == 'object') && f.getRawValue) {
7832                 v = f.getRawValue() ; // dates..
7833             }
7834             // combo boxes where name != hiddenName...
7835             if (f.name != f.getName()) {
7836                 ret[f.name] = f.getRawValue();
7837             }
7838             ret[f.getName()] = v;
7839         });
7840         
7841         return ret;
7842     },
7843
7844     /**
7845      * Clears all invalid messages in this form.
7846      * @return {BasicForm} this
7847      */
7848     clearInvalid : function(){
7849         var items = this.getItems();
7850         
7851         items.each(function(f){
7852            f.clearInvalid();
7853         });
7854         
7855         
7856         
7857         return this;
7858     },
7859
7860     /**
7861      * Resets this form.
7862      * @return {BasicForm} this
7863      */
7864     reset : function(){
7865         var items = this.getItems();
7866         items.each(function(f){
7867             f.reset();
7868         });
7869         
7870         Roo.each(this.childForms || [], function (f) {
7871             f.reset();
7872         });
7873        
7874         
7875         return this;
7876     },
7877     getItems : function()
7878     {
7879         var r=new Roo.util.MixedCollection(false, function(o){
7880             return o.id || (o.id = Roo.id());
7881         });
7882         var iter = function(el) {
7883             if (el.inputEl) {
7884                 r.add(el);
7885             }
7886             if (!el.items) {
7887                 return;
7888             }
7889             Roo.each(el.items,function(e) {
7890                 iter(e);
7891             });
7892             
7893             
7894         };
7895         
7896         iter(this);
7897         return r;
7898         
7899         
7900         
7901         
7902     }
7903     
7904 });
7905
7906  
7907 /*
7908  * Based on:
7909  * Ext JS Library 1.1.1
7910  * Copyright(c) 2006-2007, Ext JS, LLC.
7911  *
7912  * Originally Released Under LGPL - original licence link has changed is not relivant.
7913  *
7914  * Fork - LGPL
7915  * <script type="text/javascript">
7916  */
7917 /**
7918  * @class Roo.form.VTypes
7919  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7920  * @singleton
7921  */
7922 Roo.form.VTypes = function(){
7923     // closure these in so they are only created once.
7924     var alpha = /^[a-zA-Z_]+$/;
7925     var alphanum = /^[a-zA-Z0-9_]+$/;
7926     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7927     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7928
7929     // All these messages and functions are configurable
7930     return {
7931         /**
7932          * The function used to validate email addresses
7933          * @param {String} value The email address
7934          */
7935         'email' : function(v){
7936             return email.test(v);
7937         },
7938         /**
7939          * The error text to display when the email validation function returns false
7940          * @type String
7941          */
7942         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7943         /**
7944          * The keystroke filter mask to be applied on email input
7945          * @type RegExp
7946          */
7947         'emailMask' : /[a-z0-9_\.\-@]/i,
7948
7949         /**
7950          * The function used to validate URLs
7951          * @param {String} value The URL
7952          */
7953         'url' : function(v){
7954             return url.test(v);
7955         },
7956         /**
7957          * The error text to display when the url validation function returns false
7958          * @type String
7959          */
7960         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7961         
7962         /**
7963          * The function used to validate alpha values
7964          * @param {String} value The value
7965          */
7966         'alpha' : function(v){
7967             return alpha.test(v);
7968         },
7969         /**
7970          * The error text to display when the alpha validation function returns false
7971          * @type String
7972          */
7973         'alphaText' : 'This field should only contain letters and _',
7974         /**
7975          * The keystroke filter mask to be applied on alpha input
7976          * @type RegExp
7977          */
7978         'alphaMask' : /[a-z_]/i,
7979
7980         /**
7981          * The function used to validate alphanumeric values
7982          * @param {String} value The value
7983          */
7984         'alphanum' : function(v){
7985             return alphanum.test(v);
7986         },
7987         /**
7988          * The error text to display when the alphanumeric validation function returns false
7989          * @type String
7990          */
7991         'alphanumText' : 'This field should only contain letters, numbers and _',
7992         /**
7993          * The keystroke filter mask to be applied on alphanumeric input
7994          * @type RegExp
7995          */
7996         'alphanumMask' : /[a-z0-9_]/i
7997     };
7998 }();/*
7999  * - LGPL
8000  *
8001  * Input
8002  * 
8003  */
8004
8005 /**
8006  * @class Roo.bootstrap.Input
8007  * @extends Roo.bootstrap.Component
8008  * Bootstrap Input class
8009  * @cfg {Boolean} disabled is it disabled
8010  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8011  * @cfg {String} name name of the input
8012  * @cfg {string} fieldLabel - the label associated
8013  * @cfg {string} placeholder - placeholder to put in text.
8014  * @cfg {string}  before - input group add on before
8015  * @cfg {string} after - input group add on after
8016  * @cfg {string} size - (lg|sm) or leave empty..
8017  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8018  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8019  * @cfg {Number} md colspan out of 12 for computer-sized screens
8020  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8021  * @cfg {string} value default value of the input
8022  * @cfg {Number} labelWidth set the width of label (0-12)
8023  * @cfg {String} labelAlign (top|left)
8024  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8025  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8026  * @cfg {String} indicatorpos (left|right) default left
8027
8028  * @cfg {String} align (left|center|right) Default left
8029  * @cfg {Boolean} forceFeedback (true|false) Default false
8030  * 
8031  * 
8032  * 
8033  * 
8034  * @constructor
8035  * Create a new Input
8036  * @param {Object} config The config object
8037  */
8038
8039 Roo.bootstrap.Input = function(config){
8040     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8041    
8042         this.addEvents({
8043             /**
8044              * @event focus
8045              * Fires when this field receives input focus.
8046              * @param {Roo.form.Field} this
8047              */
8048             focus : true,
8049             /**
8050              * @event blur
8051              * Fires when this field loses input focus.
8052              * @param {Roo.form.Field} this
8053              */
8054             blur : true,
8055             /**
8056              * @event specialkey
8057              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8058              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8059              * @param {Roo.form.Field} this
8060              * @param {Roo.EventObject} e The event object
8061              */
8062             specialkey : true,
8063             /**
8064              * @event change
8065              * Fires just before the field blurs if the field value has changed.
8066              * @param {Roo.form.Field} this
8067              * @param {Mixed} newValue The new value
8068              * @param {Mixed} oldValue The original value
8069              */
8070             change : true,
8071             /**
8072              * @event invalid
8073              * Fires after the field has been marked as invalid.
8074              * @param {Roo.form.Field} this
8075              * @param {String} msg The validation message
8076              */
8077             invalid : true,
8078             /**
8079              * @event valid
8080              * Fires after the field has been validated with no errors.
8081              * @param {Roo.form.Field} this
8082              */
8083             valid : true,
8084              /**
8085              * @event keyup
8086              * Fires after the key up
8087              * @param {Roo.form.Field} this
8088              * @param {Roo.EventObject}  e The event Object
8089              */
8090             keyup : true
8091         });
8092 };
8093
8094 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8095      /**
8096      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8097       automatic validation (defaults to "keyup").
8098      */
8099     validationEvent : "keyup",
8100      /**
8101      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8102      */
8103     validateOnBlur : true,
8104     /**
8105      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8106      */
8107     validationDelay : 250,
8108      /**
8109      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8110      */
8111     focusClass : "x-form-focus",  // not needed???
8112     
8113        
8114     /**
8115      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8116      */
8117     invalidClass : "has-warning",
8118     
8119     /**
8120      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8121      */
8122     validClass : "has-success",
8123     
8124     /**
8125      * @cfg {Boolean} hasFeedback (true|false) default true
8126      */
8127     hasFeedback : true,
8128     
8129     /**
8130      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8131      */
8132     invalidFeedbackClass : "glyphicon-warning-sign",
8133     
8134     /**
8135      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8136      */
8137     validFeedbackClass : "glyphicon-ok",
8138     
8139     /**
8140      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8141      */
8142     selectOnFocus : false,
8143     
8144      /**
8145      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8146      */
8147     maskRe : null,
8148        /**
8149      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8150      */
8151     vtype : null,
8152     
8153       /**
8154      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8155      */
8156     disableKeyFilter : false,
8157     
8158        /**
8159      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8160      */
8161     disabled : false,
8162      /**
8163      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8164      */
8165     allowBlank : true,
8166     /**
8167      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8168      */
8169     blankText : "This field is required",
8170     
8171      /**
8172      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8173      */
8174     minLength : 0,
8175     /**
8176      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8177      */
8178     maxLength : Number.MAX_VALUE,
8179     /**
8180      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8181      */
8182     minLengthText : "The minimum length for this field is {0}",
8183     /**
8184      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8185      */
8186     maxLengthText : "The maximum length for this field is {0}",
8187   
8188     
8189     /**
8190      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8191      * If available, this function will be called only after the basic validators all return true, and will be passed the
8192      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8193      */
8194     validator : null,
8195     /**
8196      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8197      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8198      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8199      */
8200     regex : null,
8201     /**
8202      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8203      */
8204     regexText : "",
8205     
8206     autocomplete: false,
8207     
8208     
8209     fieldLabel : '',
8210     inputType : 'text',
8211     
8212     name : false,
8213     placeholder: false,
8214     before : false,
8215     after : false,
8216     size : false,
8217     hasFocus : false,
8218     preventMark: false,
8219     isFormField : true,
8220     value : '',
8221     labelWidth : 2,
8222     labelAlign : false,
8223     readOnly : false,
8224     align : false,
8225     formatedValue : false,
8226     forceFeedback : false,
8227     
8228     indicatorpos : 'left',
8229     
8230     parentLabelAlign : function()
8231     {
8232         var parent = this;
8233         while (parent.parent()) {
8234             parent = parent.parent();
8235             if (typeof(parent.labelAlign) !='undefined') {
8236                 return parent.labelAlign;
8237             }
8238         }
8239         return 'left';
8240         
8241     },
8242     
8243     getAutoCreate : function()
8244     {
8245         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8246         
8247         var id = Roo.id();
8248         
8249         var cfg = {};
8250         
8251         if(this.inputType != 'hidden'){
8252             cfg.cls = 'form-group' //input-group
8253         }
8254         
8255         var input =  {
8256             tag: 'input',
8257             id : id,
8258             type : this.inputType,
8259             value : this.value,
8260             cls : 'form-control',
8261             placeholder : this.placeholder || '',
8262             autocomplete : this.autocomplete || 'new-password'
8263         };
8264         
8265         if(this.align){
8266             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8267         }
8268         
8269         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8270             input.maxLength = this.maxLength;
8271         }
8272         
8273         if (this.disabled) {
8274             input.disabled=true;
8275         }
8276         
8277         if (this.readOnly) {
8278             input.readonly=true;
8279         }
8280         
8281         if (this.name) {
8282             input.name = this.name;
8283         }
8284         
8285         if (this.size) {
8286             input.cls += ' input-' + this.size;
8287         }
8288         
8289         var settings=this;
8290         ['xs','sm','md','lg'].map(function(size){
8291             if (settings[size]) {
8292                 cfg.cls += ' col-' + size + '-' + settings[size];
8293             }
8294         });
8295         
8296         var inputblock = input;
8297         
8298         var feedback = {
8299             tag: 'span',
8300             cls: 'glyphicon form-control-feedback'
8301         };
8302             
8303         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8304             
8305             inputblock = {
8306                 cls : 'has-feedback',
8307                 cn :  [
8308                     input,
8309                     feedback
8310                 ] 
8311             };  
8312         }
8313         
8314         if (this.before || this.after) {
8315             
8316             inputblock = {
8317                 cls : 'input-group',
8318                 cn :  [] 
8319             };
8320             
8321             if (this.before && typeof(this.before) == 'string') {
8322                 
8323                 inputblock.cn.push({
8324                     tag :'span',
8325                     cls : 'roo-input-before input-group-addon',
8326                     html : this.before
8327                 });
8328             }
8329             if (this.before && typeof(this.before) == 'object') {
8330                 this.before = Roo.factory(this.before);
8331                 
8332                 inputblock.cn.push({
8333                     tag :'span',
8334                     cls : 'roo-input-before input-group-' +
8335                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8336                 });
8337             }
8338             
8339             inputblock.cn.push(input);
8340             
8341             if (this.after && typeof(this.after) == 'string') {
8342                 inputblock.cn.push({
8343                     tag :'span',
8344                     cls : 'roo-input-after input-group-addon',
8345                     html : this.after
8346                 });
8347             }
8348             if (this.after && typeof(this.after) == 'object') {
8349                 this.after = Roo.factory(this.after);
8350                 
8351                 inputblock.cn.push({
8352                     tag :'span',
8353                     cls : 'roo-input-after input-group-' +
8354                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8355                 });
8356             }
8357             
8358             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8359                 inputblock.cls += ' has-feedback';
8360                 inputblock.cn.push(feedback);
8361             }
8362         };
8363         
8364         if (align ==='left' && this.fieldLabel.length) {
8365             
8366             cfg.cn = [
8367                 {
8368                     tag : 'i',
8369                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8370                     tooltip : 'This field is required'
8371                 },
8372                 {
8373                     tag: 'label',
8374                     'for' :  id,
8375                     cls : 'control-label col-sm-' + this.labelWidth,
8376                     html : this.fieldLabel
8377
8378                 },
8379                 {
8380                     cls : "col-sm-" + (12 - this.labelWidth), 
8381                     cn: [
8382                         inputblock
8383                     ]
8384                 }
8385
8386             ];
8387             
8388             if(this.indicatorpos == 'right'){
8389                 cfg.cn = [
8390                     {
8391                         tag: 'label',
8392                         'for' :  id,
8393                         cls : 'control-label col-sm-' + this.labelWidth,
8394                         html : this.fieldLabel
8395
8396                     },
8397                     {
8398                         tag : 'i',
8399                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8400                         tooltip : 'This field is required'
8401                     },
8402                     {
8403                         cls : "col-sm-" + (12 - this.labelWidth), 
8404                         cn: [
8405                             inputblock
8406                         ]
8407                     }
8408
8409                 ];
8410             }
8411             
8412         } else if ( this.fieldLabel.length) {
8413                 
8414             cfg.cn = [
8415                 {
8416                     tag : 'i',
8417                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8418                     tooltip : 'This field is required'
8419                 },
8420                 {
8421                     tag: 'label',
8422                    //cls : 'input-group-addon',
8423                     html : this.fieldLabel
8424
8425                 },
8426
8427                inputblock
8428
8429            ];
8430            
8431            if(this.indicatorpos == 'right'){
8432                 
8433                 cfg.cn = [
8434                     {
8435                         tag: 'label',
8436                        //cls : 'input-group-addon',
8437                         html : this.fieldLabel
8438
8439                     },
8440                     {
8441                         tag : 'i',
8442                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8443                         tooltip : 'This field is required'
8444                     },
8445
8446                    inputblock
8447
8448                ];
8449
8450             }
8451
8452         } else {
8453             
8454             cfg.cn = [
8455
8456                     inputblock
8457
8458             ];
8459                 
8460                 
8461         };
8462         
8463         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8464            cfg.cls += ' navbar-form';
8465         }
8466         
8467         if (this.parentType === 'NavGroup') {
8468            cfg.cls += ' navbar-form';
8469            cfg.tag = 'li';
8470         }
8471         
8472         return cfg;
8473         
8474     },
8475     /**
8476      * return the real input element.
8477      */
8478     inputEl: function ()
8479     {
8480         return this.el.select('input.form-control',true).first();
8481     },
8482     
8483     tooltipEl : function()
8484     {
8485         return this.inputEl();
8486     },
8487     
8488     indicatorEl : function()
8489     {
8490         var indicator = this.el.select('i.roo-required-indicator',true).first();
8491         
8492         if(!indicator){
8493             return false;
8494         }
8495         
8496         return indicator;
8497         
8498     },
8499     
8500     setDisabled : function(v)
8501     {
8502         var i  = this.inputEl().dom;
8503         if (!v) {
8504             i.removeAttribute('disabled');
8505             return;
8506             
8507         }
8508         i.setAttribute('disabled','true');
8509     },
8510     initEvents : function()
8511     {
8512           
8513         this.inputEl().on("keydown" , this.fireKey,  this);
8514         this.inputEl().on("focus", this.onFocus,  this);
8515         this.inputEl().on("blur", this.onBlur,  this);
8516         
8517         this.inputEl().relayEvent('keyup', this);
8518         
8519         this.indicator = this.indicatorEl();
8520         
8521         if(this.indicator){
8522             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8523             this.indicator.hide();
8524         }
8525  
8526         // reference to original value for reset
8527         this.originalValue = this.getValue();
8528         //Roo.form.TextField.superclass.initEvents.call(this);
8529         if(this.validationEvent == 'keyup'){
8530             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8531             this.inputEl().on('keyup', this.filterValidation, this);
8532         }
8533         else if(this.validationEvent !== false){
8534             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8535         }
8536         
8537         if(this.selectOnFocus){
8538             this.on("focus", this.preFocus, this);
8539             
8540         }
8541         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8542             this.inputEl().on("keypress", this.filterKeys, this);
8543         } else {
8544             this.inputEl().relayEvent('keypress', this);
8545         }
8546        /* if(this.grow){
8547             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8548             this.el.on("click", this.autoSize,  this);
8549         }
8550         */
8551         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8552             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8553         }
8554         
8555         if (typeof(this.before) == 'object') {
8556             this.before.render(this.el.select('.roo-input-before',true).first());
8557         }
8558         if (typeof(this.after) == 'object') {
8559             this.after.render(this.el.select('.roo-input-after',true).first());
8560         }
8561         
8562         
8563     },
8564     filterValidation : function(e){
8565         if(!e.isNavKeyPress()){
8566             this.validationTask.delay(this.validationDelay);
8567         }
8568     },
8569      /**
8570      * Validates the field value
8571      * @return {Boolean} True if the value is valid, else false
8572      */
8573     validate : function(){
8574         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8575         if(this.disabled || this.validateValue(this.getRawValue())){
8576             this.markValid();
8577             return true;
8578         }
8579         
8580         this.markInvalid();
8581         return false;
8582     },
8583     
8584     
8585     /**
8586      * Validates a value according to the field's validation rules and marks the field as invalid
8587      * if the validation fails
8588      * @param {Mixed} value The value to validate
8589      * @return {Boolean} True if the value is valid, else false
8590      */
8591     validateValue : function(value){
8592         if(value.length < 1)  { // if it's blank
8593             if(this.allowBlank){
8594                 return true;
8595             }
8596             return false;
8597         }
8598         
8599         if(value.length < this.minLength){
8600             return false;
8601         }
8602         if(value.length > this.maxLength){
8603             return false;
8604         }
8605         if(this.vtype){
8606             var vt = Roo.form.VTypes;
8607             if(!vt[this.vtype](value, this)){
8608                 return false;
8609             }
8610         }
8611         if(typeof this.validator == "function"){
8612             var msg = this.validator(value);
8613             if(msg !== true){
8614                 return false;
8615             }
8616         }
8617         
8618         if(this.regex && !this.regex.test(value)){
8619             return false;
8620         }
8621         
8622         return true;
8623     },
8624
8625     
8626     
8627      // private
8628     fireKey : function(e){
8629         //Roo.log('field ' + e.getKey());
8630         if(e.isNavKeyPress()){
8631             this.fireEvent("specialkey", this, e);
8632         }
8633     },
8634     focus : function (selectText){
8635         if(this.rendered){
8636             this.inputEl().focus();
8637             if(selectText === true){
8638                 this.inputEl().dom.select();
8639             }
8640         }
8641         return this;
8642     } ,
8643     
8644     onFocus : function(){
8645         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8646            // this.el.addClass(this.focusClass);
8647         }
8648         if(!this.hasFocus){
8649             this.hasFocus = true;
8650             this.startValue = this.getValue();
8651             this.fireEvent("focus", this);
8652         }
8653     },
8654     
8655     beforeBlur : Roo.emptyFn,
8656
8657     
8658     // private
8659     onBlur : function(){
8660         this.beforeBlur();
8661         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8662             //this.el.removeClass(this.focusClass);
8663         }
8664         this.hasFocus = false;
8665         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8666             this.validate();
8667         }
8668         var v = this.getValue();
8669         if(String(v) !== String(this.startValue)){
8670             this.fireEvent('change', this, v, this.startValue);
8671         }
8672         this.fireEvent("blur", this);
8673     },
8674     
8675     /**
8676      * Resets the current field value to the originally loaded value and clears any validation messages
8677      */
8678     reset : function(){
8679         this.setValue(this.originalValue);
8680         this.validate();
8681     },
8682      /**
8683      * Returns the name of the field
8684      * @return {Mixed} name The name field
8685      */
8686     getName: function(){
8687         return this.name;
8688     },
8689      /**
8690      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8691      * @return {Mixed} value The field value
8692      */
8693     getValue : function(){
8694         
8695         var v = this.inputEl().getValue();
8696         
8697         return v;
8698     },
8699     /**
8700      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8701      * @return {Mixed} value The field value
8702      */
8703     getRawValue : function(){
8704         var v = this.inputEl().getValue();
8705         
8706         return v;
8707     },
8708     
8709     /**
8710      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8711      * @param {Mixed} value The value to set
8712      */
8713     setRawValue : function(v){
8714         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8715     },
8716     
8717     selectText : function(start, end){
8718         var v = this.getRawValue();
8719         if(v.length > 0){
8720             start = start === undefined ? 0 : start;
8721             end = end === undefined ? v.length : end;
8722             var d = this.inputEl().dom;
8723             if(d.setSelectionRange){
8724                 d.setSelectionRange(start, end);
8725             }else if(d.createTextRange){
8726                 var range = d.createTextRange();
8727                 range.moveStart("character", start);
8728                 range.moveEnd("character", v.length-end);
8729                 range.select();
8730             }
8731         }
8732     },
8733     
8734     /**
8735      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8736      * @param {Mixed} value The value to set
8737      */
8738     setValue : function(v){
8739         this.value = v;
8740         if(this.rendered){
8741             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8742             this.validate();
8743         }
8744     },
8745     
8746     /*
8747     processValue : function(value){
8748         if(this.stripCharsRe){
8749             var newValue = value.replace(this.stripCharsRe, '');
8750             if(newValue !== value){
8751                 this.setRawValue(newValue);
8752                 return newValue;
8753             }
8754         }
8755         return value;
8756     },
8757   */
8758     preFocus : function(){
8759         
8760         if(this.selectOnFocus){
8761             this.inputEl().dom.select();
8762         }
8763     },
8764     filterKeys : function(e){
8765         var k = e.getKey();
8766         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8767             return;
8768         }
8769         var c = e.getCharCode(), cc = String.fromCharCode(c);
8770         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8771             return;
8772         }
8773         if(!this.maskRe.test(cc)){
8774             e.stopEvent();
8775         }
8776     },
8777      /**
8778      * Clear any invalid styles/messages for this field
8779      */
8780     clearInvalid : function(){
8781         
8782         if(!this.el || this.preventMark){ // not rendered
8783             return;
8784         }
8785         
8786         if(this.indicator){
8787             this.indicator.hide();
8788         }
8789         
8790         this.el.removeClass(this.invalidClass);
8791         
8792         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8793             
8794             var feedback = this.el.select('.form-control-feedback', true).first();
8795             
8796             if(feedback){
8797                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8798             }
8799             
8800         }
8801         
8802         this.fireEvent('valid', this);
8803     },
8804     
8805      /**
8806      * Mark this field as valid
8807      */
8808     markValid : function()
8809     {
8810         if(!this.el  || this.preventMark){ // not rendered
8811             return;
8812         }
8813         
8814         this.el.removeClass([this.invalidClass, this.validClass]);
8815         
8816         var feedback = this.el.select('.form-control-feedback', true).first();
8817             
8818         if(feedback){
8819             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8820         }
8821
8822         if(this.disabled || this.allowBlank){
8823             return;
8824         }
8825         
8826         if(this.indicator){
8827             this.indicator.hide();
8828         }
8829         
8830         this.el.addClass(this.validClass);
8831         
8832         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8833             
8834             var feedback = this.el.select('.form-control-feedback', true).first();
8835             
8836             if(feedback){
8837                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8838                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8839             }
8840             
8841         }
8842         
8843         this.fireEvent('valid', this);
8844     },
8845     
8846      /**
8847      * Mark this field as invalid
8848      * @param {String} msg The validation message
8849      */
8850     markInvalid : function(msg)
8851     {
8852         if(!this.el  || this.preventMark){ // not rendered
8853             return;
8854         }
8855         
8856         this.el.removeClass([this.invalidClass, this.validClass]);
8857         
8858         var feedback = this.el.select('.form-control-feedback', true).first();
8859             
8860         if(feedback){
8861             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8862         }
8863
8864         if(this.disabled || this.allowBlank){
8865             return;
8866         }
8867         
8868         if(this.indicator){
8869             this.indicator.show();
8870         }
8871         
8872         this.el.addClass(this.invalidClass);
8873         
8874         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8875             
8876             var feedback = this.el.select('.form-control-feedback', true).first();
8877             
8878             if(feedback){
8879                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8880                 
8881                 if(this.getValue().length || this.forceFeedback){
8882                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8883                 }
8884                 
8885             }
8886             
8887         }
8888         
8889         this.fireEvent('invalid', this, msg);
8890     },
8891     // private
8892     SafariOnKeyDown : function(event)
8893     {
8894         // this is a workaround for a password hang bug on chrome/ webkit.
8895         
8896         var isSelectAll = false;
8897         
8898         if(this.inputEl().dom.selectionEnd > 0){
8899             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8900         }
8901         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8902             event.preventDefault();
8903             this.setValue('');
8904             return;
8905         }
8906         
8907         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8908             
8909             event.preventDefault();
8910             // this is very hacky as keydown always get's upper case.
8911             //
8912             var cc = String.fromCharCode(event.getCharCode());
8913             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8914             
8915         }
8916     },
8917     adjustWidth : function(tag, w){
8918         tag = tag.toLowerCase();
8919         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8920             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8921                 if(tag == 'input'){
8922                     return w + 2;
8923                 }
8924                 if(tag == 'textarea'){
8925                     return w-2;
8926                 }
8927             }else if(Roo.isOpera){
8928                 if(tag == 'input'){
8929                     return w + 2;
8930                 }
8931                 if(tag == 'textarea'){
8932                     return w-2;
8933                 }
8934             }
8935         }
8936         return w;
8937     }
8938     
8939 });
8940
8941  
8942 /*
8943  * - LGPL
8944  *
8945  * Input
8946  * 
8947  */
8948
8949 /**
8950  * @class Roo.bootstrap.TextArea
8951  * @extends Roo.bootstrap.Input
8952  * Bootstrap TextArea class
8953  * @cfg {Number} cols Specifies the visible width of a text area
8954  * @cfg {Number} rows Specifies the visible number of lines in a text area
8955  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8956  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8957  * @cfg {string} html text
8958  * 
8959  * @constructor
8960  * Create a new TextArea
8961  * @param {Object} config The config object
8962  */
8963
8964 Roo.bootstrap.TextArea = function(config){
8965     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8966    
8967 };
8968
8969 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8970      
8971     cols : false,
8972     rows : 5,
8973     readOnly : false,
8974     warp : 'soft',
8975     resize : false,
8976     value: false,
8977     html: false,
8978     
8979     getAutoCreate : function(){
8980         
8981         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8982         
8983         var id = Roo.id();
8984         
8985         var cfg = {};
8986         
8987         var input =  {
8988             tag: 'textarea',
8989             id : id,
8990             warp : this.warp,
8991             rows : this.rows,
8992             value : this.value || '',
8993             html: this.html || '',
8994             cls : 'form-control',
8995             placeholder : this.placeholder || '' 
8996             
8997         };
8998         
8999         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9000             input.maxLength = this.maxLength;
9001         }
9002         
9003         if(this.resize){
9004             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9005         }
9006         
9007         if(this.cols){
9008             input.cols = this.cols;
9009         }
9010         
9011         if (this.readOnly) {
9012             input.readonly = true;
9013         }
9014         
9015         if (this.name) {
9016             input.name = this.name;
9017         }
9018         
9019         if (this.size) {
9020             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9021         }
9022         
9023         var settings=this;
9024         ['xs','sm','md','lg'].map(function(size){
9025             if (settings[size]) {
9026                 cfg.cls += ' col-' + size + '-' + settings[size];
9027             }
9028         });
9029         
9030         var inputblock = input;
9031         
9032         if(this.hasFeedback && !this.allowBlank){
9033             
9034             var feedback = {
9035                 tag: 'span',
9036                 cls: 'glyphicon form-control-feedback'
9037             };
9038
9039             inputblock = {
9040                 cls : 'has-feedback',
9041                 cn :  [
9042                     input,
9043                     feedback
9044                 ] 
9045             };  
9046         }
9047         
9048         
9049         if (this.before || this.after) {
9050             
9051             inputblock = {
9052                 cls : 'input-group',
9053                 cn :  [] 
9054             };
9055             if (this.before) {
9056                 inputblock.cn.push({
9057                     tag :'span',
9058                     cls : 'input-group-addon',
9059                     html : this.before
9060                 });
9061             }
9062             
9063             inputblock.cn.push(input);
9064             
9065             if(this.hasFeedback && !this.allowBlank){
9066                 inputblock.cls += ' has-feedback';
9067                 inputblock.cn.push(feedback);
9068             }
9069             
9070             if (this.after) {
9071                 inputblock.cn.push({
9072                     tag :'span',
9073                     cls : 'input-group-addon',
9074                     html : this.after
9075                 });
9076             }
9077             
9078         }
9079         
9080         if (align ==='left' && this.fieldLabel.length) {
9081 //                Roo.log("left and has label");
9082                 cfg.cn = [
9083                     
9084                     {
9085                         tag: 'label',
9086                         'for' :  id,
9087                         cls : 'control-label col-sm-' + this.labelWidth,
9088                         html : this.fieldLabel
9089                         
9090                     },
9091                     {
9092                         cls : "col-sm-" + (12 - this.labelWidth), 
9093                         cn: [
9094                             inputblock
9095                         ]
9096                     }
9097                     
9098                 ];
9099         } else if ( this.fieldLabel.length) {
9100 //                Roo.log(" label");
9101                  cfg.cn = [
9102                    
9103                     {
9104                         tag: 'label',
9105                         //cls : 'input-group-addon',
9106                         html : this.fieldLabel
9107                         
9108                     },
9109                     
9110                     inputblock
9111                     
9112                 ];
9113
9114         } else {
9115             
9116 //                   Roo.log(" no label && no align");
9117                 cfg.cn = [
9118                     
9119                         inputblock
9120                     
9121                 ];
9122                 
9123                 
9124         }
9125         
9126         if (this.disabled) {
9127             input.disabled=true;
9128         }
9129         
9130         return cfg;
9131         
9132     },
9133     /**
9134      * return the real textarea element.
9135      */
9136     inputEl: function ()
9137     {
9138         return this.el.select('textarea.form-control',true).first();
9139     },
9140     
9141     /**
9142      * Clear any invalid styles/messages for this field
9143      */
9144     clearInvalid : function()
9145     {
9146         
9147         if(!this.el || this.preventMark){ // not rendered
9148             return;
9149         }
9150         
9151         var label = this.el.select('label', true).first();
9152         var icon = this.el.select('i.fa-star', true).first();
9153         
9154         if(label && icon){
9155             icon.remove();
9156         }
9157         
9158         this.el.removeClass(this.invalidClass);
9159         
9160         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9161             
9162             var feedback = this.el.select('.form-control-feedback', true).first();
9163             
9164             if(feedback){
9165                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9166             }
9167             
9168         }
9169         
9170         this.fireEvent('valid', this);
9171     },
9172     
9173      /**
9174      * Mark this field as valid
9175      */
9176     markValid : function()
9177     {
9178         if(!this.el  || this.preventMark){ // not rendered
9179             return;
9180         }
9181         
9182         this.el.removeClass([this.invalidClass, this.validClass]);
9183         
9184         var feedback = this.el.select('.form-control-feedback', true).first();
9185             
9186         if(feedback){
9187             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9188         }
9189
9190         if(this.disabled || this.allowBlank){
9191             return;
9192         }
9193         
9194         var label = this.el.select('label', true).first();
9195         var icon = this.el.select('i.fa-star', true).first();
9196         
9197         if(label && icon){
9198             icon.remove();
9199         }
9200         
9201         this.el.addClass(this.validClass);
9202         
9203         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9204             
9205             var feedback = this.el.select('.form-control-feedback', true).first();
9206             
9207             if(feedback){
9208                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9209                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9210             }
9211             
9212         }
9213         
9214         this.fireEvent('valid', this);
9215     },
9216     
9217      /**
9218      * Mark this field as invalid
9219      * @param {String} msg The validation message
9220      */
9221     markInvalid : function(msg)
9222     {
9223         if(!this.el  || this.preventMark){ // not rendered
9224             return;
9225         }
9226         
9227         this.el.removeClass([this.invalidClass, this.validClass]);
9228         
9229         var feedback = this.el.select('.form-control-feedback', true).first();
9230             
9231         if(feedback){
9232             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9233         }
9234
9235         if(this.disabled || this.allowBlank){
9236             return;
9237         }
9238         
9239         var label = this.el.select('label', true).first();
9240         var icon = this.el.select('i.fa-star', true).first();
9241         
9242         if(!this.getValue().length && label && !icon){
9243             this.el.createChild({
9244                 tag : 'i',
9245                 cls : 'text-danger fa fa-lg fa-star',
9246                 tooltip : 'This field is required',
9247                 style : 'margin-right:5px;'
9248             }, label, true);
9249         }
9250
9251         this.el.addClass(this.invalidClass);
9252         
9253         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9254             
9255             var feedback = this.el.select('.form-control-feedback', true).first();
9256             
9257             if(feedback){
9258                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9259                 
9260                 if(this.getValue().length || this.forceFeedback){
9261                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9262                 }
9263                 
9264             }
9265             
9266         }
9267         
9268         this.fireEvent('invalid', this, msg);
9269     }
9270 });
9271
9272  
9273 /*
9274  * - LGPL
9275  *
9276  * trigger field - base class for combo..
9277  * 
9278  */
9279  
9280 /**
9281  * @class Roo.bootstrap.TriggerField
9282  * @extends Roo.bootstrap.Input
9283  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9284  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9285  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9286  * for which you can provide a custom implementation.  For example:
9287  * <pre><code>
9288 var trigger = new Roo.bootstrap.TriggerField();
9289 trigger.onTriggerClick = myTriggerFn;
9290 trigger.applyTo('my-field');
9291 </code></pre>
9292  *
9293  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9294  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9295  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9296  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9297  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9298
9299  * @constructor
9300  * Create a new TriggerField.
9301  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9302  * to the base TextField)
9303  */
9304 Roo.bootstrap.TriggerField = function(config){
9305     this.mimicing = false;
9306     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9307 };
9308
9309 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9310     /**
9311      * @cfg {String} triggerClass A CSS class to apply to the trigger
9312      */
9313      /**
9314      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9315      */
9316     hideTrigger:false,
9317
9318     /**
9319      * @cfg {Boolean} removable (true|false) special filter default false
9320      */
9321     removable : false,
9322     
9323     /** @cfg {Boolean} grow @hide */
9324     /** @cfg {Number} growMin @hide */
9325     /** @cfg {Number} growMax @hide */
9326
9327     /**
9328      * @hide 
9329      * @method
9330      */
9331     autoSize: Roo.emptyFn,
9332     // private
9333     monitorTab : true,
9334     // private
9335     deferHeight : true,
9336
9337     
9338     actionMode : 'wrap',
9339     
9340     caret : false,
9341     
9342     
9343     getAutoCreate : function(){
9344        
9345         var align = this.labelAlign || this.parentLabelAlign();
9346         
9347         var id = Roo.id();
9348         
9349         var cfg = {
9350             cls: 'form-group' //input-group
9351         };
9352         
9353         
9354         var input =  {
9355             tag: 'input',
9356             id : id,
9357             type : this.inputType,
9358             cls : 'form-control',
9359             autocomplete: 'new-password',
9360             placeholder : this.placeholder || '' 
9361             
9362         };
9363         if (this.name) {
9364             input.name = this.name;
9365         }
9366         if (this.size) {
9367             input.cls += ' input-' + this.size;
9368         }
9369         
9370         if (this.disabled) {
9371             input.disabled=true;
9372         }
9373         
9374         var inputblock = input;
9375         
9376         if(this.hasFeedback && !this.allowBlank){
9377             
9378             var feedback = {
9379                 tag: 'span',
9380                 cls: 'glyphicon form-control-feedback'
9381             };
9382             
9383             if(this.removable && !this.editable && !this.tickable){
9384                 inputblock = {
9385                     cls : 'has-feedback',
9386                     cn :  [
9387                         inputblock,
9388                         {
9389                             tag: 'button',
9390                             html : 'x',
9391                             cls : 'roo-combo-removable-btn close'
9392                         },
9393                         feedback
9394                     ] 
9395                 };
9396             } else {
9397                 inputblock = {
9398                     cls : 'has-feedback',
9399                     cn :  [
9400                         inputblock,
9401                         feedback
9402                     ] 
9403                 };
9404             }
9405
9406         } else {
9407             if(this.removable && !this.editable && !this.tickable){
9408                 inputblock = {
9409                     cls : 'roo-removable',
9410                     cn :  [
9411                         inputblock,
9412                         {
9413                             tag: 'button',
9414                             html : 'x',
9415                             cls : 'roo-combo-removable-btn close'
9416                         }
9417                     ] 
9418                 };
9419             }
9420         }
9421         
9422         if (this.before || this.after) {
9423             
9424             inputblock = {
9425                 cls : 'input-group',
9426                 cn :  [] 
9427             };
9428             if (this.before) {
9429                 inputblock.cn.push({
9430                     tag :'span',
9431                     cls : 'input-group-addon',
9432                     html : this.before
9433                 });
9434             }
9435             
9436             inputblock.cn.push(input);
9437             
9438             if(this.hasFeedback && !this.allowBlank){
9439                 inputblock.cls += ' has-feedback';
9440                 inputblock.cn.push(feedback);
9441             }
9442             
9443             if (this.after) {
9444                 inputblock.cn.push({
9445                     tag :'span',
9446                     cls : 'input-group-addon',
9447                     html : this.after
9448                 });
9449             }
9450             
9451         };
9452         
9453         var box = {
9454             tag: 'div',
9455             cn: [
9456                 {
9457                     tag: 'input',
9458                     type : 'hidden',
9459                     cls: 'form-hidden-field'
9460                 },
9461                 inputblock
9462             ]
9463             
9464         };
9465         
9466         if(this.multiple){
9467             box = {
9468                 tag: 'div',
9469                 cn: [
9470                     {
9471                         tag: 'input',
9472                         type : 'hidden',
9473                         cls: 'form-hidden-field'
9474                     },
9475                     {
9476                         tag: 'ul',
9477                         cls: 'roo-select2-choices',
9478                         cn:[
9479                             {
9480                                 tag: 'li',
9481                                 cls: 'roo-select2-search-field',
9482                                 cn: [
9483
9484                                     inputblock
9485                                 ]
9486                             }
9487                         ]
9488                     }
9489                 ]
9490             }
9491         };
9492         
9493         var combobox = {
9494             cls: 'roo-select2-container input-group',
9495             cn: [
9496                 box
9497 //                {
9498 //                    tag: 'ul',
9499 //                    cls: 'typeahead typeahead-long dropdown-menu',
9500 //                    style: 'display:none'
9501 //                }
9502             ]
9503         };
9504         
9505         if(!this.multiple && this.showToggleBtn){
9506             
9507             var caret = {
9508                         tag: 'span',
9509                         cls: 'caret'
9510              };
9511             if (this.caret != false) {
9512                 caret = {
9513                      tag: 'i',
9514                      cls: 'fa fa-' + this.caret
9515                 };
9516                 
9517             }
9518             
9519             combobox.cn.push({
9520                 tag :'span',
9521                 cls : 'input-group-addon btn dropdown-toggle',
9522                 cn : [
9523                     caret,
9524                     {
9525                         tag: 'span',
9526                         cls: 'combobox-clear',
9527                         cn  : [
9528                             {
9529                                 tag : 'i',
9530                                 cls: 'icon-remove'
9531                             }
9532                         ]
9533                     }
9534                 ]
9535
9536             })
9537         }
9538         
9539         if(this.multiple){
9540             combobox.cls += ' roo-select2-container-multi';
9541         }
9542         
9543         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9544             
9545 //                Roo.log("left and has label");
9546             cfg.cn = [
9547                 {
9548                     tag : 'i',
9549                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9550                     tooltip : 'This field is required'
9551                 },
9552                 {
9553                     tag: 'label',
9554                     'for' :  id,
9555                     cls : 'control-label col-sm-' + this.labelWidth,
9556                     html : this.fieldLabel
9557
9558                 },
9559                 {
9560                     cls : "col-sm-" + (12 - this.labelWidth), 
9561                     cn: [
9562                         combobox
9563                     ]
9564                 }
9565
9566             ];
9567             
9568             if(this.indicatorpos == 'right'){
9569                 cfg.cn = [
9570                     {
9571                         tag: 'label',
9572                         'for' :  id,
9573                         cls : 'control-label col-sm-' + this.labelWidth,
9574                         html : this.fieldLabel
9575
9576                     },
9577                     {
9578                         tag : 'i',
9579                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9580                         tooltip : 'This field is required'
9581                     },
9582                     {
9583                         cls : "col-sm-" + (12 - this.labelWidth), 
9584                         cn: [
9585                             combobox
9586                         ]
9587                     }
9588
9589                 ];
9590             }
9591             
9592         } else if ( this.fieldLabel.length) {
9593 //                Roo.log(" label");
9594             cfg.cn = [
9595                 {
9596                    tag : 'i',
9597                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9598                    tooltip : 'This field is required'
9599                },
9600                {
9601                    tag: 'label',
9602                    //cls : 'input-group-addon',
9603                    html : this.fieldLabel
9604
9605                },
9606
9607                combobox
9608
9609             ];
9610             
9611             if(this.indicatorpos == 'right'){
9612                 
9613                 cfg.cn = [
9614                     {
9615                        tag: 'label',
9616                        //cls : 'input-group-addon',
9617                        html : this.fieldLabel
9618
9619                     },
9620                     {
9621                        tag : 'i',
9622                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9623                        tooltip : 'This field is required'
9624                     },
9625                     
9626                     combobox
9627
9628                 ];
9629
9630             }
9631
9632         } else {
9633             
9634 //                Roo.log(" no label && no align");
9635                 cfg = combobox
9636                      
9637                 
9638         }
9639          
9640         var settings=this;
9641         ['xs','sm','md','lg'].map(function(size){
9642             if (settings[size]) {
9643                 cfg.cls += ' col-' + size + '-' + settings[size];
9644             }
9645         });
9646         
9647         return cfg;
9648         
9649     },
9650     
9651     
9652     
9653     // private
9654     onResize : function(w, h){
9655 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9656 //        if(typeof w == 'number'){
9657 //            var x = w - this.trigger.getWidth();
9658 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9659 //            this.trigger.setStyle('left', x+'px');
9660 //        }
9661     },
9662
9663     // private
9664     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9665
9666     // private
9667     getResizeEl : function(){
9668         return this.inputEl();
9669     },
9670
9671     // private
9672     getPositionEl : function(){
9673         return this.inputEl();
9674     },
9675
9676     // private
9677     alignErrorIcon : function(){
9678         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9679     },
9680
9681     // private
9682     initEvents : function(){
9683         
9684         this.createList();
9685         
9686         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9687         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9688         if(!this.multiple && this.showToggleBtn){
9689             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9690             if(this.hideTrigger){
9691                 this.trigger.setDisplayed(false);
9692             }
9693             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9694         }
9695         
9696         if(this.multiple){
9697             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9698         }
9699         
9700         if(this.removable && !this.editable && !this.tickable){
9701             var close = this.closeTriggerEl();
9702             
9703             if(close){
9704                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9705                 close.on('click', this.removeBtnClick, this, close);
9706             }
9707         }
9708         
9709         //this.trigger.addClassOnOver('x-form-trigger-over');
9710         //this.trigger.addClassOnClick('x-form-trigger-click');
9711         
9712         //if(!this.width){
9713         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9714         //}
9715     },
9716     
9717     closeTriggerEl : function()
9718     {
9719         var close = this.el.select('.roo-combo-removable-btn', true).first();
9720         return close ? close : false;
9721     },
9722     
9723     removeBtnClick : function(e, h, el)
9724     {
9725         e.preventDefault();
9726         
9727         if(this.fireEvent("remove", this) !== false){
9728             this.reset();
9729             this.fireEvent("afterremove", this)
9730         }
9731     },
9732     
9733     createList : function()
9734     {
9735         this.list = Roo.get(document.body).createChild({
9736             tag: 'ul',
9737             cls: 'typeahead typeahead-long dropdown-menu',
9738             style: 'display:none'
9739         });
9740         
9741         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9742         
9743     },
9744
9745     // private
9746     initTrigger : function(){
9747        
9748     },
9749
9750     // private
9751     onDestroy : function(){
9752         if(this.trigger){
9753             this.trigger.removeAllListeners();
9754           //  this.trigger.remove();
9755         }
9756         //if(this.wrap){
9757         //    this.wrap.remove();
9758         //}
9759         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9760     },
9761
9762     // private
9763     onFocus : function(){
9764         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9765         /*
9766         if(!this.mimicing){
9767             this.wrap.addClass('x-trigger-wrap-focus');
9768             this.mimicing = true;
9769             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9770             if(this.monitorTab){
9771                 this.el.on("keydown", this.checkTab, this);
9772             }
9773         }
9774         */
9775     },
9776
9777     // private
9778     checkTab : function(e){
9779         if(e.getKey() == e.TAB){
9780             this.triggerBlur();
9781         }
9782     },
9783
9784     // private
9785     onBlur : function(){
9786         // do nothing
9787     },
9788
9789     // private
9790     mimicBlur : function(e, t){
9791         /*
9792         if(!this.wrap.contains(t) && this.validateBlur()){
9793             this.triggerBlur();
9794         }
9795         */
9796     },
9797
9798     // private
9799     triggerBlur : function(){
9800         this.mimicing = false;
9801         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9802         if(this.monitorTab){
9803             this.el.un("keydown", this.checkTab, this);
9804         }
9805         //this.wrap.removeClass('x-trigger-wrap-focus');
9806         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9807     },
9808
9809     // private
9810     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9811     validateBlur : function(e, t){
9812         return true;
9813     },
9814
9815     // private
9816     onDisable : function(){
9817         this.inputEl().dom.disabled = true;
9818         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9819         //if(this.wrap){
9820         //    this.wrap.addClass('x-item-disabled');
9821         //}
9822     },
9823
9824     // private
9825     onEnable : function(){
9826         this.inputEl().dom.disabled = false;
9827         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9828         //if(this.wrap){
9829         //    this.el.removeClass('x-item-disabled');
9830         //}
9831     },
9832
9833     // private
9834     onShow : function(){
9835         var ae = this.getActionEl();
9836         
9837         if(ae){
9838             ae.dom.style.display = '';
9839             ae.dom.style.visibility = 'visible';
9840         }
9841     },
9842
9843     // private
9844     
9845     onHide : function(){
9846         var ae = this.getActionEl();
9847         ae.dom.style.display = 'none';
9848     },
9849
9850     /**
9851      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9852      * by an implementing function.
9853      * @method
9854      * @param {EventObject} e
9855      */
9856     onTriggerClick : Roo.emptyFn
9857 });
9858  /*
9859  * Based on:
9860  * Ext JS Library 1.1.1
9861  * Copyright(c) 2006-2007, Ext JS, LLC.
9862  *
9863  * Originally Released Under LGPL - original licence link has changed is not relivant.
9864  *
9865  * Fork - LGPL
9866  * <script type="text/javascript">
9867  */
9868
9869
9870 /**
9871  * @class Roo.data.SortTypes
9872  * @singleton
9873  * Defines the default sorting (casting?) comparison functions used when sorting data.
9874  */
9875 Roo.data.SortTypes = {
9876     /**
9877      * Default sort that does nothing
9878      * @param {Mixed} s The value being converted
9879      * @return {Mixed} The comparison value
9880      */
9881     none : function(s){
9882         return s;
9883     },
9884     
9885     /**
9886      * The regular expression used to strip tags
9887      * @type {RegExp}
9888      * @property
9889      */
9890     stripTagsRE : /<\/?[^>]+>/gi,
9891     
9892     /**
9893      * Strips all HTML tags to sort on text only
9894      * @param {Mixed} s The value being converted
9895      * @return {String} The comparison value
9896      */
9897     asText : function(s){
9898         return String(s).replace(this.stripTagsRE, "");
9899     },
9900     
9901     /**
9902      * Strips all HTML tags to sort on text only - Case insensitive
9903      * @param {Mixed} s The value being converted
9904      * @return {String} The comparison value
9905      */
9906     asUCText : function(s){
9907         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9908     },
9909     
9910     /**
9911      * Case insensitive string
9912      * @param {Mixed} s The value being converted
9913      * @return {String} The comparison value
9914      */
9915     asUCString : function(s) {
9916         return String(s).toUpperCase();
9917     },
9918     
9919     /**
9920      * Date sorting
9921      * @param {Mixed} s The value being converted
9922      * @return {Number} The comparison value
9923      */
9924     asDate : function(s) {
9925         if(!s){
9926             return 0;
9927         }
9928         if(s instanceof Date){
9929             return s.getTime();
9930         }
9931         return Date.parse(String(s));
9932     },
9933     
9934     /**
9935      * Float sorting
9936      * @param {Mixed} s The value being converted
9937      * @return {Float} The comparison value
9938      */
9939     asFloat : function(s) {
9940         var val = parseFloat(String(s).replace(/,/g, ""));
9941         if(isNaN(val)) {
9942             val = 0;
9943         }
9944         return val;
9945     },
9946     
9947     /**
9948      * Integer sorting
9949      * @param {Mixed} s The value being converted
9950      * @return {Number} The comparison value
9951      */
9952     asInt : function(s) {
9953         var val = parseInt(String(s).replace(/,/g, ""));
9954         if(isNaN(val)) {
9955             val = 0;
9956         }
9957         return val;
9958     }
9959 };/*
9960  * Based on:
9961  * Ext JS Library 1.1.1
9962  * Copyright(c) 2006-2007, Ext JS, LLC.
9963  *
9964  * Originally Released Under LGPL - original licence link has changed is not relivant.
9965  *
9966  * Fork - LGPL
9967  * <script type="text/javascript">
9968  */
9969
9970 /**
9971 * @class Roo.data.Record
9972  * Instances of this class encapsulate both record <em>definition</em> information, and record
9973  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9974  * to access Records cached in an {@link Roo.data.Store} object.<br>
9975  * <p>
9976  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9977  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9978  * objects.<br>
9979  * <p>
9980  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9981  * @constructor
9982  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9983  * {@link #create}. The parameters are the same.
9984  * @param {Array} data An associative Array of data values keyed by the field name.
9985  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9986  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9987  * not specified an integer id is generated.
9988  */
9989 Roo.data.Record = function(data, id){
9990     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9991     this.data = data;
9992 };
9993
9994 /**
9995  * Generate a constructor for a specific record layout.
9996  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9997  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9998  * Each field definition object may contain the following properties: <ul>
9999  * <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,
10000  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10001  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10002  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10003  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10004  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10005  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10006  * this may be omitted.</p></li>
10007  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10008  * <ul><li>auto (Default, implies no conversion)</li>
10009  * <li>string</li>
10010  * <li>int</li>
10011  * <li>float</li>
10012  * <li>boolean</li>
10013  * <li>date</li></ul></p></li>
10014  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10015  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10016  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10017  * by the Reader into an object that will be stored in the Record. It is passed the
10018  * following parameters:<ul>
10019  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10020  * </ul></p></li>
10021  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10022  * </ul>
10023  * <br>usage:<br><pre><code>
10024 var TopicRecord = Roo.data.Record.create(
10025     {name: 'title', mapping: 'topic_title'},
10026     {name: 'author', mapping: 'username'},
10027     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10028     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10029     {name: 'lastPoster', mapping: 'user2'},
10030     {name: 'excerpt', mapping: 'post_text'}
10031 );
10032
10033 var myNewRecord = new TopicRecord({
10034     title: 'Do my job please',
10035     author: 'noobie',
10036     totalPosts: 1,
10037     lastPost: new Date(),
10038     lastPoster: 'Animal',
10039     excerpt: 'No way dude!'
10040 });
10041 myStore.add(myNewRecord);
10042 </code></pre>
10043  * @method create
10044  * @static
10045  */
10046 Roo.data.Record.create = function(o){
10047     var f = function(){
10048         f.superclass.constructor.apply(this, arguments);
10049     };
10050     Roo.extend(f, Roo.data.Record);
10051     var p = f.prototype;
10052     p.fields = new Roo.util.MixedCollection(false, function(field){
10053         return field.name;
10054     });
10055     for(var i = 0, len = o.length; i < len; i++){
10056         p.fields.add(new Roo.data.Field(o[i]));
10057     }
10058     f.getField = function(name){
10059         return p.fields.get(name);  
10060     };
10061     return f;
10062 };
10063
10064 Roo.data.Record.AUTO_ID = 1000;
10065 Roo.data.Record.EDIT = 'edit';
10066 Roo.data.Record.REJECT = 'reject';
10067 Roo.data.Record.COMMIT = 'commit';
10068
10069 Roo.data.Record.prototype = {
10070     /**
10071      * Readonly flag - true if this record has been modified.
10072      * @type Boolean
10073      */
10074     dirty : false,
10075     editing : false,
10076     error: null,
10077     modified: null,
10078
10079     // private
10080     join : function(store){
10081         this.store = store;
10082     },
10083
10084     /**
10085      * Set the named field to the specified value.
10086      * @param {String} name The name of the field to set.
10087      * @param {Object} value The value to set the field to.
10088      */
10089     set : function(name, value){
10090         if(this.data[name] == value){
10091             return;
10092         }
10093         this.dirty = true;
10094         if(!this.modified){
10095             this.modified = {};
10096         }
10097         if(typeof this.modified[name] == 'undefined'){
10098             this.modified[name] = this.data[name];
10099         }
10100         this.data[name] = value;
10101         if(!this.editing && this.store){
10102             this.store.afterEdit(this);
10103         }       
10104     },
10105
10106     /**
10107      * Get the value of the named field.
10108      * @param {String} name The name of the field to get the value of.
10109      * @return {Object} The value of the field.
10110      */
10111     get : function(name){
10112         return this.data[name]; 
10113     },
10114
10115     // private
10116     beginEdit : function(){
10117         this.editing = true;
10118         this.modified = {}; 
10119     },
10120
10121     // private
10122     cancelEdit : function(){
10123         this.editing = false;
10124         delete this.modified;
10125     },
10126
10127     // private
10128     endEdit : function(){
10129         this.editing = false;
10130         if(this.dirty && this.store){
10131             this.store.afterEdit(this);
10132         }
10133     },
10134
10135     /**
10136      * Usually called by the {@link Roo.data.Store} which owns the Record.
10137      * Rejects all changes made to the Record since either creation, or the last commit operation.
10138      * Modified fields are reverted to their original values.
10139      * <p>
10140      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10141      * of reject operations.
10142      */
10143     reject : function(){
10144         var m = this.modified;
10145         for(var n in m){
10146             if(typeof m[n] != "function"){
10147                 this.data[n] = m[n];
10148             }
10149         }
10150         this.dirty = false;
10151         delete this.modified;
10152         this.editing = false;
10153         if(this.store){
10154             this.store.afterReject(this);
10155         }
10156     },
10157
10158     /**
10159      * Usually called by the {@link Roo.data.Store} which owns the Record.
10160      * Commits all changes made to the Record since either creation, or the last commit operation.
10161      * <p>
10162      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10163      * of commit operations.
10164      */
10165     commit : function(){
10166         this.dirty = false;
10167         delete this.modified;
10168         this.editing = false;
10169         if(this.store){
10170             this.store.afterCommit(this);
10171         }
10172     },
10173
10174     // private
10175     hasError : function(){
10176         return this.error != null;
10177     },
10178
10179     // private
10180     clearError : function(){
10181         this.error = null;
10182     },
10183
10184     /**
10185      * Creates a copy of this record.
10186      * @param {String} id (optional) A new record id if you don't want to use this record's id
10187      * @return {Record}
10188      */
10189     copy : function(newId) {
10190         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10191     }
10192 };/*
10193  * Based on:
10194  * Ext JS Library 1.1.1
10195  * Copyright(c) 2006-2007, Ext JS, LLC.
10196  *
10197  * Originally Released Under LGPL - original licence link has changed is not relivant.
10198  *
10199  * Fork - LGPL
10200  * <script type="text/javascript">
10201  */
10202
10203
10204
10205 /**
10206  * @class Roo.data.Store
10207  * @extends Roo.util.Observable
10208  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10209  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10210  * <p>
10211  * 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
10212  * has no knowledge of the format of the data returned by the Proxy.<br>
10213  * <p>
10214  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10215  * instances from the data object. These records are cached and made available through accessor functions.
10216  * @constructor
10217  * Creates a new Store.
10218  * @param {Object} config A config object containing the objects needed for the Store to access data,
10219  * and read the data into Records.
10220  */
10221 Roo.data.Store = function(config){
10222     this.data = new Roo.util.MixedCollection(false);
10223     this.data.getKey = function(o){
10224         return o.id;
10225     };
10226     this.baseParams = {};
10227     // private
10228     this.paramNames = {
10229         "start" : "start",
10230         "limit" : "limit",
10231         "sort" : "sort",
10232         "dir" : "dir",
10233         "multisort" : "_multisort"
10234     };
10235
10236     if(config && config.data){
10237         this.inlineData = config.data;
10238         delete config.data;
10239     }
10240
10241     Roo.apply(this, config);
10242     
10243     if(this.reader){ // reader passed
10244         this.reader = Roo.factory(this.reader, Roo.data);
10245         this.reader.xmodule = this.xmodule || false;
10246         if(!this.recordType){
10247             this.recordType = this.reader.recordType;
10248         }
10249         if(this.reader.onMetaChange){
10250             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10251         }
10252     }
10253
10254     if(this.recordType){
10255         this.fields = this.recordType.prototype.fields;
10256     }
10257     this.modified = [];
10258
10259     this.addEvents({
10260         /**
10261          * @event datachanged
10262          * Fires when the data cache has changed, and a widget which is using this Store
10263          * as a Record cache should refresh its view.
10264          * @param {Store} this
10265          */
10266         datachanged : true,
10267         /**
10268          * @event metachange
10269          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10270          * @param {Store} this
10271          * @param {Object} meta The JSON metadata
10272          */
10273         metachange : true,
10274         /**
10275          * @event add
10276          * Fires when Records have been added to the Store
10277          * @param {Store} this
10278          * @param {Roo.data.Record[]} records The array of Records added
10279          * @param {Number} index The index at which the record(s) were added
10280          */
10281         add : true,
10282         /**
10283          * @event remove
10284          * Fires when a Record has been removed from the Store
10285          * @param {Store} this
10286          * @param {Roo.data.Record} record The Record that was removed
10287          * @param {Number} index The index at which the record was removed
10288          */
10289         remove : true,
10290         /**
10291          * @event update
10292          * Fires when a Record has been updated
10293          * @param {Store} this
10294          * @param {Roo.data.Record} record The Record that was updated
10295          * @param {String} operation The update operation being performed.  Value may be one of:
10296          * <pre><code>
10297  Roo.data.Record.EDIT
10298  Roo.data.Record.REJECT
10299  Roo.data.Record.COMMIT
10300          * </code></pre>
10301          */
10302         update : true,
10303         /**
10304          * @event clear
10305          * Fires when the data cache has been cleared.
10306          * @param {Store} this
10307          */
10308         clear : true,
10309         /**
10310          * @event beforeload
10311          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10312          * the load action will be canceled.
10313          * @param {Store} this
10314          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10315          */
10316         beforeload : true,
10317         /**
10318          * @event beforeloadadd
10319          * Fires after a new set of Records has been loaded.
10320          * @param {Store} this
10321          * @param {Roo.data.Record[]} records The Records that were loaded
10322          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10323          */
10324         beforeloadadd : true,
10325         /**
10326          * @event load
10327          * Fires after a new set of Records has been loaded, before they are added to the store.
10328          * @param {Store} this
10329          * @param {Roo.data.Record[]} records The Records that were loaded
10330          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10331          * @params {Object} return from reader
10332          */
10333         load : true,
10334         /**
10335          * @event loadexception
10336          * Fires if an exception occurs in the Proxy during loading.
10337          * Called with the signature of the Proxy's "loadexception" event.
10338          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10339          * 
10340          * @param {Proxy} 
10341          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10342          * @param {Object} load options 
10343          * @param {Object} jsonData from your request (normally this contains the Exception)
10344          */
10345         loadexception : true
10346     });
10347     
10348     if(this.proxy){
10349         this.proxy = Roo.factory(this.proxy, Roo.data);
10350         this.proxy.xmodule = this.xmodule || false;
10351         this.relayEvents(this.proxy,  ["loadexception"]);
10352     }
10353     this.sortToggle = {};
10354     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10355
10356     Roo.data.Store.superclass.constructor.call(this);
10357
10358     if(this.inlineData){
10359         this.loadData(this.inlineData);
10360         delete this.inlineData;
10361     }
10362 };
10363
10364 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10365      /**
10366     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10367     * without a remote query - used by combo/forms at present.
10368     */
10369     
10370     /**
10371     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10372     */
10373     /**
10374     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10375     */
10376     /**
10377     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10378     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10379     */
10380     /**
10381     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10382     * on any HTTP request
10383     */
10384     /**
10385     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10386     */
10387     /**
10388     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10389     */
10390     multiSort: false,
10391     /**
10392     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10393     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10394     */
10395     remoteSort : false,
10396
10397     /**
10398     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10399      * loaded or when a record is removed. (defaults to false).
10400     */
10401     pruneModifiedRecords : false,
10402
10403     // private
10404     lastOptions : null,
10405
10406     /**
10407      * Add Records to the Store and fires the add event.
10408      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10409      */
10410     add : function(records){
10411         records = [].concat(records);
10412         for(var i = 0, len = records.length; i < len; i++){
10413             records[i].join(this);
10414         }
10415         var index = this.data.length;
10416         this.data.addAll(records);
10417         this.fireEvent("add", this, records, index);
10418     },
10419
10420     /**
10421      * Remove a Record from the Store and fires the remove event.
10422      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10423      */
10424     remove : function(record){
10425         var index = this.data.indexOf(record);
10426         this.data.removeAt(index);
10427         if(this.pruneModifiedRecords){
10428             this.modified.remove(record);
10429         }
10430         this.fireEvent("remove", this, record, index);
10431     },
10432
10433     /**
10434      * Remove all Records from the Store and fires the clear event.
10435      */
10436     removeAll : function(){
10437         this.data.clear();
10438         if(this.pruneModifiedRecords){
10439             this.modified = [];
10440         }
10441         this.fireEvent("clear", this);
10442     },
10443
10444     /**
10445      * Inserts Records to the Store at the given index and fires the add event.
10446      * @param {Number} index The start index at which to insert the passed Records.
10447      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10448      */
10449     insert : function(index, records){
10450         records = [].concat(records);
10451         for(var i = 0, len = records.length; i < len; i++){
10452             this.data.insert(index, records[i]);
10453             records[i].join(this);
10454         }
10455         this.fireEvent("add", this, records, index);
10456     },
10457
10458     /**
10459      * Get the index within the cache of the passed Record.
10460      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10461      * @return {Number} The index of the passed Record. Returns -1 if not found.
10462      */
10463     indexOf : function(record){
10464         return this.data.indexOf(record);
10465     },
10466
10467     /**
10468      * Get the index within the cache of the Record with the passed id.
10469      * @param {String} id The id of the Record to find.
10470      * @return {Number} The index of the Record. Returns -1 if not found.
10471      */
10472     indexOfId : function(id){
10473         return this.data.indexOfKey(id);
10474     },
10475
10476     /**
10477      * Get the Record with the specified id.
10478      * @param {String} id The id of the Record to find.
10479      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10480      */
10481     getById : function(id){
10482         return this.data.key(id);
10483     },
10484
10485     /**
10486      * Get the Record at the specified index.
10487      * @param {Number} index The index of the Record to find.
10488      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10489      */
10490     getAt : function(index){
10491         return this.data.itemAt(index);
10492     },
10493
10494     /**
10495      * Returns a range of Records between specified indices.
10496      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10497      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10498      * @return {Roo.data.Record[]} An array of Records
10499      */
10500     getRange : function(start, end){
10501         return this.data.getRange(start, end);
10502     },
10503
10504     // private
10505     storeOptions : function(o){
10506         o = Roo.apply({}, o);
10507         delete o.callback;
10508         delete o.scope;
10509         this.lastOptions = o;
10510     },
10511
10512     /**
10513      * Loads the Record cache from the configured Proxy using the configured Reader.
10514      * <p>
10515      * If using remote paging, then the first load call must specify the <em>start</em>
10516      * and <em>limit</em> properties in the options.params property to establish the initial
10517      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10518      * <p>
10519      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10520      * and this call will return before the new data has been loaded. Perform any post-processing
10521      * in a callback function, or in a "load" event handler.</strong>
10522      * <p>
10523      * @param {Object} options An object containing properties which control loading options:<ul>
10524      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10525      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10526      * passed the following arguments:<ul>
10527      * <li>r : Roo.data.Record[]</li>
10528      * <li>options: Options object from the load call</li>
10529      * <li>success: Boolean success indicator</li></ul></li>
10530      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10531      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10532      * </ul>
10533      */
10534     load : function(options){
10535         options = options || {};
10536         if(this.fireEvent("beforeload", this, options) !== false){
10537             this.storeOptions(options);
10538             var p = Roo.apply(options.params || {}, this.baseParams);
10539             // if meta was not loaded from remote source.. try requesting it.
10540             if (!this.reader.metaFromRemote) {
10541                 p._requestMeta = 1;
10542             }
10543             if(this.sortInfo && this.remoteSort){
10544                 var pn = this.paramNames;
10545                 p[pn["sort"]] = this.sortInfo.field;
10546                 p[pn["dir"]] = this.sortInfo.direction;
10547             }
10548             if (this.multiSort) {
10549                 var pn = this.paramNames;
10550                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10551             }
10552             
10553             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10554         }
10555     },
10556
10557     /**
10558      * Reloads the Record cache from the configured Proxy using the configured Reader and
10559      * the options from the last load operation performed.
10560      * @param {Object} options (optional) An object containing properties which may override the options
10561      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10562      * the most recently used options are reused).
10563      */
10564     reload : function(options){
10565         this.load(Roo.applyIf(options||{}, this.lastOptions));
10566     },
10567
10568     // private
10569     // Called as a callback by the Reader during a load operation.
10570     loadRecords : function(o, options, success){
10571         if(!o || success === false){
10572             if(success !== false){
10573                 this.fireEvent("load", this, [], options, o);
10574             }
10575             if(options.callback){
10576                 options.callback.call(options.scope || this, [], options, false);
10577             }
10578             return;
10579         }
10580         // if data returned failure - throw an exception.
10581         if (o.success === false) {
10582             // show a message if no listener is registered.
10583             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10584                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10585             }
10586             // loadmask wil be hooked into this..
10587             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10588             return;
10589         }
10590         var r = o.records, t = o.totalRecords || r.length;
10591         
10592         this.fireEvent("beforeloadadd", this, r, options, o);
10593         
10594         if(!options || options.add !== true){
10595             if(this.pruneModifiedRecords){
10596                 this.modified = [];
10597             }
10598             for(var i = 0, len = r.length; i < len; i++){
10599                 r[i].join(this);
10600             }
10601             if(this.snapshot){
10602                 this.data = this.snapshot;
10603                 delete this.snapshot;
10604             }
10605             this.data.clear();
10606             this.data.addAll(r);
10607             this.totalLength = t;
10608             this.applySort();
10609             this.fireEvent("datachanged", this);
10610         }else{
10611             this.totalLength = Math.max(t, this.data.length+r.length);
10612             this.add(r);
10613         }
10614         this.fireEvent("load", this, r, options, o);
10615         if(options.callback){
10616             options.callback.call(options.scope || this, r, options, true);
10617         }
10618     },
10619
10620
10621     /**
10622      * Loads data from a passed data block. A Reader which understands the format of the data
10623      * must have been configured in the constructor.
10624      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10625      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10626      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10627      */
10628     loadData : function(o, append){
10629         var r = this.reader.readRecords(o);
10630         this.loadRecords(r, {add: append}, true);
10631     },
10632
10633     /**
10634      * Gets the number of cached records.
10635      * <p>
10636      * <em>If using paging, this may not be the total size of the dataset. If the data object
10637      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10638      * the data set size</em>
10639      */
10640     getCount : function(){
10641         return this.data.length || 0;
10642     },
10643
10644     /**
10645      * Gets the total number of records in the dataset as returned by the server.
10646      * <p>
10647      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10648      * the dataset size</em>
10649      */
10650     getTotalCount : function(){
10651         return this.totalLength || 0;
10652     },
10653
10654     /**
10655      * Returns the sort state of the Store as an object with two properties:
10656      * <pre><code>
10657  field {String} The name of the field by which the Records are sorted
10658  direction {String} The sort order, "ASC" or "DESC"
10659      * </code></pre>
10660      */
10661     getSortState : function(){
10662         return this.sortInfo;
10663     },
10664
10665     // private
10666     applySort : function(){
10667         if(this.sortInfo && !this.remoteSort){
10668             var s = this.sortInfo, f = s.field;
10669             var st = this.fields.get(f).sortType;
10670             var fn = function(r1, r2){
10671                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10672                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10673             };
10674             this.data.sort(s.direction, fn);
10675             if(this.snapshot && this.snapshot != this.data){
10676                 this.snapshot.sort(s.direction, fn);
10677             }
10678         }
10679     },
10680
10681     /**
10682      * Sets the default sort column and order to be used by the next load operation.
10683      * @param {String} fieldName The name of the field to sort by.
10684      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10685      */
10686     setDefaultSort : function(field, dir){
10687         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10688     },
10689
10690     /**
10691      * Sort the Records.
10692      * If remote sorting is used, the sort is performed on the server, and the cache is
10693      * reloaded. If local sorting is used, the cache is sorted internally.
10694      * @param {String} fieldName The name of the field to sort by.
10695      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10696      */
10697     sort : function(fieldName, dir){
10698         var f = this.fields.get(fieldName);
10699         if(!dir){
10700             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10701             
10702             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10703                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10704             }else{
10705                 dir = f.sortDir;
10706             }
10707         }
10708         this.sortToggle[f.name] = dir;
10709         this.sortInfo = {field: f.name, direction: dir};
10710         if(!this.remoteSort){
10711             this.applySort();
10712             this.fireEvent("datachanged", this);
10713         }else{
10714             this.load(this.lastOptions);
10715         }
10716     },
10717
10718     /**
10719      * Calls the specified function for each of the Records in the cache.
10720      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10721      * Returning <em>false</em> aborts and exits the iteration.
10722      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10723      */
10724     each : function(fn, scope){
10725         this.data.each(fn, scope);
10726     },
10727
10728     /**
10729      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10730      * (e.g., during paging).
10731      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10732      */
10733     getModifiedRecords : function(){
10734         return this.modified;
10735     },
10736
10737     // private
10738     createFilterFn : function(property, value, anyMatch){
10739         if(!value.exec){ // not a regex
10740             value = String(value);
10741             if(value.length == 0){
10742                 return false;
10743             }
10744             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10745         }
10746         return function(r){
10747             return value.test(r.data[property]);
10748         };
10749     },
10750
10751     /**
10752      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10753      * @param {String} property A field on your records
10754      * @param {Number} start The record index to start at (defaults to 0)
10755      * @param {Number} end The last record index to include (defaults to length - 1)
10756      * @return {Number} The sum
10757      */
10758     sum : function(property, start, end){
10759         var rs = this.data.items, v = 0;
10760         start = start || 0;
10761         end = (end || end === 0) ? end : rs.length-1;
10762
10763         for(var i = start; i <= end; i++){
10764             v += (rs[i].data[property] || 0);
10765         }
10766         return v;
10767     },
10768
10769     /**
10770      * Filter the records by a specified property.
10771      * @param {String} field A field on your records
10772      * @param {String/RegExp} value Either a string that the field
10773      * should start with or a RegExp to test against the field
10774      * @param {Boolean} anyMatch True to match any part not just the beginning
10775      */
10776     filter : function(property, value, anyMatch){
10777         var fn = this.createFilterFn(property, value, anyMatch);
10778         return fn ? this.filterBy(fn) : this.clearFilter();
10779     },
10780
10781     /**
10782      * Filter by a function. The specified function will be called with each
10783      * record in this data source. If the function returns true the record is included,
10784      * otherwise it is filtered.
10785      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10786      * @param {Object} scope (optional) The scope of the function (defaults to this)
10787      */
10788     filterBy : function(fn, scope){
10789         this.snapshot = this.snapshot || this.data;
10790         this.data = this.queryBy(fn, scope||this);
10791         this.fireEvent("datachanged", this);
10792     },
10793
10794     /**
10795      * Query the records by a specified property.
10796      * @param {String} field A field on your records
10797      * @param {String/RegExp} value Either a string that the field
10798      * should start with or a RegExp to test against the field
10799      * @param {Boolean} anyMatch True to match any part not just the beginning
10800      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10801      */
10802     query : function(property, value, anyMatch){
10803         var fn = this.createFilterFn(property, value, anyMatch);
10804         return fn ? this.queryBy(fn) : this.data.clone();
10805     },
10806
10807     /**
10808      * Query by a function. The specified function will be called with each
10809      * record in this data source. If the function returns true the record is included
10810      * in the results.
10811      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10812      * @param {Object} scope (optional) The scope of the function (defaults to this)
10813       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10814      **/
10815     queryBy : function(fn, scope){
10816         var data = this.snapshot || this.data;
10817         return data.filterBy(fn, scope||this);
10818     },
10819
10820     /**
10821      * Collects unique values for a particular dataIndex from this store.
10822      * @param {String} dataIndex The property to collect
10823      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10824      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10825      * @return {Array} An array of the unique values
10826      **/
10827     collect : function(dataIndex, allowNull, bypassFilter){
10828         var d = (bypassFilter === true && this.snapshot) ?
10829                 this.snapshot.items : this.data.items;
10830         var v, sv, r = [], l = {};
10831         for(var i = 0, len = d.length; i < len; i++){
10832             v = d[i].data[dataIndex];
10833             sv = String(v);
10834             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10835                 l[sv] = true;
10836                 r[r.length] = v;
10837             }
10838         }
10839         return r;
10840     },
10841
10842     /**
10843      * Revert to a view of the Record cache with no filtering applied.
10844      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10845      */
10846     clearFilter : function(suppressEvent){
10847         if(this.snapshot && this.snapshot != this.data){
10848             this.data = this.snapshot;
10849             delete this.snapshot;
10850             if(suppressEvent !== true){
10851                 this.fireEvent("datachanged", this);
10852             }
10853         }
10854     },
10855
10856     // private
10857     afterEdit : function(record){
10858         if(this.modified.indexOf(record) == -1){
10859             this.modified.push(record);
10860         }
10861         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10862     },
10863     
10864     // private
10865     afterReject : function(record){
10866         this.modified.remove(record);
10867         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10868     },
10869
10870     // private
10871     afterCommit : function(record){
10872         this.modified.remove(record);
10873         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10874     },
10875
10876     /**
10877      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10878      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10879      */
10880     commitChanges : function(){
10881         var m = this.modified.slice(0);
10882         this.modified = [];
10883         for(var i = 0, len = m.length; i < len; i++){
10884             m[i].commit();
10885         }
10886     },
10887
10888     /**
10889      * Cancel outstanding changes on all changed records.
10890      */
10891     rejectChanges : function(){
10892         var m = this.modified.slice(0);
10893         this.modified = [];
10894         for(var i = 0, len = m.length; i < len; i++){
10895             m[i].reject();
10896         }
10897     },
10898
10899     onMetaChange : function(meta, rtype, o){
10900         this.recordType = rtype;
10901         this.fields = rtype.prototype.fields;
10902         delete this.snapshot;
10903         this.sortInfo = meta.sortInfo || this.sortInfo;
10904         this.modified = [];
10905         this.fireEvent('metachange', this, this.reader.meta);
10906     },
10907     
10908     moveIndex : function(data, type)
10909     {
10910         var index = this.indexOf(data);
10911         
10912         var newIndex = index + type;
10913         
10914         this.remove(data);
10915         
10916         this.insert(newIndex, data);
10917         
10918     }
10919 });/*
10920  * Based on:
10921  * Ext JS Library 1.1.1
10922  * Copyright(c) 2006-2007, Ext JS, LLC.
10923  *
10924  * Originally Released Under LGPL - original licence link has changed is not relivant.
10925  *
10926  * Fork - LGPL
10927  * <script type="text/javascript">
10928  */
10929
10930 /**
10931  * @class Roo.data.SimpleStore
10932  * @extends Roo.data.Store
10933  * Small helper class to make creating Stores from Array data easier.
10934  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10935  * @cfg {Array} fields An array of field definition objects, or field name strings.
10936  * @cfg {Array} data The multi-dimensional array of data
10937  * @constructor
10938  * @param {Object} config
10939  */
10940 Roo.data.SimpleStore = function(config){
10941     Roo.data.SimpleStore.superclass.constructor.call(this, {
10942         isLocal : true,
10943         reader: new Roo.data.ArrayReader({
10944                 id: config.id
10945             },
10946             Roo.data.Record.create(config.fields)
10947         ),
10948         proxy : new Roo.data.MemoryProxy(config.data)
10949     });
10950     this.load();
10951 };
10952 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10953  * Based on:
10954  * Ext JS Library 1.1.1
10955  * Copyright(c) 2006-2007, Ext JS, LLC.
10956  *
10957  * Originally Released Under LGPL - original licence link has changed is not relivant.
10958  *
10959  * Fork - LGPL
10960  * <script type="text/javascript">
10961  */
10962
10963 /**
10964 /**
10965  * @extends Roo.data.Store
10966  * @class Roo.data.JsonStore
10967  * Small helper class to make creating Stores for JSON data easier. <br/>
10968 <pre><code>
10969 var store = new Roo.data.JsonStore({
10970     url: 'get-images.php',
10971     root: 'images',
10972     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10973 });
10974 </code></pre>
10975  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10976  * JsonReader and HttpProxy (unless inline data is provided).</b>
10977  * @cfg {Array} fields An array of field definition objects, or field name strings.
10978  * @constructor
10979  * @param {Object} config
10980  */
10981 Roo.data.JsonStore = function(c){
10982     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10983         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10984         reader: new Roo.data.JsonReader(c, c.fields)
10985     }));
10986 };
10987 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10988  * Based on:
10989  * Ext JS Library 1.1.1
10990  * Copyright(c) 2006-2007, Ext JS, LLC.
10991  *
10992  * Originally Released Under LGPL - original licence link has changed is not relivant.
10993  *
10994  * Fork - LGPL
10995  * <script type="text/javascript">
10996  */
10997
10998  
10999 Roo.data.Field = function(config){
11000     if(typeof config == "string"){
11001         config = {name: config};
11002     }
11003     Roo.apply(this, config);
11004     
11005     if(!this.type){
11006         this.type = "auto";
11007     }
11008     
11009     var st = Roo.data.SortTypes;
11010     // named sortTypes are supported, here we look them up
11011     if(typeof this.sortType == "string"){
11012         this.sortType = st[this.sortType];
11013     }
11014     
11015     // set default sortType for strings and dates
11016     if(!this.sortType){
11017         switch(this.type){
11018             case "string":
11019                 this.sortType = st.asUCString;
11020                 break;
11021             case "date":
11022                 this.sortType = st.asDate;
11023                 break;
11024             default:
11025                 this.sortType = st.none;
11026         }
11027     }
11028
11029     // define once
11030     var stripRe = /[\$,%]/g;
11031
11032     // prebuilt conversion function for this field, instead of
11033     // switching every time we're reading a value
11034     if(!this.convert){
11035         var cv, dateFormat = this.dateFormat;
11036         switch(this.type){
11037             case "":
11038             case "auto":
11039             case undefined:
11040                 cv = function(v){ return v; };
11041                 break;
11042             case "string":
11043                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11044                 break;
11045             case "int":
11046                 cv = function(v){
11047                     return v !== undefined && v !== null && v !== '' ?
11048                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11049                     };
11050                 break;
11051             case "float":
11052                 cv = function(v){
11053                     return v !== undefined && v !== null && v !== '' ?
11054                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11055                     };
11056                 break;
11057             case "bool":
11058             case "boolean":
11059                 cv = function(v){ return v === true || v === "true" || v == 1; };
11060                 break;
11061             case "date":
11062                 cv = function(v){
11063                     if(!v){
11064                         return '';
11065                     }
11066                     if(v instanceof Date){
11067                         return v;
11068                     }
11069                     if(dateFormat){
11070                         if(dateFormat == "timestamp"){
11071                             return new Date(v*1000);
11072                         }
11073                         return Date.parseDate(v, dateFormat);
11074                     }
11075                     var parsed = Date.parse(v);
11076                     return parsed ? new Date(parsed) : null;
11077                 };
11078              break;
11079             
11080         }
11081         this.convert = cv;
11082     }
11083 };
11084
11085 Roo.data.Field.prototype = {
11086     dateFormat: null,
11087     defaultValue: "",
11088     mapping: null,
11089     sortType : null,
11090     sortDir : "ASC"
11091 };/*
11092  * Based on:
11093  * Ext JS Library 1.1.1
11094  * Copyright(c) 2006-2007, Ext JS, LLC.
11095  *
11096  * Originally Released Under LGPL - original licence link has changed is not relivant.
11097  *
11098  * Fork - LGPL
11099  * <script type="text/javascript">
11100  */
11101  
11102 // Base class for reading structured data from a data source.  This class is intended to be
11103 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11104
11105 /**
11106  * @class Roo.data.DataReader
11107  * Base class for reading structured data from a data source.  This class is intended to be
11108  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11109  */
11110
11111 Roo.data.DataReader = function(meta, recordType){
11112     
11113     this.meta = meta;
11114     
11115     this.recordType = recordType instanceof Array ? 
11116         Roo.data.Record.create(recordType) : recordType;
11117 };
11118
11119 Roo.data.DataReader.prototype = {
11120      /**
11121      * Create an empty record
11122      * @param {Object} data (optional) - overlay some values
11123      * @return {Roo.data.Record} record created.
11124      */
11125     newRow :  function(d) {
11126         var da =  {};
11127         this.recordType.prototype.fields.each(function(c) {
11128             switch( c.type) {
11129                 case 'int' : da[c.name] = 0; break;
11130                 case 'date' : da[c.name] = new Date(); break;
11131                 case 'float' : da[c.name] = 0.0; break;
11132                 case 'boolean' : da[c.name] = false; break;
11133                 default : da[c.name] = ""; break;
11134             }
11135             
11136         });
11137         return new this.recordType(Roo.apply(da, d));
11138     }
11139     
11140 };/*
11141  * Based on:
11142  * Ext JS Library 1.1.1
11143  * Copyright(c) 2006-2007, Ext JS, LLC.
11144  *
11145  * Originally Released Under LGPL - original licence link has changed is not relivant.
11146  *
11147  * Fork - LGPL
11148  * <script type="text/javascript">
11149  */
11150
11151 /**
11152  * @class Roo.data.DataProxy
11153  * @extends Roo.data.Observable
11154  * This class is an abstract base class for implementations which provide retrieval of
11155  * unformatted data objects.<br>
11156  * <p>
11157  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11158  * (of the appropriate type which knows how to parse the data object) to provide a block of
11159  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11160  * <p>
11161  * Custom implementations must implement the load method as described in
11162  * {@link Roo.data.HttpProxy#load}.
11163  */
11164 Roo.data.DataProxy = function(){
11165     this.addEvents({
11166         /**
11167          * @event beforeload
11168          * Fires before a network request is made to retrieve a data object.
11169          * @param {Object} This DataProxy object.
11170          * @param {Object} params The params parameter to the load function.
11171          */
11172         beforeload : true,
11173         /**
11174          * @event load
11175          * Fires before the load method's callback is called.
11176          * @param {Object} This DataProxy object.
11177          * @param {Object} o The data object.
11178          * @param {Object} arg The callback argument object passed to the load function.
11179          */
11180         load : true,
11181         /**
11182          * @event loadexception
11183          * Fires if an Exception occurs during data retrieval.
11184          * @param {Object} This DataProxy object.
11185          * @param {Object} o The data object.
11186          * @param {Object} arg The callback argument object passed to the load function.
11187          * @param {Object} e The Exception.
11188          */
11189         loadexception : true
11190     });
11191     Roo.data.DataProxy.superclass.constructor.call(this);
11192 };
11193
11194 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11195
11196     /**
11197      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11198      */
11199 /*
11200  * Based on:
11201  * Ext JS Library 1.1.1
11202  * Copyright(c) 2006-2007, Ext JS, LLC.
11203  *
11204  * Originally Released Under LGPL - original licence link has changed is not relivant.
11205  *
11206  * Fork - LGPL
11207  * <script type="text/javascript">
11208  */
11209 /**
11210  * @class Roo.data.MemoryProxy
11211  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11212  * to the Reader when its load method is called.
11213  * @constructor
11214  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11215  */
11216 Roo.data.MemoryProxy = function(data){
11217     if (data.data) {
11218         data = data.data;
11219     }
11220     Roo.data.MemoryProxy.superclass.constructor.call(this);
11221     this.data = data;
11222 };
11223
11224 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11225     
11226     /**
11227      * Load data from the requested source (in this case an in-memory
11228      * data object passed to the constructor), read the data object into
11229      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11230      * process that block using the passed callback.
11231      * @param {Object} params This parameter is not used by the MemoryProxy class.
11232      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11233      * object into a block of Roo.data.Records.
11234      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11235      * The function must be passed <ul>
11236      * <li>The Record block object</li>
11237      * <li>The "arg" argument from the load function</li>
11238      * <li>A boolean success indicator</li>
11239      * </ul>
11240      * @param {Object} scope The scope in which to call the callback
11241      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11242      */
11243     load : function(params, reader, callback, scope, arg){
11244         params = params || {};
11245         var result;
11246         try {
11247             result = reader.readRecords(this.data);
11248         }catch(e){
11249             this.fireEvent("loadexception", this, arg, null, e);
11250             callback.call(scope, null, arg, false);
11251             return;
11252         }
11253         callback.call(scope, result, arg, true);
11254     },
11255     
11256     // private
11257     update : function(params, records){
11258         
11259     }
11260 });/*
11261  * Based on:
11262  * Ext JS Library 1.1.1
11263  * Copyright(c) 2006-2007, Ext JS, LLC.
11264  *
11265  * Originally Released Under LGPL - original licence link has changed is not relivant.
11266  *
11267  * Fork - LGPL
11268  * <script type="text/javascript">
11269  */
11270 /**
11271  * @class Roo.data.HttpProxy
11272  * @extends Roo.data.DataProxy
11273  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11274  * configured to reference a certain URL.<br><br>
11275  * <p>
11276  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11277  * from which the running page was served.<br><br>
11278  * <p>
11279  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11280  * <p>
11281  * Be aware that to enable the browser to parse an XML document, the server must set
11282  * the Content-Type header in the HTTP response to "text/xml".
11283  * @constructor
11284  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11285  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11286  * will be used to make the request.
11287  */
11288 Roo.data.HttpProxy = function(conn){
11289     Roo.data.HttpProxy.superclass.constructor.call(this);
11290     // is conn a conn config or a real conn?
11291     this.conn = conn;
11292     this.useAjax = !conn || !conn.events;
11293   
11294 };
11295
11296 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11297     // thse are take from connection...
11298     
11299     /**
11300      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11301      */
11302     /**
11303      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11304      * extra parameters to each request made by this object. (defaults to undefined)
11305      */
11306     /**
11307      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11308      *  to each request made by this object. (defaults to undefined)
11309      */
11310     /**
11311      * @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)
11312      */
11313     /**
11314      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11315      */
11316      /**
11317      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11318      * @type Boolean
11319      */
11320   
11321
11322     /**
11323      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11324      * @type Boolean
11325      */
11326     /**
11327      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11328      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11329      * a finer-grained basis than the DataProxy events.
11330      */
11331     getConnection : function(){
11332         return this.useAjax ? Roo.Ajax : this.conn;
11333     },
11334
11335     /**
11336      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11337      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11338      * process that block using the passed callback.
11339      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11340      * for the request to the remote server.
11341      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11342      * object into a block of Roo.data.Records.
11343      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11344      * The function must be passed <ul>
11345      * <li>The Record block object</li>
11346      * <li>The "arg" argument from the load function</li>
11347      * <li>A boolean success indicator</li>
11348      * </ul>
11349      * @param {Object} scope The scope in which to call the callback
11350      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11351      */
11352     load : function(params, reader, callback, scope, arg){
11353         if(this.fireEvent("beforeload", this, params) !== false){
11354             var  o = {
11355                 params : params || {},
11356                 request: {
11357                     callback : callback,
11358                     scope : scope,
11359                     arg : arg
11360                 },
11361                 reader: reader,
11362                 callback : this.loadResponse,
11363                 scope: this
11364             };
11365             if(this.useAjax){
11366                 Roo.applyIf(o, this.conn);
11367                 if(this.activeRequest){
11368                     Roo.Ajax.abort(this.activeRequest);
11369                 }
11370                 this.activeRequest = Roo.Ajax.request(o);
11371             }else{
11372                 this.conn.request(o);
11373             }
11374         }else{
11375             callback.call(scope||this, null, arg, false);
11376         }
11377     },
11378
11379     // private
11380     loadResponse : function(o, success, response){
11381         delete this.activeRequest;
11382         if(!success){
11383             this.fireEvent("loadexception", this, o, response);
11384             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11385             return;
11386         }
11387         var result;
11388         try {
11389             result = o.reader.read(response);
11390         }catch(e){
11391             this.fireEvent("loadexception", this, o, response, e);
11392             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11393             return;
11394         }
11395         
11396         this.fireEvent("load", this, o, o.request.arg);
11397         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11398     },
11399
11400     // private
11401     update : function(dataSet){
11402
11403     },
11404
11405     // private
11406     updateResponse : function(dataSet){
11407
11408     }
11409 });/*
11410  * Based on:
11411  * Ext JS Library 1.1.1
11412  * Copyright(c) 2006-2007, Ext JS, LLC.
11413  *
11414  * Originally Released Under LGPL - original licence link has changed is not relivant.
11415  *
11416  * Fork - LGPL
11417  * <script type="text/javascript">
11418  */
11419
11420 /**
11421  * @class Roo.data.ScriptTagProxy
11422  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11423  * other than the originating domain of the running page.<br><br>
11424  * <p>
11425  * <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
11426  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11427  * <p>
11428  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11429  * source code that is used as the source inside a &lt;script> tag.<br><br>
11430  * <p>
11431  * In order for the browser to process the returned data, the server must wrap the data object
11432  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11433  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11434  * depending on whether the callback name was passed:
11435  * <p>
11436  * <pre><code>
11437 boolean scriptTag = false;
11438 String cb = request.getParameter("callback");
11439 if (cb != null) {
11440     scriptTag = true;
11441     response.setContentType("text/javascript");
11442 } else {
11443     response.setContentType("application/x-json");
11444 }
11445 Writer out = response.getWriter();
11446 if (scriptTag) {
11447     out.write(cb + "(");
11448 }
11449 out.print(dataBlock.toJsonString());
11450 if (scriptTag) {
11451     out.write(");");
11452 }
11453 </pre></code>
11454  *
11455  * @constructor
11456  * @param {Object} config A configuration object.
11457  */
11458 Roo.data.ScriptTagProxy = function(config){
11459     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11460     Roo.apply(this, config);
11461     this.head = document.getElementsByTagName("head")[0];
11462 };
11463
11464 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11465
11466 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11467     /**
11468      * @cfg {String} url The URL from which to request the data object.
11469      */
11470     /**
11471      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11472      */
11473     timeout : 30000,
11474     /**
11475      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11476      * the server the name of the callback function set up by the load call to process the returned data object.
11477      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11478      * javascript output which calls this named function passing the data object as its only parameter.
11479      */
11480     callbackParam : "callback",
11481     /**
11482      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11483      * name to the request.
11484      */
11485     nocache : true,
11486
11487     /**
11488      * Load data from the configured URL, read the data object into
11489      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11490      * process that block using the passed callback.
11491      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11492      * for the request to the remote server.
11493      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11494      * object into a block of Roo.data.Records.
11495      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11496      * The function must be passed <ul>
11497      * <li>The Record block object</li>
11498      * <li>The "arg" argument from the load function</li>
11499      * <li>A boolean success indicator</li>
11500      * </ul>
11501      * @param {Object} scope The scope in which to call the callback
11502      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11503      */
11504     load : function(params, reader, callback, scope, arg){
11505         if(this.fireEvent("beforeload", this, params) !== false){
11506
11507             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11508
11509             var url = this.url;
11510             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11511             if(this.nocache){
11512                 url += "&_dc=" + (new Date().getTime());
11513             }
11514             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11515             var trans = {
11516                 id : transId,
11517                 cb : "stcCallback"+transId,
11518                 scriptId : "stcScript"+transId,
11519                 params : params,
11520                 arg : arg,
11521                 url : url,
11522                 callback : callback,
11523                 scope : scope,
11524                 reader : reader
11525             };
11526             var conn = this;
11527
11528             window[trans.cb] = function(o){
11529                 conn.handleResponse(o, trans);
11530             };
11531
11532             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11533
11534             if(this.autoAbort !== false){
11535                 this.abort();
11536             }
11537
11538             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11539
11540             var script = document.createElement("script");
11541             script.setAttribute("src", url);
11542             script.setAttribute("type", "text/javascript");
11543             script.setAttribute("id", trans.scriptId);
11544             this.head.appendChild(script);
11545
11546             this.trans = trans;
11547         }else{
11548             callback.call(scope||this, null, arg, false);
11549         }
11550     },
11551
11552     // private
11553     isLoading : function(){
11554         return this.trans ? true : false;
11555     },
11556
11557     /**
11558      * Abort the current server request.
11559      */
11560     abort : function(){
11561         if(this.isLoading()){
11562             this.destroyTrans(this.trans);
11563         }
11564     },
11565
11566     // private
11567     destroyTrans : function(trans, isLoaded){
11568         this.head.removeChild(document.getElementById(trans.scriptId));
11569         clearTimeout(trans.timeoutId);
11570         if(isLoaded){
11571             window[trans.cb] = undefined;
11572             try{
11573                 delete window[trans.cb];
11574             }catch(e){}
11575         }else{
11576             // if hasn't been loaded, wait for load to remove it to prevent script error
11577             window[trans.cb] = function(){
11578                 window[trans.cb] = undefined;
11579                 try{
11580                     delete window[trans.cb];
11581                 }catch(e){}
11582             };
11583         }
11584     },
11585
11586     // private
11587     handleResponse : function(o, trans){
11588         this.trans = false;
11589         this.destroyTrans(trans, true);
11590         var result;
11591         try {
11592             result = trans.reader.readRecords(o);
11593         }catch(e){
11594             this.fireEvent("loadexception", this, o, trans.arg, e);
11595             trans.callback.call(trans.scope||window, null, trans.arg, false);
11596             return;
11597         }
11598         this.fireEvent("load", this, o, trans.arg);
11599         trans.callback.call(trans.scope||window, result, trans.arg, true);
11600     },
11601
11602     // private
11603     handleFailure : function(trans){
11604         this.trans = false;
11605         this.destroyTrans(trans, false);
11606         this.fireEvent("loadexception", this, null, trans.arg);
11607         trans.callback.call(trans.scope||window, null, trans.arg, false);
11608     }
11609 });/*
11610  * Based on:
11611  * Ext JS Library 1.1.1
11612  * Copyright(c) 2006-2007, Ext JS, LLC.
11613  *
11614  * Originally Released Under LGPL - original licence link has changed is not relivant.
11615  *
11616  * Fork - LGPL
11617  * <script type="text/javascript">
11618  */
11619
11620 /**
11621  * @class Roo.data.JsonReader
11622  * @extends Roo.data.DataReader
11623  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11624  * based on mappings in a provided Roo.data.Record constructor.
11625  * 
11626  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11627  * in the reply previously. 
11628  * 
11629  * <p>
11630  * Example code:
11631  * <pre><code>
11632 var RecordDef = Roo.data.Record.create([
11633     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11634     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11635 ]);
11636 var myReader = new Roo.data.JsonReader({
11637     totalProperty: "results",    // The property which contains the total dataset size (optional)
11638     root: "rows",                // The property which contains an Array of row objects
11639     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11640 }, RecordDef);
11641 </code></pre>
11642  * <p>
11643  * This would consume a JSON file like this:
11644  * <pre><code>
11645 { 'results': 2, 'rows': [
11646     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11647     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11648 }
11649 </code></pre>
11650  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11651  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11652  * paged from the remote server.
11653  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11654  * @cfg {String} root name of the property which contains the Array of row objects.
11655  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11656  * @cfg {Array} fields Array of field definition objects
11657  * @constructor
11658  * Create a new JsonReader
11659  * @param {Object} meta Metadata configuration options
11660  * @param {Object} recordType Either an Array of field definition objects,
11661  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11662  */
11663 Roo.data.JsonReader = function(meta, recordType){
11664     
11665     meta = meta || {};
11666     // set some defaults:
11667     Roo.applyIf(meta, {
11668         totalProperty: 'total',
11669         successProperty : 'success',
11670         root : 'data',
11671         id : 'id'
11672     });
11673     
11674     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11675 };
11676 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11677     
11678     /**
11679      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11680      * Used by Store query builder to append _requestMeta to params.
11681      * 
11682      */
11683     metaFromRemote : false,
11684     /**
11685      * This method is only used by a DataProxy which has retrieved data from a remote server.
11686      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11687      * @return {Object} data A data block which is used by an Roo.data.Store object as
11688      * a cache of Roo.data.Records.
11689      */
11690     read : function(response){
11691         var json = response.responseText;
11692        
11693         var o = /* eval:var:o */ eval("("+json+")");
11694         if(!o) {
11695             throw {message: "JsonReader.read: Json object not found"};
11696         }
11697         
11698         if(o.metaData){
11699             
11700             delete this.ef;
11701             this.metaFromRemote = true;
11702             this.meta = o.metaData;
11703             this.recordType = Roo.data.Record.create(o.metaData.fields);
11704             this.onMetaChange(this.meta, this.recordType, o);
11705         }
11706         return this.readRecords(o);
11707     },
11708
11709     // private function a store will implement
11710     onMetaChange : function(meta, recordType, o){
11711
11712     },
11713
11714     /**
11715          * @ignore
11716          */
11717     simpleAccess: function(obj, subsc) {
11718         return obj[subsc];
11719     },
11720
11721         /**
11722          * @ignore
11723          */
11724     getJsonAccessor: function(){
11725         var re = /[\[\.]/;
11726         return function(expr) {
11727             try {
11728                 return(re.test(expr))
11729                     ? new Function("obj", "return obj." + expr)
11730                     : function(obj){
11731                         return obj[expr];
11732                     };
11733             } catch(e){}
11734             return Roo.emptyFn;
11735         };
11736     }(),
11737
11738     /**
11739      * Create a data block containing Roo.data.Records from an XML document.
11740      * @param {Object} o An object which contains an Array of row objects in the property specified
11741      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11742      * which contains the total size of the dataset.
11743      * @return {Object} data A data block which is used by an Roo.data.Store object as
11744      * a cache of Roo.data.Records.
11745      */
11746     readRecords : function(o){
11747         /**
11748          * After any data loads, the raw JSON data is available for further custom processing.
11749          * @type Object
11750          */
11751         this.o = o;
11752         var s = this.meta, Record = this.recordType,
11753             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11754
11755 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11756         if (!this.ef) {
11757             if(s.totalProperty) {
11758                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11759                 }
11760                 if(s.successProperty) {
11761                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11762                 }
11763                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11764                 if (s.id) {
11765                         var g = this.getJsonAccessor(s.id);
11766                         this.getId = function(rec) {
11767                                 var r = g(rec);  
11768                                 return (r === undefined || r === "") ? null : r;
11769                         };
11770                 } else {
11771                         this.getId = function(){return null;};
11772                 }
11773             this.ef = [];
11774             for(var jj = 0; jj < fl; jj++){
11775                 f = fi[jj];
11776                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11777                 this.ef[jj] = this.getJsonAccessor(map);
11778             }
11779         }
11780
11781         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11782         if(s.totalProperty){
11783             var vt = parseInt(this.getTotal(o), 10);
11784             if(!isNaN(vt)){
11785                 totalRecords = vt;
11786             }
11787         }
11788         if(s.successProperty){
11789             var vs = this.getSuccess(o);
11790             if(vs === false || vs === 'false'){
11791                 success = false;
11792             }
11793         }
11794         var records = [];
11795         for(var i = 0; i < c; i++){
11796                 var n = root[i];
11797             var values = {};
11798             var id = this.getId(n);
11799             for(var j = 0; j < fl; j++){
11800                 f = fi[j];
11801             var v = this.ef[j](n);
11802             if (!f.convert) {
11803                 Roo.log('missing convert for ' + f.name);
11804                 Roo.log(f);
11805                 continue;
11806             }
11807             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11808             }
11809             var record = new Record(values, id);
11810             record.json = n;
11811             records[i] = record;
11812         }
11813         return {
11814             raw : o,
11815             success : success,
11816             records : records,
11817             totalRecords : totalRecords
11818         };
11819     }
11820 });/*
11821  * Based on:
11822  * Ext JS Library 1.1.1
11823  * Copyright(c) 2006-2007, Ext JS, LLC.
11824  *
11825  * Originally Released Under LGPL - original licence link has changed is not relivant.
11826  *
11827  * Fork - LGPL
11828  * <script type="text/javascript">
11829  */
11830
11831 /**
11832  * @class Roo.data.ArrayReader
11833  * @extends Roo.data.DataReader
11834  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11835  * Each element of that Array represents a row of data fields. The
11836  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11837  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11838  * <p>
11839  * Example code:.
11840  * <pre><code>
11841 var RecordDef = Roo.data.Record.create([
11842     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11843     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11844 ]);
11845 var myReader = new Roo.data.ArrayReader({
11846     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11847 }, RecordDef);
11848 </code></pre>
11849  * <p>
11850  * This would consume an Array like this:
11851  * <pre><code>
11852 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11853   </code></pre>
11854  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11855  * @constructor
11856  * Create a new JsonReader
11857  * @param {Object} meta Metadata configuration options.
11858  * @param {Object} recordType Either an Array of field definition objects
11859  * as specified to {@link Roo.data.Record#create},
11860  * or an {@link Roo.data.Record} object
11861  * created using {@link Roo.data.Record#create}.
11862  */
11863 Roo.data.ArrayReader = function(meta, recordType){
11864     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11865 };
11866
11867 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11868     /**
11869      * Create a data block containing Roo.data.Records from an XML document.
11870      * @param {Object} o An Array of row objects which represents the dataset.
11871      * @return {Object} data A data block which is used by an Roo.data.Store object as
11872      * a cache of Roo.data.Records.
11873      */
11874     readRecords : function(o){
11875         var sid = this.meta ? this.meta.id : null;
11876         var recordType = this.recordType, fields = recordType.prototype.fields;
11877         var records = [];
11878         var root = o;
11879             for(var i = 0; i < root.length; i++){
11880                     var n = root[i];
11881                 var values = {};
11882                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11883                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11884                 var f = fields.items[j];
11885                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11886                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11887                 v = f.convert(v);
11888                 values[f.name] = v;
11889             }
11890                 var record = new recordType(values, id);
11891                 record.json = n;
11892                 records[records.length] = record;
11893             }
11894             return {
11895                 records : records,
11896                 totalRecords : records.length
11897             };
11898     }
11899 });/*
11900  * - LGPL
11901  * * 
11902  */
11903
11904 /**
11905  * @class Roo.bootstrap.ComboBox
11906  * @extends Roo.bootstrap.TriggerField
11907  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11908  * @cfg {Boolean} append (true|false) default false
11909  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11910  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11911  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11912  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11913  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11914  * @cfg {Boolean} animate default true
11915  * @cfg {Boolean} emptyResultText only for touch device
11916  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11917  * @constructor
11918  * Create a new ComboBox.
11919  * @param {Object} config Configuration options
11920  */
11921 Roo.bootstrap.ComboBox = function(config){
11922     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11923     this.addEvents({
11924         /**
11925          * @event expand
11926          * Fires when the dropdown list is expanded
11927              * @param {Roo.bootstrap.ComboBox} combo This combo box
11928              */
11929         'expand' : true,
11930         /**
11931          * @event collapse
11932          * Fires when the dropdown list is collapsed
11933              * @param {Roo.bootstrap.ComboBox} combo This combo box
11934              */
11935         'collapse' : true,
11936         /**
11937          * @event beforeselect
11938          * Fires before a list item is selected. Return false to cancel the selection.
11939              * @param {Roo.bootstrap.ComboBox} combo This combo box
11940              * @param {Roo.data.Record} record The data record returned from the underlying store
11941              * @param {Number} index The index of the selected item in the dropdown list
11942              */
11943         'beforeselect' : true,
11944         /**
11945          * @event select
11946          * Fires when a list item is selected
11947              * @param {Roo.bootstrap.ComboBox} combo This combo box
11948              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11949              * @param {Number} index The index of the selected item in the dropdown list
11950              */
11951         'select' : true,
11952         /**
11953          * @event beforequery
11954          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11955          * The event object passed has these properties:
11956              * @param {Roo.bootstrap.ComboBox} combo This combo box
11957              * @param {String} query The query
11958              * @param {Boolean} forceAll true to force "all" query
11959              * @param {Boolean} cancel true to cancel the query
11960              * @param {Object} e The query event object
11961              */
11962         'beforequery': true,
11963          /**
11964          * @event add
11965          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11966              * @param {Roo.bootstrap.ComboBox} combo This combo box
11967              */
11968         'add' : true,
11969         /**
11970          * @event edit
11971          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11972              * @param {Roo.bootstrap.ComboBox} combo This combo box
11973              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11974              */
11975         'edit' : true,
11976         /**
11977          * @event remove
11978          * Fires when the remove value from the combobox array
11979              * @param {Roo.bootstrap.ComboBox} combo This combo box
11980              */
11981         'remove' : true,
11982         /**
11983          * @event afterremove
11984          * Fires when the remove value from the combobox array
11985              * @param {Roo.bootstrap.ComboBox} combo This combo box
11986              */
11987         'afterremove' : true,
11988         /**
11989          * @event specialfilter
11990          * Fires when specialfilter
11991             * @param {Roo.bootstrap.ComboBox} combo This combo box
11992             */
11993         'specialfilter' : true,
11994         /**
11995          * @event tick
11996          * Fires when tick the element
11997             * @param {Roo.bootstrap.ComboBox} combo This combo box
11998             */
11999         'tick' : true,
12000         /**
12001          * @event touchviewdisplay
12002          * Fires when touch view require special display (default is using displayField)
12003             * @param {Roo.bootstrap.ComboBox} combo This combo box
12004             * @param {Object} cfg set html .
12005             */
12006         'touchviewdisplay' : true
12007         
12008     });
12009     
12010     this.item = [];
12011     this.tickItems = [];
12012     
12013     this.selectedIndex = -1;
12014     if(this.mode == 'local'){
12015         if(config.queryDelay === undefined){
12016             this.queryDelay = 10;
12017         }
12018         if(config.minChars === undefined){
12019             this.minChars = 0;
12020         }
12021     }
12022 };
12023
12024 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12025      
12026     /**
12027      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12028      * rendering into an Roo.Editor, defaults to false)
12029      */
12030     /**
12031      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12032      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12033      */
12034     /**
12035      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12036      */
12037     /**
12038      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12039      * the dropdown list (defaults to undefined, with no header element)
12040      */
12041
12042      /**
12043      * @cfg {String/Roo.Template} tpl The template to use to render the output
12044      */
12045      
12046      /**
12047      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12048      */
12049     listWidth: undefined,
12050     /**
12051      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12052      * mode = 'remote' or 'text' if mode = 'local')
12053      */
12054     displayField: undefined,
12055     
12056     /**
12057      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12058      * mode = 'remote' or 'value' if mode = 'local'). 
12059      * Note: use of a valueField requires the user make a selection
12060      * in order for a value to be mapped.
12061      */
12062     valueField: undefined,
12063     /**
12064      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12065      */
12066     modalTitle : '',
12067     
12068     /**
12069      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12070      * field's data value (defaults to the underlying DOM element's name)
12071      */
12072     hiddenName: undefined,
12073     /**
12074      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12075      */
12076     listClass: '',
12077     /**
12078      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12079      */
12080     selectedClass: 'active',
12081     
12082     /**
12083      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12084      */
12085     shadow:'sides',
12086     /**
12087      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12088      * anchor positions (defaults to 'tl-bl')
12089      */
12090     listAlign: 'tl-bl?',
12091     /**
12092      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12093      */
12094     maxHeight: 300,
12095     /**
12096      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12097      * query specified by the allQuery config option (defaults to 'query')
12098      */
12099     triggerAction: 'query',
12100     /**
12101      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12102      * (defaults to 4, does not apply if editable = false)
12103      */
12104     minChars : 4,
12105     /**
12106      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12107      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12108      */
12109     typeAhead: false,
12110     /**
12111      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12112      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12113      */
12114     queryDelay: 500,
12115     /**
12116      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12117      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12118      */
12119     pageSize: 0,
12120     /**
12121      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12122      * when editable = true (defaults to false)
12123      */
12124     selectOnFocus:false,
12125     /**
12126      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12127      */
12128     queryParam: 'query',
12129     /**
12130      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12131      * when mode = 'remote' (defaults to 'Loading...')
12132      */
12133     loadingText: 'Loading...',
12134     /**
12135      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12136      */
12137     resizable: false,
12138     /**
12139      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12140      */
12141     handleHeight : 8,
12142     /**
12143      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12144      * traditional select (defaults to true)
12145      */
12146     editable: true,
12147     /**
12148      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12149      */
12150     allQuery: '',
12151     /**
12152      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12153      */
12154     mode: 'remote',
12155     /**
12156      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12157      * listWidth has a higher value)
12158      */
12159     minListWidth : 70,
12160     /**
12161      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12162      * allow the user to set arbitrary text into the field (defaults to false)
12163      */
12164     forceSelection:false,
12165     /**
12166      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12167      * if typeAhead = true (defaults to 250)
12168      */
12169     typeAheadDelay : 250,
12170     /**
12171      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12172      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12173      */
12174     valueNotFoundText : undefined,
12175     /**
12176      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12177      */
12178     blockFocus : false,
12179     
12180     /**
12181      * @cfg {Boolean} disableClear Disable showing of clear button.
12182      */
12183     disableClear : false,
12184     /**
12185      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12186      */
12187     alwaysQuery : false,
12188     
12189     /**
12190      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12191      */
12192     multiple : false,
12193     
12194     /**
12195      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12196      */
12197     invalidClass : "has-warning",
12198     
12199     /**
12200      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12201      */
12202     validClass : "has-success",
12203     
12204     /**
12205      * @cfg {Boolean} specialFilter (true|false) special filter default false
12206      */
12207     specialFilter : false,
12208     
12209     /**
12210      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12211      */
12212     mobileTouchView : true,
12213     
12214     //private
12215     addicon : false,
12216     editicon: false,
12217     
12218     page: 0,
12219     hasQuery: false,
12220     append: false,
12221     loadNext: false,
12222     autoFocus : true,
12223     tickable : false,
12224     btnPosition : 'right',
12225     triggerList : true,
12226     showToggleBtn : true,
12227     animate : true,
12228     emptyResultText: 'Empty',
12229     triggerText : 'Select',
12230     
12231     // element that contains real text value.. (when hidden is used..)
12232     
12233     getAutoCreate : function()
12234     {
12235         var cfg = false;
12236         
12237         /*
12238          * Touch Devices
12239          */
12240         
12241         if(Roo.isTouch && this.mobileTouchView){
12242             cfg = this.getAutoCreateTouchView();
12243             return cfg;;
12244         }
12245         
12246         /*
12247          *  Normal ComboBox
12248          */
12249         if(!this.tickable){
12250             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12251             return cfg;
12252         }
12253         
12254         /*
12255          *  ComboBox with tickable selections
12256          */
12257              
12258         var align = this.labelAlign || this.parentLabelAlign();
12259         
12260         cfg = {
12261             cls : 'form-group roo-combobox-tickable' //input-group
12262         };
12263         
12264         var buttons = {
12265             tag : 'div',
12266             cls : 'tickable-buttons',
12267             cn : [
12268                 {
12269                     tag : 'button',
12270                     type : 'button',
12271                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12272                     html : this.triggerText
12273                 },
12274                 {
12275                     tag : 'button',
12276                     type : 'button',
12277                     name : 'ok',
12278                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12279                     html : 'Done'
12280                 },
12281                 {
12282                     tag : 'button',
12283                     type : 'button',
12284                     name : 'cancel',
12285                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12286                     html : 'Cancel'
12287                 }
12288             ]
12289         };
12290         
12291         if(this.editable){
12292             buttons.cn.unshift({
12293                 tag: 'input',
12294                 cls: 'roo-select2-search-field-input'
12295             });
12296         }
12297         
12298         var _this = this;
12299         
12300         Roo.each(buttons.cn, function(c){
12301             if (_this.size) {
12302                 c.cls += ' btn-' + _this.size;
12303             }
12304
12305             if (_this.disabled) {
12306                 c.disabled = true;
12307             }
12308         });
12309         
12310         var box = {
12311             tag: 'div',
12312             cn: [
12313                 {
12314                     tag: 'input',
12315                     type : 'hidden',
12316                     cls: 'form-hidden-field'
12317                 },
12318                 {
12319                     tag: 'ul',
12320                     cls: 'roo-select2-choices',
12321                     cn:[
12322                         {
12323                             tag: 'li',
12324                             cls: 'roo-select2-search-field',
12325                             cn: [
12326
12327                                 buttons
12328                             ]
12329                         }
12330                     ]
12331                 }
12332             ]
12333         };
12334         
12335         var combobox = {
12336             cls: 'roo-select2-container input-group roo-select2-container-multi',
12337             cn: [
12338                 box
12339 //                {
12340 //                    tag: 'ul',
12341 //                    cls: 'typeahead typeahead-long dropdown-menu',
12342 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12343 //                }
12344             ]
12345         };
12346         
12347         if(this.hasFeedback && !this.allowBlank){
12348             
12349             var feedback = {
12350                 tag: 'span',
12351                 cls: 'glyphicon form-control-feedback'
12352             };
12353
12354             combobox.cn.push(feedback);
12355         }
12356         
12357         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12358             
12359 //                Roo.log("left and has label");
12360             cfg.cn = [
12361                 {
12362                     tag : 'i',
12363                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12364                     tooltip : 'This field is required'
12365                 },
12366                 {
12367                     tag: 'label',
12368                     'for' :  id,
12369                     cls : 'control-label col-sm-' + this.labelWidth,
12370                     html : this.fieldLabel
12371
12372                 },
12373                 {
12374                     cls : "col-sm-" + (12 - this.labelWidth), 
12375                     cn: [
12376                         combobox
12377                     ]
12378                 }
12379
12380             ];
12381
12382             if(this.indicatorpos == 'right'){
12383                 
12384                 cfg.cn = [
12385                     {
12386                         tag: 'label',
12387                         'for' :  id,
12388                         cls : 'control-label col-sm-' + this.labelWidth,
12389                         html : this.fieldLabel
12390
12391                     },
12392                     {
12393                         tag : 'i',
12394                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12395                         tooltip : 'This field is required'
12396                     },
12397                     {
12398                         cls : "col-sm-" + (12 - this.labelWidth), 
12399                         cn: [
12400                             combobox
12401                         ]
12402                     }
12403
12404                 ];
12405             
12406             }
12407                 
12408                 
12409         } else if ( this.fieldLabel.length) {
12410 //                Roo.log(" label");
12411                  cfg.cn = [
12412                     {
12413                         tag : 'i',
12414                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12415                         tooltip : 'This field is required'
12416                     },
12417                     {
12418                         tag: 'label',
12419                         //cls : 'input-group-addon',
12420                         html : this.fieldLabel
12421                         
12422                     },
12423                     
12424                     combobox
12425                     
12426                 ];
12427                 
12428                 if(this.indicatorpos == 'right'){
12429                     
12430                     cfg.cn = [
12431                         {
12432                             tag: 'label',
12433                             //cls : 'input-group-addon',
12434                             html : this.fieldLabel
12435
12436                         },
12437                         
12438                         {
12439                             tag : 'i',
12440                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12441                             tooltip : 'This field is required'
12442                         },
12443                         
12444                         combobox
12445
12446                     ];
12447                 
12448                 }
12449
12450         } else {
12451             
12452 //                Roo.log(" no label && no align");
12453                 cfg = combobox
12454                      
12455                 
12456         }
12457          
12458         var settings=this;
12459         ['xs','sm','md','lg'].map(function(size){
12460             if (settings[size]) {
12461                 cfg.cls += ' col-' + size + '-' + settings[size];
12462             }
12463         });
12464         
12465         return cfg;
12466         
12467     },
12468     
12469     _initEventsCalled : false,
12470     
12471     // private
12472     initEvents: function()
12473     {
12474         
12475         if (this._initEventsCalled) { // as we call render... prevent looping...
12476             return;
12477         }
12478         this._initEventsCalled = true;
12479         
12480         if (!this.store) {
12481             throw "can not find store for combo";
12482         }
12483         
12484         this.store = Roo.factory(this.store, Roo.data);
12485         
12486         // if we are building from html. then this element is so complex, that we can not really
12487         // use the rendered HTML.
12488         // so we have to trash and replace the previous code.
12489         if (Roo.XComponent.build_from_html) {
12490             
12491             // remove this element....
12492             var e = this.el.dom, k=0;
12493             while (e ) { e = e.previousSibling;  ++k;}
12494
12495             this.el.remove();
12496             
12497             this.el=false;
12498             this.rendered = false;
12499             
12500             this.render(this.parent().getChildContainer(true), k);
12501             
12502             
12503             
12504         }
12505         
12506         
12507         /*
12508          * Touch Devices
12509          */
12510         
12511         if(Roo.isTouch && this.mobileTouchView){
12512             this.initTouchView();
12513             return;
12514         }
12515         
12516         if(this.tickable){
12517             this.initTickableEvents();
12518             return;
12519         }
12520         
12521         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12522         
12523         if(this.hiddenName){
12524             
12525             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12526             
12527             this.hiddenField.dom.value =
12528                 this.hiddenValue !== undefined ? this.hiddenValue :
12529                 this.value !== undefined ? this.value : '';
12530
12531             // prevent input submission
12532             this.el.dom.removeAttribute('name');
12533             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12534              
12535              
12536         }
12537         //if(Roo.isGecko){
12538         //    this.el.dom.setAttribute('autocomplete', 'off');
12539         //}
12540         
12541         var cls = 'x-combo-list';
12542         
12543         //this.list = new Roo.Layer({
12544         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12545         //});
12546         
12547         var _this = this;
12548         
12549         (function(){
12550             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12551             _this.list.setWidth(lw);
12552         }).defer(100);
12553         
12554         this.list.on('mouseover', this.onViewOver, this);
12555         this.list.on('mousemove', this.onViewMove, this);
12556         
12557         this.list.on('scroll', this.onViewScroll, this);
12558         
12559         /*
12560         this.list.swallowEvent('mousewheel');
12561         this.assetHeight = 0;
12562
12563         if(this.title){
12564             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12565             this.assetHeight += this.header.getHeight();
12566         }
12567
12568         this.innerList = this.list.createChild({cls:cls+'-inner'});
12569         this.innerList.on('mouseover', this.onViewOver, this);
12570         this.innerList.on('mousemove', this.onViewMove, this);
12571         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12572         
12573         if(this.allowBlank && !this.pageSize && !this.disableClear){
12574             this.footer = this.list.createChild({cls:cls+'-ft'});
12575             this.pageTb = new Roo.Toolbar(this.footer);
12576            
12577         }
12578         if(this.pageSize){
12579             this.footer = this.list.createChild({cls:cls+'-ft'});
12580             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12581                     {pageSize: this.pageSize});
12582             
12583         }
12584         
12585         if (this.pageTb && this.allowBlank && !this.disableClear) {
12586             var _this = this;
12587             this.pageTb.add(new Roo.Toolbar.Fill(), {
12588                 cls: 'x-btn-icon x-btn-clear',
12589                 text: '&#160;',
12590                 handler: function()
12591                 {
12592                     _this.collapse();
12593                     _this.clearValue();
12594                     _this.onSelect(false, -1);
12595                 }
12596             });
12597         }
12598         if (this.footer) {
12599             this.assetHeight += this.footer.getHeight();
12600         }
12601         */
12602             
12603         if(!this.tpl){
12604             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12605         }
12606
12607         this.view = new Roo.View(this.list, this.tpl, {
12608             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12609         });
12610         //this.view.wrapEl.setDisplayed(false);
12611         this.view.on('click', this.onViewClick, this);
12612         
12613         
12614         
12615         this.store.on('beforeload', this.onBeforeLoad, this);
12616         this.store.on('load', this.onLoad, this);
12617         this.store.on('loadexception', this.onLoadException, this);
12618         /*
12619         if(this.resizable){
12620             this.resizer = new Roo.Resizable(this.list,  {
12621                pinned:true, handles:'se'
12622             });
12623             this.resizer.on('resize', function(r, w, h){
12624                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12625                 this.listWidth = w;
12626                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12627                 this.restrictHeight();
12628             }, this);
12629             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12630         }
12631         */
12632         if(!this.editable){
12633             this.editable = true;
12634             this.setEditable(false);
12635         }
12636         
12637         /*
12638         
12639         if (typeof(this.events.add.listeners) != 'undefined') {
12640             
12641             this.addicon = this.wrap.createChild(
12642                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12643        
12644             this.addicon.on('click', function(e) {
12645                 this.fireEvent('add', this);
12646             }, this);
12647         }
12648         if (typeof(this.events.edit.listeners) != 'undefined') {
12649             
12650             this.editicon = this.wrap.createChild(
12651                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12652             if (this.addicon) {
12653                 this.editicon.setStyle('margin-left', '40px');
12654             }
12655             this.editicon.on('click', function(e) {
12656                 
12657                 // we fire even  if inothing is selected..
12658                 this.fireEvent('edit', this, this.lastData );
12659                 
12660             }, this);
12661         }
12662         */
12663         
12664         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12665             "up" : function(e){
12666                 this.inKeyMode = true;
12667                 this.selectPrev();
12668             },
12669
12670             "down" : function(e){
12671                 if(!this.isExpanded()){
12672                     this.onTriggerClick();
12673                 }else{
12674                     this.inKeyMode = true;
12675                     this.selectNext();
12676                 }
12677             },
12678
12679             "enter" : function(e){
12680 //                this.onViewClick();
12681                 //return true;
12682                 this.collapse();
12683                 
12684                 if(this.fireEvent("specialkey", this, e)){
12685                     this.onViewClick(false);
12686                 }
12687                 
12688                 return true;
12689             },
12690
12691             "esc" : function(e){
12692                 this.collapse();
12693             },
12694
12695             "tab" : function(e){
12696                 this.collapse();
12697                 
12698                 if(this.fireEvent("specialkey", this, e)){
12699                     this.onViewClick(false);
12700                 }
12701                 
12702                 return true;
12703             },
12704
12705             scope : this,
12706
12707             doRelay : function(foo, bar, hname){
12708                 if(hname == 'down' || this.scope.isExpanded()){
12709                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12710                 }
12711                 return true;
12712             },
12713
12714             forceKeyDown: true
12715         });
12716         
12717         
12718         this.queryDelay = Math.max(this.queryDelay || 10,
12719                 this.mode == 'local' ? 10 : 250);
12720         
12721         
12722         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12723         
12724         if(this.typeAhead){
12725             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12726         }
12727         if(this.editable !== false){
12728             this.inputEl().on("keyup", this.onKeyUp, this);
12729         }
12730         if(this.forceSelection){
12731             this.inputEl().on('blur', this.doForce, this);
12732         }
12733         
12734         if(this.multiple){
12735             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12736             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12737         }
12738     },
12739     
12740     initTickableEvents: function()
12741     {   
12742         this.createList();
12743         
12744         if(this.hiddenName){
12745             
12746             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12747             
12748             this.hiddenField.dom.value =
12749                 this.hiddenValue !== undefined ? this.hiddenValue :
12750                 this.value !== undefined ? this.value : '';
12751
12752             // prevent input submission
12753             this.el.dom.removeAttribute('name');
12754             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12755              
12756              
12757         }
12758         
12759 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12760         
12761         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12762         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12763         if(this.triggerList){
12764             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12765         }
12766          
12767         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12768         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12769         
12770         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12771         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12772         
12773         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12774         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12775         
12776         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12777         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12778         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12779         
12780         this.okBtn.hide();
12781         this.cancelBtn.hide();
12782         
12783         var _this = this;
12784         
12785         (function(){
12786             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12787             _this.list.setWidth(lw);
12788         }).defer(100);
12789         
12790         this.list.on('mouseover', this.onViewOver, this);
12791         this.list.on('mousemove', this.onViewMove, this);
12792         
12793         this.list.on('scroll', this.onViewScroll, this);
12794         
12795         if(!this.tpl){
12796             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>';
12797         }
12798
12799         this.view = new Roo.View(this.list, this.tpl, {
12800             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12801         });
12802         
12803         //this.view.wrapEl.setDisplayed(false);
12804         this.view.on('click', this.onViewClick, this);
12805         
12806         
12807         
12808         this.store.on('beforeload', this.onBeforeLoad, this);
12809         this.store.on('load', this.onLoad, this);
12810         this.store.on('loadexception', this.onLoadException, this);
12811         
12812         if(this.editable){
12813             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12814                 "up" : function(e){
12815                     this.inKeyMode = true;
12816                     this.selectPrev();
12817                 },
12818
12819                 "down" : function(e){
12820                     this.inKeyMode = true;
12821                     this.selectNext();
12822                 },
12823
12824                 "enter" : function(e){
12825                     if(this.fireEvent("specialkey", this, e)){
12826                         this.onViewClick(false);
12827                     }
12828                     
12829                     return true;
12830                 },
12831
12832                 "esc" : function(e){
12833                     this.onTickableFooterButtonClick(e, false, false);
12834                 },
12835
12836                 "tab" : function(e){
12837                     this.fireEvent("specialkey", this, e);
12838                     
12839                     this.onTickableFooterButtonClick(e, false, false);
12840                     
12841                     return true;
12842                 },
12843
12844                 scope : this,
12845
12846                 doRelay : function(e, fn, key){
12847                     if(this.scope.isExpanded()){
12848                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12849                     }
12850                     return true;
12851                 },
12852
12853                 forceKeyDown: true
12854             });
12855         }
12856         
12857         this.queryDelay = Math.max(this.queryDelay || 10,
12858                 this.mode == 'local' ? 10 : 250);
12859         
12860         
12861         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12862         
12863         if(this.typeAhead){
12864             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12865         }
12866         
12867         if(this.editable !== false){
12868             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12869         }
12870         
12871     },
12872
12873     onDestroy : function(){
12874         if(this.view){
12875             this.view.setStore(null);
12876             this.view.el.removeAllListeners();
12877             this.view.el.remove();
12878             this.view.purgeListeners();
12879         }
12880         if(this.list){
12881             this.list.dom.innerHTML  = '';
12882         }
12883         
12884         if(this.store){
12885             this.store.un('beforeload', this.onBeforeLoad, this);
12886             this.store.un('load', this.onLoad, this);
12887             this.store.un('loadexception', this.onLoadException, this);
12888         }
12889         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12890     },
12891
12892     // private
12893     fireKey : function(e){
12894         if(e.isNavKeyPress() && !this.list.isVisible()){
12895             this.fireEvent("specialkey", this, e);
12896         }
12897     },
12898
12899     // private
12900     onResize: function(w, h){
12901 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12902 //        
12903 //        if(typeof w != 'number'){
12904 //            // we do not handle it!?!?
12905 //            return;
12906 //        }
12907 //        var tw = this.trigger.getWidth();
12908 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12909 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12910 //        var x = w - tw;
12911 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12912 //            
12913 //        //this.trigger.setStyle('left', x+'px');
12914 //        
12915 //        if(this.list && this.listWidth === undefined){
12916 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12917 //            this.list.setWidth(lw);
12918 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12919 //        }
12920         
12921     
12922         
12923     },
12924
12925     /**
12926      * Allow or prevent the user from directly editing the field text.  If false is passed,
12927      * the user will only be able to select from the items defined in the dropdown list.  This method
12928      * is the runtime equivalent of setting the 'editable' config option at config time.
12929      * @param {Boolean} value True to allow the user to directly edit the field text
12930      */
12931     setEditable : function(value){
12932         if(value == this.editable){
12933             return;
12934         }
12935         this.editable = value;
12936         if(!value){
12937             this.inputEl().dom.setAttribute('readOnly', true);
12938             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12939             this.inputEl().addClass('x-combo-noedit');
12940         }else{
12941             this.inputEl().dom.setAttribute('readOnly', false);
12942             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12943             this.inputEl().removeClass('x-combo-noedit');
12944         }
12945     },
12946
12947     // private
12948     
12949     onBeforeLoad : function(combo,opts){
12950         if(!this.hasFocus){
12951             return;
12952         }
12953          if (!opts.add) {
12954             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12955          }
12956         this.restrictHeight();
12957         this.selectedIndex = -1;
12958     },
12959
12960     // private
12961     onLoad : function(){
12962         
12963         this.hasQuery = false;
12964         
12965         if(!this.hasFocus){
12966             return;
12967         }
12968         
12969         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12970             this.loading.hide();
12971         }
12972              
12973         if(this.store.getCount() > 0){
12974             this.expand();
12975             this.restrictHeight();
12976             if(this.lastQuery == this.allQuery){
12977                 if(this.editable && !this.tickable){
12978                     this.inputEl().dom.select();
12979                 }
12980                 
12981                 if(
12982                     !this.selectByValue(this.value, true) &&
12983                     this.autoFocus && 
12984                     (
12985                         !this.store.lastOptions ||
12986                         typeof(this.store.lastOptions.add) == 'undefined' || 
12987                         this.store.lastOptions.add != true
12988                     )
12989                 ){
12990                     this.select(0, true);
12991                 }
12992             }else{
12993                 if(this.autoFocus){
12994                     this.selectNext();
12995                 }
12996                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12997                     this.taTask.delay(this.typeAheadDelay);
12998                 }
12999             }
13000         }else{
13001             this.onEmptyResults();
13002         }
13003         
13004         //this.el.focus();
13005     },
13006     // private
13007     onLoadException : function()
13008     {
13009         this.hasQuery = false;
13010         
13011         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13012             this.loading.hide();
13013         }
13014         
13015         if(this.tickable && this.editable){
13016             return;
13017         }
13018         
13019         this.collapse();
13020         // only causes errors at present
13021         //Roo.log(this.store.reader.jsonData);
13022         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13023             // fixme
13024             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13025         //}
13026         
13027         
13028     },
13029     // private
13030     onTypeAhead : function(){
13031         if(this.store.getCount() > 0){
13032             var r = this.store.getAt(0);
13033             var newValue = r.data[this.displayField];
13034             var len = newValue.length;
13035             var selStart = this.getRawValue().length;
13036             
13037             if(selStart != len){
13038                 this.setRawValue(newValue);
13039                 this.selectText(selStart, newValue.length);
13040             }
13041         }
13042     },
13043
13044     // private
13045     onSelect : function(record, index){
13046         
13047         if(this.fireEvent('beforeselect', this, record, index) !== false){
13048         
13049             this.setFromData(index > -1 ? record.data : false);
13050             
13051             this.collapse();
13052             this.fireEvent('select', this, record, index);
13053         }
13054     },
13055
13056     /**
13057      * Returns the currently selected field value or empty string if no value is set.
13058      * @return {String} value The selected value
13059      */
13060     getValue : function(){
13061         
13062         if(this.multiple){
13063             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13064         }
13065         
13066         if(this.valueField){
13067             return typeof this.value != 'undefined' ? this.value : '';
13068         }else{
13069             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13070         }
13071     },
13072
13073     /**
13074      * Clears any text/value currently set in the field
13075      */
13076     clearValue : function(){
13077         if(this.hiddenField){
13078             this.hiddenField.dom.value = '';
13079         }
13080         this.value = '';
13081         this.setRawValue('');
13082         this.lastSelectionText = '';
13083         this.lastData = false;
13084         
13085         var close = this.closeTriggerEl();
13086         
13087         if(close){
13088             close.hide();
13089         }
13090         
13091         this.validate();
13092         
13093     },
13094
13095     /**
13096      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13097      * will be displayed in the field.  If the value does not match the data value of an existing item,
13098      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13099      * Otherwise the field will be blank (although the value will still be set).
13100      * @param {String} value The value to match
13101      */
13102     setValue : function(v){
13103         if(this.multiple){
13104             this.syncValue();
13105             return;
13106         }
13107         
13108         var text = v;
13109         if(this.valueField){
13110             var r = this.findRecord(this.valueField, v);
13111             if(r){
13112                 text = r.data[this.displayField];
13113             }else if(this.valueNotFoundText !== undefined){
13114                 text = this.valueNotFoundText;
13115             }
13116         }
13117         this.lastSelectionText = text;
13118         if(this.hiddenField){
13119             this.hiddenField.dom.value = v;
13120         }
13121         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13122         this.value = v;
13123         
13124         var close = this.closeTriggerEl();
13125         
13126         if(close){
13127             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13128         }
13129         
13130         this.validate();
13131     },
13132     /**
13133      * @property {Object} the last set data for the element
13134      */
13135     
13136     lastData : false,
13137     /**
13138      * Sets the value of the field based on a object which is related to the record format for the store.
13139      * @param {Object} value the value to set as. or false on reset?
13140      */
13141     setFromData : function(o){
13142         
13143         if(this.multiple){
13144             this.addItem(o);
13145             return;
13146         }
13147             
13148         var dv = ''; // display value
13149         var vv = ''; // value value..
13150         this.lastData = o;
13151         if (this.displayField) {
13152             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13153         } else {
13154             // this is an error condition!!!
13155             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13156         }
13157         
13158         if(this.valueField){
13159             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13160         }
13161         
13162         var close = this.closeTriggerEl();
13163         
13164         if(close){
13165             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13166         }
13167         
13168         if(this.hiddenField){
13169             this.hiddenField.dom.value = vv;
13170             
13171             this.lastSelectionText = dv;
13172             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13173             this.value = vv;
13174             return;
13175         }
13176         // no hidden field.. - we store the value in 'value', but still display
13177         // display field!!!!
13178         this.lastSelectionText = dv;
13179         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13180         this.value = vv;
13181         
13182         
13183         
13184     },
13185     // private
13186     reset : function(){
13187         // overridden so that last data is reset..
13188         
13189         if(this.multiple){
13190             this.clearItem();
13191             return;
13192         }
13193         
13194         this.setValue(this.originalValue);
13195         //this.clearInvalid();
13196         this.lastData = false;
13197         if (this.view) {
13198             this.view.clearSelections();
13199         }
13200         
13201         this.validate();
13202     },
13203     // private
13204     findRecord : function(prop, value){
13205         var record;
13206         if(this.store.getCount() > 0){
13207             this.store.each(function(r){
13208                 if(r.data[prop] == value){
13209                     record = r;
13210                     return false;
13211                 }
13212                 return true;
13213             });
13214         }
13215         return record;
13216     },
13217     
13218     getName: function()
13219     {
13220         // returns hidden if it's set..
13221         if (!this.rendered) {return ''};
13222         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13223         
13224     },
13225     // private
13226     onViewMove : function(e, t){
13227         this.inKeyMode = false;
13228     },
13229
13230     // private
13231     onViewOver : function(e, t){
13232         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13233             return;
13234         }
13235         var item = this.view.findItemFromChild(t);
13236         
13237         if(item){
13238             var index = this.view.indexOf(item);
13239             this.select(index, false);
13240         }
13241     },
13242
13243     // private
13244     onViewClick : function(view, doFocus, el, e)
13245     {
13246         var index = this.view.getSelectedIndexes()[0];
13247         
13248         var r = this.store.getAt(index);
13249         
13250         if(this.tickable){
13251             
13252             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13253                 return;
13254             }
13255             
13256             var rm = false;
13257             var _this = this;
13258             
13259             Roo.each(this.tickItems, function(v,k){
13260                 
13261                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13262                     Roo.log(v);
13263                     _this.tickItems.splice(k, 1);
13264                     
13265                     if(typeof(e) == 'undefined' && view == false){
13266                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13267                     }
13268                     
13269                     rm = true;
13270                     return;
13271                 }
13272             });
13273             
13274             if(rm){
13275                 return;
13276             }
13277             
13278             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13279                 this.tickItems.push(r.data);
13280             }
13281             
13282             if(typeof(e) == 'undefined' && view == false){
13283                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13284             }
13285                     
13286             return;
13287         }
13288         
13289         if(r){
13290             this.onSelect(r, index);
13291         }
13292         if(doFocus !== false && !this.blockFocus){
13293             this.inputEl().focus();
13294         }
13295     },
13296
13297     // private
13298     restrictHeight : function(){
13299         //this.innerList.dom.style.height = '';
13300         //var inner = this.innerList.dom;
13301         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13302         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13303         //this.list.beginUpdate();
13304         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13305         this.list.alignTo(this.inputEl(), this.listAlign);
13306         this.list.alignTo(this.inputEl(), this.listAlign);
13307         //this.list.endUpdate();
13308     },
13309
13310     // private
13311     onEmptyResults : function(){
13312         
13313         if(this.tickable && this.editable){
13314             this.restrictHeight();
13315             return;
13316         }
13317         
13318         this.collapse();
13319     },
13320
13321     /**
13322      * Returns true if the dropdown list is expanded, else false.
13323      */
13324     isExpanded : function(){
13325         return this.list.isVisible();
13326     },
13327
13328     /**
13329      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13330      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13331      * @param {String} value The data value of the item to select
13332      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13333      * selected item if it is not currently in view (defaults to true)
13334      * @return {Boolean} True if the value matched an item in the list, else false
13335      */
13336     selectByValue : function(v, scrollIntoView){
13337         if(v !== undefined && v !== null){
13338             var r = this.findRecord(this.valueField || this.displayField, v);
13339             if(r){
13340                 this.select(this.store.indexOf(r), scrollIntoView);
13341                 return true;
13342             }
13343         }
13344         return false;
13345     },
13346
13347     /**
13348      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13349      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13350      * @param {Number} index The zero-based index of the list item to select
13351      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13352      * selected item if it is not currently in view (defaults to true)
13353      */
13354     select : function(index, scrollIntoView){
13355         this.selectedIndex = index;
13356         this.view.select(index);
13357         if(scrollIntoView !== false){
13358             var el = this.view.getNode(index);
13359             /*
13360              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13361              */
13362             if(el){
13363                 this.list.scrollChildIntoView(el, false);
13364             }
13365         }
13366     },
13367
13368     // private
13369     selectNext : 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 < ct-1){
13375                 this.select(this.selectedIndex+1);
13376             }
13377         }
13378     },
13379
13380     // private
13381     selectPrev : function(){
13382         var ct = this.store.getCount();
13383         if(ct > 0){
13384             if(this.selectedIndex == -1){
13385                 this.select(0);
13386             }else if(this.selectedIndex != 0){
13387                 this.select(this.selectedIndex-1);
13388             }
13389         }
13390     },
13391
13392     // private
13393     onKeyUp : function(e){
13394         if(this.editable !== false && !e.isSpecialKey()){
13395             this.lastKey = e.getKey();
13396             this.dqTask.delay(this.queryDelay);
13397         }
13398     },
13399
13400     // private
13401     validateBlur : function(){
13402         return !this.list || !this.list.isVisible();   
13403     },
13404
13405     // private
13406     initQuery : function(){
13407         
13408         var v = this.getRawValue();
13409         
13410         if(this.tickable && this.editable){
13411             v = this.tickableInputEl().getValue();
13412         }
13413         
13414         this.doQuery(v);
13415     },
13416
13417     // private
13418     doForce : function(){
13419         if(this.inputEl().dom.value.length > 0){
13420             this.inputEl().dom.value =
13421                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13422              
13423         }
13424     },
13425
13426     /**
13427      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13428      * query allowing the query action to be canceled if needed.
13429      * @param {String} query The SQL query to execute
13430      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13431      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13432      * saved in the current store (defaults to false)
13433      */
13434     doQuery : function(q, forceAll){
13435         
13436         if(q === undefined || q === null){
13437             q = '';
13438         }
13439         var qe = {
13440             query: q,
13441             forceAll: forceAll,
13442             combo: this,
13443             cancel:false
13444         };
13445         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13446             return false;
13447         }
13448         q = qe.query;
13449         
13450         forceAll = qe.forceAll;
13451         if(forceAll === true || (q.length >= this.minChars)){
13452             
13453             this.hasQuery = true;
13454             
13455             if(this.lastQuery != q || this.alwaysQuery){
13456                 this.lastQuery = q;
13457                 if(this.mode == 'local'){
13458                     this.selectedIndex = -1;
13459                     if(forceAll){
13460                         this.store.clearFilter();
13461                     }else{
13462                         
13463                         if(this.specialFilter){
13464                             this.fireEvent('specialfilter', this);
13465                             this.onLoad();
13466                             return;
13467                         }
13468                         
13469                         this.store.filter(this.displayField, q);
13470                     }
13471                     
13472                     this.store.fireEvent("datachanged", this.store);
13473                     
13474                     this.onLoad();
13475                     
13476                     
13477                 }else{
13478                     
13479                     this.store.baseParams[this.queryParam] = q;
13480                     
13481                     var options = {params : this.getParams(q)};
13482                     
13483                     if(this.loadNext){
13484                         options.add = true;
13485                         options.params.start = this.page * this.pageSize;
13486                     }
13487                     
13488                     this.store.load(options);
13489                     
13490                     /*
13491                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13492                      *  we should expand the list on onLoad
13493                      *  so command out it
13494                      */
13495 //                    this.expand();
13496                 }
13497             }else{
13498                 this.selectedIndex = -1;
13499                 this.onLoad();   
13500             }
13501         }
13502         
13503         this.loadNext = false;
13504     },
13505     
13506     // private
13507     getParams : function(q){
13508         var p = {};
13509         //p[this.queryParam] = q;
13510         
13511         if(this.pageSize){
13512             p.start = 0;
13513             p.limit = this.pageSize;
13514         }
13515         return p;
13516     },
13517
13518     /**
13519      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13520      */
13521     collapse : function(){
13522         if(!this.isExpanded()){
13523             return;
13524         }
13525         
13526         this.list.hide();
13527         
13528         if(this.tickable){
13529             this.hasFocus = false;
13530             this.okBtn.hide();
13531             this.cancelBtn.hide();
13532             this.trigger.show();
13533             
13534             if(this.editable){
13535                 this.tickableInputEl().dom.value = '';
13536                 this.tickableInputEl().blur();
13537             }
13538             
13539         }
13540         
13541         Roo.get(document).un('mousedown', this.collapseIf, this);
13542         Roo.get(document).un('mousewheel', this.collapseIf, this);
13543         if (!this.editable) {
13544             Roo.get(document).un('keydown', this.listKeyPress, this);
13545         }
13546         this.fireEvent('collapse', this);
13547         
13548         this.validate();
13549     },
13550
13551     // private
13552     collapseIf : function(e){
13553         var in_combo  = e.within(this.el);
13554         var in_list =  e.within(this.list);
13555         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13556         
13557         if (in_combo || in_list || is_list) {
13558             //e.stopPropagation();
13559             return;
13560         }
13561         
13562         if(this.tickable){
13563             this.onTickableFooterButtonClick(e, false, false);
13564         }
13565
13566         this.collapse();
13567         
13568     },
13569
13570     /**
13571      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13572      */
13573     expand : function(){
13574        
13575         if(this.isExpanded() || !this.hasFocus){
13576             return;
13577         }
13578         
13579         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13580         this.list.setWidth(lw);
13581         
13582         
13583          Roo.log('expand');
13584         
13585         this.list.show();
13586         
13587         this.restrictHeight();
13588         
13589         if(this.tickable){
13590             
13591             this.tickItems = Roo.apply([], this.item);
13592             
13593             this.okBtn.show();
13594             this.cancelBtn.show();
13595             this.trigger.hide();
13596             
13597             if(this.editable){
13598                 this.tickableInputEl().focus();
13599             }
13600             
13601         }
13602         
13603         Roo.get(document).on('mousedown', this.collapseIf, this);
13604         Roo.get(document).on('mousewheel', this.collapseIf, this);
13605         if (!this.editable) {
13606             Roo.get(document).on('keydown', this.listKeyPress, this);
13607         }
13608         
13609         this.fireEvent('expand', this);
13610     },
13611
13612     // private
13613     // Implements the default empty TriggerField.onTriggerClick function
13614     onTriggerClick : function(e)
13615     {
13616         Roo.log('trigger click');
13617         
13618         if(this.disabled || !this.triggerList){
13619             return;
13620         }
13621         
13622         this.page = 0;
13623         this.loadNext = false;
13624         
13625         if(this.isExpanded()){
13626             this.collapse();
13627             if (!this.blockFocus) {
13628                 this.inputEl().focus();
13629             }
13630             
13631         }else {
13632             this.hasFocus = true;
13633             if(this.triggerAction == 'all') {
13634                 this.doQuery(this.allQuery, true);
13635             } else {
13636                 this.doQuery(this.getRawValue());
13637             }
13638             if (!this.blockFocus) {
13639                 this.inputEl().focus();
13640             }
13641         }
13642     },
13643     
13644     onTickableTriggerClick : function(e)
13645     {
13646         if(this.disabled){
13647             return;
13648         }
13649         
13650         this.page = 0;
13651         this.loadNext = false;
13652         this.hasFocus = true;
13653         
13654         if(this.triggerAction == 'all') {
13655             this.doQuery(this.allQuery, true);
13656         } else {
13657             this.doQuery(this.getRawValue());
13658         }
13659     },
13660     
13661     onSearchFieldClick : function(e)
13662     {
13663         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13664             this.onTickableFooterButtonClick(e, false, false);
13665             return;
13666         }
13667         
13668         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13669             return;
13670         }
13671         
13672         this.page = 0;
13673         this.loadNext = false;
13674         this.hasFocus = true;
13675         
13676         if(this.triggerAction == 'all') {
13677             this.doQuery(this.allQuery, true);
13678         } else {
13679             this.doQuery(this.getRawValue());
13680         }
13681     },
13682     
13683     listKeyPress : function(e)
13684     {
13685         //Roo.log('listkeypress');
13686         // scroll to first matching element based on key pres..
13687         if (e.isSpecialKey()) {
13688             return false;
13689         }
13690         var k = String.fromCharCode(e.getKey()).toUpperCase();
13691         //Roo.log(k);
13692         var match  = false;
13693         var csel = this.view.getSelectedNodes();
13694         var cselitem = false;
13695         if (csel.length) {
13696             var ix = this.view.indexOf(csel[0]);
13697             cselitem  = this.store.getAt(ix);
13698             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13699                 cselitem = false;
13700             }
13701             
13702         }
13703         
13704         this.store.each(function(v) { 
13705             if (cselitem) {
13706                 // start at existing selection.
13707                 if (cselitem.id == v.id) {
13708                     cselitem = false;
13709                 }
13710                 return true;
13711             }
13712                 
13713             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13714                 match = this.store.indexOf(v);
13715                 return false;
13716             }
13717             return true;
13718         }, this);
13719         
13720         if (match === false) {
13721             return true; // no more action?
13722         }
13723         // scroll to?
13724         this.view.select(match);
13725         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13726         sn.scrollIntoView(sn.dom.parentNode, false);
13727     },
13728     
13729     onViewScroll : function(e, t){
13730         
13731         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){
13732             return;
13733         }
13734         
13735         this.hasQuery = true;
13736         
13737         this.loading = this.list.select('.loading', true).first();
13738         
13739         if(this.loading === null){
13740             this.list.createChild({
13741                 tag: 'div',
13742                 cls: 'loading roo-select2-more-results roo-select2-active',
13743                 html: 'Loading more results...'
13744             });
13745             
13746             this.loading = this.list.select('.loading', true).first();
13747             
13748             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13749             
13750             this.loading.hide();
13751         }
13752         
13753         this.loading.show();
13754         
13755         var _combo = this;
13756         
13757         this.page++;
13758         this.loadNext = true;
13759         
13760         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13761         
13762         return;
13763     },
13764     
13765     addItem : function(o)
13766     {   
13767         var dv = ''; // display value
13768         
13769         if (this.displayField) {
13770             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13771         } else {
13772             // this is an error condition!!!
13773             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13774         }
13775         
13776         if(!dv.length){
13777             return;
13778         }
13779         
13780         var choice = this.choices.createChild({
13781             tag: 'li',
13782             cls: 'roo-select2-search-choice',
13783             cn: [
13784                 {
13785                     tag: 'div',
13786                     html: dv
13787                 },
13788                 {
13789                     tag: 'a',
13790                     href: '#',
13791                     cls: 'roo-select2-search-choice-close',
13792                     tabindex: '-1'
13793                 }
13794             ]
13795             
13796         }, this.searchField);
13797         
13798         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13799         
13800         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13801         
13802         this.item.push(o);
13803         
13804         this.lastData = o;
13805         
13806         this.syncValue();
13807         
13808         this.inputEl().dom.value = '';
13809         
13810         this.validate();
13811     },
13812     
13813     onRemoveItem : function(e, _self, o)
13814     {
13815         e.preventDefault();
13816         
13817         this.lastItem = Roo.apply([], this.item);
13818         
13819         var index = this.item.indexOf(o.data) * 1;
13820         
13821         if( index < 0){
13822             Roo.log('not this item?!');
13823             return;
13824         }
13825         
13826         this.item.splice(index, 1);
13827         o.item.remove();
13828         
13829         this.syncValue();
13830         
13831         this.fireEvent('remove', this, e);
13832         
13833         this.validate();
13834         
13835     },
13836     
13837     syncValue : function()
13838     {
13839         if(!this.item.length){
13840             this.clearValue();
13841             return;
13842         }
13843             
13844         var value = [];
13845         var _this = this;
13846         Roo.each(this.item, function(i){
13847             if(_this.valueField){
13848                 value.push(i[_this.valueField]);
13849                 return;
13850             }
13851
13852             value.push(i);
13853         });
13854
13855         this.value = value.join(',');
13856
13857         if(this.hiddenField){
13858             this.hiddenField.dom.value = this.value;
13859         }
13860         
13861         this.store.fireEvent("datachanged", this.store);
13862         
13863         this.validate();
13864     },
13865     
13866     clearItem : function()
13867     {
13868         if(!this.multiple){
13869             return;
13870         }
13871         
13872         this.item = [];
13873         
13874         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13875            c.remove();
13876         });
13877         
13878         this.syncValue();
13879         
13880         this.validate();
13881         
13882         if(this.tickable && !Roo.isTouch){
13883             this.view.refresh();
13884         }
13885     },
13886     
13887     inputEl: function ()
13888     {
13889         if(Roo.isTouch && this.mobileTouchView){
13890             return this.el.select('input.form-control',true).first();
13891         }
13892         
13893         if(this.tickable){
13894             return this.searchField;
13895         }
13896         
13897         return this.el.select('input.form-control',true).first();
13898     },
13899     
13900     
13901     onTickableFooterButtonClick : function(e, btn, el)
13902     {
13903         e.preventDefault();
13904         
13905         this.lastItem = Roo.apply([], this.item);
13906         
13907         if(btn && btn.name == 'cancel'){
13908             this.tickItems = Roo.apply([], this.item);
13909             this.collapse();
13910             return;
13911         }
13912         
13913         this.clearItem();
13914         
13915         var _this = this;
13916         
13917         Roo.each(this.tickItems, function(o){
13918             _this.addItem(o);
13919         });
13920         
13921         this.collapse();
13922         
13923     },
13924     
13925     validate : function()
13926     {
13927         var v = this.getRawValue();
13928         
13929         if(this.multiple){
13930             v = this.getValue();
13931         }
13932         
13933         if(this.disabled || this.allowBlank || v.length){
13934             this.markValid();
13935             return true;
13936         }
13937         
13938         this.markInvalid();
13939         return false;
13940     },
13941     
13942     tickableInputEl : function()
13943     {
13944         if(!this.tickable || !this.editable){
13945             return this.inputEl();
13946         }
13947         
13948         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13949     },
13950     
13951     
13952     getAutoCreateTouchView : function()
13953     {
13954         var id = Roo.id();
13955         
13956         var cfg = {
13957             cls: 'form-group' //input-group
13958         };
13959         
13960         var input =  {
13961             tag: 'input',
13962             id : id,
13963             type : this.inputType,
13964             cls : 'form-control x-combo-noedit',
13965             autocomplete: 'new-password',
13966             placeholder : this.placeholder || '',
13967             readonly : true
13968         };
13969         
13970         if (this.name) {
13971             input.name = this.name;
13972         }
13973         
13974         if (this.size) {
13975             input.cls += ' input-' + this.size;
13976         }
13977         
13978         if (this.disabled) {
13979             input.disabled = true;
13980         }
13981         
13982         var inputblock = {
13983             cls : '',
13984             cn : [
13985                 input
13986             ]
13987         };
13988         
13989         if(this.before){
13990             inputblock.cls += ' input-group';
13991             
13992             inputblock.cn.unshift({
13993                 tag :'span',
13994                 cls : 'input-group-addon',
13995                 html : this.before
13996             });
13997         }
13998         
13999         if(this.removable && !this.multiple){
14000             inputblock.cls += ' roo-removable';
14001             
14002             inputblock.cn.push({
14003                 tag: 'button',
14004                 html : 'x',
14005                 cls : 'roo-combo-removable-btn close'
14006             });
14007         }
14008
14009         if(this.hasFeedback && !this.allowBlank){
14010             
14011             inputblock.cls += ' has-feedback';
14012             
14013             inputblock.cn.push({
14014                 tag: 'span',
14015                 cls: 'glyphicon form-control-feedback'
14016             });
14017             
14018         }
14019         
14020         if (this.after) {
14021             
14022             inputblock.cls += (this.before) ? '' : ' input-group';
14023             
14024             inputblock.cn.push({
14025                 tag :'span',
14026                 cls : 'input-group-addon',
14027                 html : this.after
14028             });
14029         }
14030
14031         var box = {
14032             tag: 'div',
14033             cn: [
14034                 {
14035                     tag: 'input',
14036                     type : 'hidden',
14037                     cls: 'form-hidden-field'
14038                 },
14039                 inputblock
14040             ]
14041             
14042         };
14043         
14044         if(this.multiple){
14045             box = {
14046                 tag: 'div',
14047                 cn: [
14048                     {
14049                         tag: 'input',
14050                         type : 'hidden',
14051                         cls: 'form-hidden-field'
14052                     },
14053                     {
14054                         tag: 'ul',
14055                         cls: 'roo-select2-choices',
14056                         cn:[
14057                             {
14058                                 tag: 'li',
14059                                 cls: 'roo-select2-search-field',
14060                                 cn: [
14061
14062                                     inputblock
14063                                 ]
14064                             }
14065                         ]
14066                     }
14067                 ]
14068             }
14069         };
14070         
14071         var combobox = {
14072             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14073             cn: [
14074                 box
14075             ]
14076         };
14077         
14078         if(!this.multiple && this.showToggleBtn){
14079             
14080             var caret = {
14081                         tag: 'span',
14082                         cls: 'caret'
14083             };
14084             
14085             if (this.caret != false) {
14086                 caret = {
14087                      tag: 'i',
14088                      cls: 'fa fa-' + this.caret
14089                 };
14090                 
14091             }
14092             
14093             combobox.cn.push({
14094                 tag :'span',
14095                 cls : 'input-group-addon btn dropdown-toggle',
14096                 cn : [
14097                     caret,
14098                     {
14099                         tag: 'span',
14100                         cls: 'combobox-clear',
14101                         cn  : [
14102                             {
14103                                 tag : 'i',
14104                                 cls: 'icon-remove'
14105                             }
14106                         ]
14107                     }
14108                 ]
14109
14110             })
14111         }
14112         
14113         if(this.multiple){
14114             combobox.cls += ' roo-select2-container-multi';
14115         }
14116         
14117         var align = this.labelAlign || this.parentLabelAlign();
14118         
14119         cfg.cn = combobox;
14120         
14121         if(this.fieldLabel.length && this.labelWidth){
14122             
14123             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14124             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14125             
14126             cfg.cn = [
14127                 {
14128                    tag : 'i',
14129                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14130                    tooltip : 'This field is required'
14131                 },
14132                 {
14133                     tag: 'label',
14134                     cls : 'control-label ' + lw,
14135                     html : this.fieldLabel
14136
14137                 },
14138                 {
14139                     cls : cw, 
14140                     cn: [
14141                         combobox
14142                     ]
14143                 }
14144             ];
14145             
14146             if(this.indicatorpos == 'right'){
14147                 cfg.cn = [
14148                     {
14149                         tag: 'label',
14150                         cls : 'control-label ' + lw,
14151                         html : this.fieldLabel
14152
14153                     },
14154                     {
14155                        tag : 'i',
14156                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14157                        tooltip : 'This field is required'
14158                     },
14159                     {
14160                         cls : cw, 
14161                         cn: [
14162                             combobox
14163                         ]
14164                     }
14165                 ];
14166             }
14167         }
14168         
14169         var settings = this;
14170         
14171         ['xs','sm','md','lg'].map(function(size){
14172             if (settings[size]) {
14173                 cfg.cls += ' col-' + size + '-' + settings[size];
14174             }
14175         });
14176         
14177         return cfg;
14178     },
14179     
14180     initTouchView : function()
14181     {
14182         this.renderTouchView();
14183         
14184         this.touchViewEl.on('scroll', function(){
14185             this.el.dom.scrollTop = 0;
14186         }, this);
14187         
14188         this.originalValue = this.getValue();
14189         
14190         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14191         
14192         this.inputEl().on("click", this.showTouchView, this);
14193         this.triggerEl.on("click", this.showTouchView, this);
14194         
14195         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14196         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14197         
14198         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14199         
14200         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14201         this.store.on('load', this.onTouchViewLoad, this);
14202         this.store.on('loadexception', this.onTouchViewLoadException, this);
14203         
14204         if(this.hiddenName){
14205             
14206             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14207             
14208             this.hiddenField.dom.value =
14209                 this.hiddenValue !== undefined ? this.hiddenValue :
14210                 this.value !== undefined ? this.value : '';
14211         
14212             this.el.dom.removeAttribute('name');
14213             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14214         }
14215         
14216         if(this.multiple){
14217             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14218             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14219         }
14220         
14221         if(this.removable && !this.multiple){
14222             var close = this.closeTriggerEl();
14223             if(close){
14224                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14225                 close.on('click', this.removeBtnClick, this, close);
14226             }
14227         }
14228         /*
14229          * fix the bug in Safari iOS8
14230          */
14231         this.inputEl().on("focus", function(e){
14232             document.activeElement.blur();
14233         }, this);
14234         
14235         return;
14236         
14237         
14238     },
14239     
14240     renderTouchView : function()
14241     {
14242         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14243         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14244         
14245         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14246         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14247         
14248         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14249         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14250         this.touchViewBodyEl.setStyle('overflow', 'auto');
14251         
14252         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14253         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14254         
14255         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14256         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14257         
14258     },
14259     
14260     showTouchView : function()
14261     {
14262         if(this.disabled){
14263             return;
14264         }
14265         
14266         this.touchViewHeaderEl.hide();
14267
14268         if(this.modalTitle.length){
14269             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14270             this.touchViewHeaderEl.show();
14271         }
14272
14273         this.touchViewEl.show();
14274
14275         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14276         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14277                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14278
14279         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14280
14281         if(this.modalTitle.length){
14282             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14283         }
14284         
14285         this.touchViewBodyEl.setHeight(bodyHeight);
14286
14287         if(this.animate){
14288             var _this = this;
14289             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14290         }else{
14291             this.touchViewEl.addClass('in');
14292         }
14293
14294         this.doTouchViewQuery();
14295         
14296     },
14297     
14298     hideTouchView : function()
14299     {
14300         this.touchViewEl.removeClass('in');
14301
14302         if(this.animate){
14303             var _this = this;
14304             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14305         }else{
14306             this.touchViewEl.setStyle('display', 'none');
14307         }
14308         
14309     },
14310     
14311     setTouchViewValue : function()
14312     {
14313         if(this.multiple){
14314             this.clearItem();
14315         
14316             var _this = this;
14317
14318             Roo.each(this.tickItems, function(o){
14319                 this.addItem(o);
14320             }, this);
14321         }
14322         
14323         this.hideTouchView();
14324     },
14325     
14326     doTouchViewQuery : function()
14327     {
14328         var qe = {
14329             query: '',
14330             forceAll: true,
14331             combo: this,
14332             cancel:false
14333         };
14334         
14335         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14336             return false;
14337         }
14338         
14339         if(!this.alwaysQuery || this.mode == 'local'){
14340             this.onTouchViewLoad();
14341             return;
14342         }
14343         
14344         this.store.load();
14345     },
14346     
14347     onTouchViewBeforeLoad : function(combo,opts)
14348     {
14349         return;
14350     },
14351
14352     // private
14353     onTouchViewLoad : function()
14354     {
14355         if(this.store.getCount() < 1){
14356             this.onTouchViewEmptyResults();
14357             return;
14358         }
14359         
14360         this.clearTouchView();
14361         
14362         var rawValue = this.getRawValue();
14363         
14364         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14365         
14366         this.tickItems = [];
14367         
14368         this.store.data.each(function(d, rowIndex){
14369             var row = this.touchViewListGroup.createChild(template);
14370             
14371             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14372                 row.addClass(d.data.cls);
14373             }
14374             
14375             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14376                 var cfg = {
14377                     data : d.data,
14378                     html : d.data[this.displayField]
14379                 };
14380                 
14381                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14382                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14383                 }
14384             }
14385             row.removeClass('selected');
14386             if(!this.multiple && this.valueField &&
14387                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14388             {
14389                 // radio buttons..
14390                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14391                 row.addClass('selected');
14392             }
14393             
14394             if(this.multiple && this.valueField &&
14395                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14396             {
14397                 
14398                 // checkboxes...
14399                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14400                 this.tickItems.push(d.data);
14401             }
14402             
14403             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14404             
14405         }, this);
14406         
14407         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14408         
14409         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14410
14411         if(this.modalTitle.length){
14412             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14413         }
14414
14415         var listHeight = this.touchViewListGroup.getHeight();
14416         
14417         var _this = this;
14418         
14419         if(firstChecked && listHeight > bodyHeight){
14420             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14421         }
14422         
14423     },
14424     
14425     onTouchViewLoadException : function()
14426     {
14427         this.hideTouchView();
14428     },
14429     
14430     onTouchViewEmptyResults : function()
14431     {
14432         this.clearTouchView();
14433         
14434         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14435         
14436         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14437         
14438     },
14439     
14440     clearTouchView : function()
14441     {
14442         this.touchViewListGroup.dom.innerHTML = '';
14443     },
14444     
14445     onTouchViewClick : function(e, el, o)
14446     {
14447         e.preventDefault();
14448         
14449         var row = o.row;
14450         var rowIndex = o.rowIndex;
14451         
14452         var r = this.store.getAt(rowIndex);
14453         
14454         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14455             
14456             if(!this.multiple){
14457                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14458                     c.dom.removeAttribute('checked');
14459                 }, this);
14460
14461                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14462
14463                 this.setFromData(r.data);
14464
14465                 var close = this.closeTriggerEl();
14466
14467                 if(close){
14468                     close.show();
14469                 }
14470
14471                 this.hideTouchView();
14472
14473                 this.fireEvent('select', this, r, rowIndex);
14474
14475                 return;
14476             }
14477
14478             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14479                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14480                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14481                 return;
14482             }
14483
14484             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14485             this.addItem(r.data);
14486             this.tickItems.push(r.data);
14487         }
14488     }
14489     
14490
14491     /** 
14492     * @cfg {Boolean} grow 
14493     * @hide 
14494     */
14495     /** 
14496     * @cfg {Number} growMin 
14497     * @hide 
14498     */
14499     /** 
14500     * @cfg {Number} growMax 
14501     * @hide 
14502     */
14503     /**
14504      * @hide
14505      * @method autoSize
14506      */
14507 });
14508
14509 Roo.apply(Roo.bootstrap.ComboBox,  {
14510     
14511     header : {
14512         tag: 'div',
14513         cls: 'modal-header',
14514         cn: [
14515             {
14516                 tag: 'h4',
14517                 cls: 'modal-title'
14518             }
14519         ]
14520     },
14521     
14522     body : {
14523         tag: 'div',
14524         cls: 'modal-body',
14525         cn: [
14526             {
14527                 tag: 'ul',
14528                 cls: 'list-group'
14529             }
14530         ]
14531     },
14532     
14533     listItemRadio : {
14534         tag: 'li',
14535         cls: 'list-group-item',
14536         cn: [
14537             {
14538                 tag: 'span',
14539                 cls: 'roo-combobox-list-group-item-value'
14540             },
14541             {
14542                 tag: 'div',
14543                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14544                 cn: [
14545                     {
14546                         tag: 'input',
14547                         type: 'radio'
14548                     },
14549                     {
14550                         tag: 'label'
14551                     }
14552                 ]
14553             }
14554         ]
14555     },
14556     
14557     listItemCheckbox : {
14558         tag: 'li',
14559         cls: 'list-group-item',
14560         cn: [
14561             {
14562                 tag: 'span',
14563                 cls: 'roo-combobox-list-group-item-value'
14564             },
14565             {
14566                 tag: 'div',
14567                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14568                 cn: [
14569                     {
14570                         tag: 'input',
14571                         type: 'checkbox'
14572                     },
14573                     {
14574                         tag: 'label'
14575                     }
14576                 ]
14577             }
14578         ]
14579     },
14580     
14581     emptyResult : {
14582         tag: 'div',
14583         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14584     },
14585     
14586     footer : {
14587         tag: 'div',
14588         cls: 'modal-footer',
14589         cn: [
14590             {
14591                 tag: 'div',
14592                 cls: 'row',
14593                 cn: [
14594                     {
14595                         tag: 'div',
14596                         cls: 'col-xs-6 text-left',
14597                         cn: {
14598                             tag: 'button',
14599                             cls: 'btn btn-danger roo-touch-view-cancel',
14600                             html: 'Cancel'
14601                         }
14602                     },
14603                     {
14604                         tag: 'div',
14605                         cls: 'col-xs-6 text-right',
14606                         cn: {
14607                             tag: 'button',
14608                             cls: 'btn btn-success roo-touch-view-ok',
14609                             html: 'OK'
14610                         }
14611                     }
14612                 ]
14613             }
14614         ]
14615         
14616     }
14617 });
14618
14619 Roo.apply(Roo.bootstrap.ComboBox,  {
14620     
14621     touchViewTemplate : {
14622         tag: 'div',
14623         cls: 'modal fade roo-combobox-touch-view',
14624         cn: [
14625             {
14626                 tag: 'div',
14627                 cls: 'modal-dialog',
14628                 style : 'position:fixed', // we have to fix position....
14629                 cn: [
14630                     {
14631                         tag: 'div',
14632                         cls: 'modal-content',
14633                         cn: [
14634                             Roo.bootstrap.ComboBox.header,
14635                             Roo.bootstrap.ComboBox.body,
14636                             Roo.bootstrap.ComboBox.footer
14637                         ]
14638                     }
14639                 ]
14640             }
14641         ]
14642     }
14643 });/*
14644  * Based on:
14645  * Ext JS Library 1.1.1
14646  * Copyright(c) 2006-2007, Ext JS, LLC.
14647  *
14648  * Originally Released Under LGPL - original licence link has changed is not relivant.
14649  *
14650  * Fork - LGPL
14651  * <script type="text/javascript">
14652  */
14653
14654 /**
14655  * @class Roo.View
14656  * @extends Roo.util.Observable
14657  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14658  * This class also supports single and multi selection modes. <br>
14659  * Create a data model bound view:
14660  <pre><code>
14661  var store = new Roo.data.Store(...);
14662
14663  var view = new Roo.View({
14664     el : "my-element",
14665     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14666  
14667     singleSelect: true,
14668     selectedClass: "ydataview-selected",
14669     store: store
14670  });
14671
14672  // listen for node click?
14673  view.on("click", function(vw, index, node, e){
14674  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14675  });
14676
14677  // load XML data
14678  dataModel.load("foobar.xml");
14679  </code></pre>
14680  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14681  * <br><br>
14682  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14683  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14684  * 
14685  * Note: old style constructor is still suported (container, template, config)
14686  * 
14687  * @constructor
14688  * Create a new View
14689  * @param {Object} config The config object
14690  * 
14691  */
14692 Roo.View = function(config, depreciated_tpl, depreciated_config){
14693     
14694     this.parent = false;
14695     
14696     if (typeof(depreciated_tpl) == 'undefined') {
14697         // new way.. - universal constructor.
14698         Roo.apply(this, config);
14699         this.el  = Roo.get(this.el);
14700     } else {
14701         // old format..
14702         this.el  = Roo.get(config);
14703         this.tpl = depreciated_tpl;
14704         Roo.apply(this, depreciated_config);
14705     }
14706     this.wrapEl  = this.el.wrap().wrap();
14707     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14708     
14709     
14710     if(typeof(this.tpl) == "string"){
14711         this.tpl = new Roo.Template(this.tpl);
14712     } else {
14713         // support xtype ctors..
14714         this.tpl = new Roo.factory(this.tpl, Roo);
14715     }
14716     
14717     
14718     this.tpl.compile();
14719     
14720     /** @private */
14721     this.addEvents({
14722         /**
14723          * @event beforeclick
14724          * Fires before a click is processed. Returns false to cancel the default action.
14725          * @param {Roo.View} this
14726          * @param {Number} index The index of the target node
14727          * @param {HTMLElement} node The target node
14728          * @param {Roo.EventObject} e The raw event object
14729          */
14730             "beforeclick" : true,
14731         /**
14732          * @event click
14733          * Fires when a template node is clicked.
14734          * @param {Roo.View} this
14735          * @param {Number} index The index of the target node
14736          * @param {HTMLElement} node The target node
14737          * @param {Roo.EventObject} e The raw event object
14738          */
14739             "click" : true,
14740         /**
14741          * @event dblclick
14742          * Fires when a template node is double clicked.
14743          * @param {Roo.View} this
14744          * @param {Number} index The index of the target node
14745          * @param {HTMLElement} node The target node
14746          * @param {Roo.EventObject} e The raw event object
14747          */
14748             "dblclick" : true,
14749         /**
14750          * @event contextmenu
14751          * Fires when a template node is right clicked.
14752          * @param {Roo.View} this
14753          * @param {Number} index The index of the target node
14754          * @param {HTMLElement} node The target node
14755          * @param {Roo.EventObject} e The raw event object
14756          */
14757             "contextmenu" : true,
14758         /**
14759          * @event selectionchange
14760          * Fires when the selected nodes change.
14761          * @param {Roo.View} this
14762          * @param {Array} selections Array of the selected nodes
14763          */
14764             "selectionchange" : true,
14765     
14766         /**
14767          * @event beforeselect
14768          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14769          * @param {Roo.View} this
14770          * @param {HTMLElement} node The node to be selected
14771          * @param {Array} selections Array of currently selected nodes
14772          */
14773             "beforeselect" : true,
14774         /**
14775          * @event preparedata
14776          * Fires on every row to render, to allow you to change the data.
14777          * @param {Roo.View} this
14778          * @param {Object} data to be rendered (change this)
14779          */
14780           "preparedata" : true
14781           
14782           
14783         });
14784
14785
14786
14787     this.el.on({
14788         "click": this.onClick,
14789         "dblclick": this.onDblClick,
14790         "contextmenu": this.onContextMenu,
14791         scope:this
14792     });
14793
14794     this.selections = [];
14795     this.nodes = [];
14796     this.cmp = new Roo.CompositeElementLite([]);
14797     if(this.store){
14798         this.store = Roo.factory(this.store, Roo.data);
14799         this.setStore(this.store, true);
14800     }
14801     
14802     if ( this.footer && this.footer.xtype) {
14803            
14804          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14805         
14806         this.footer.dataSource = this.store;
14807         this.footer.container = fctr;
14808         this.footer = Roo.factory(this.footer, Roo);
14809         fctr.insertFirst(this.el);
14810         
14811         // this is a bit insane - as the paging toolbar seems to detach the el..
14812 //        dom.parentNode.parentNode.parentNode
14813          // they get detached?
14814     }
14815     
14816     
14817     Roo.View.superclass.constructor.call(this);
14818     
14819     
14820 };
14821
14822 Roo.extend(Roo.View, Roo.util.Observable, {
14823     
14824      /**
14825      * @cfg {Roo.data.Store} store Data store to load data from.
14826      */
14827     store : false,
14828     
14829     /**
14830      * @cfg {String|Roo.Element} el The container element.
14831      */
14832     el : '',
14833     
14834     /**
14835      * @cfg {String|Roo.Template} tpl The template used by this View 
14836      */
14837     tpl : false,
14838     /**
14839      * @cfg {String} dataName the named area of the template to use as the data area
14840      *                          Works with domtemplates roo-name="name"
14841      */
14842     dataName: false,
14843     /**
14844      * @cfg {String} selectedClass The css class to add to selected nodes
14845      */
14846     selectedClass : "x-view-selected",
14847      /**
14848      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14849      */
14850     emptyText : "",
14851     
14852     /**
14853      * @cfg {String} text to display on mask (default Loading)
14854      */
14855     mask : false,
14856     /**
14857      * @cfg {Boolean} multiSelect Allow multiple selection
14858      */
14859     multiSelect : false,
14860     /**
14861      * @cfg {Boolean} singleSelect Allow single selection
14862      */
14863     singleSelect:  false,
14864     
14865     /**
14866      * @cfg {Boolean} toggleSelect - selecting 
14867      */
14868     toggleSelect : false,
14869     
14870     /**
14871      * @cfg {Boolean} tickable - selecting 
14872      */
14873     tickable : false,
14874     
14875     /**
14876      * Returns the element this view is bound to.
14877      * @return {Roo.Element}
14878      */
14879     getEl : function(){
14880         return this.wrapEl;
14881     },
14882     
14883     
14884
14885     /**
14886      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14887      */
14888     refresh : function(){
14889         //Roo.log('refresh');
14890         var t = this.tpl;
14891         
14892         // if we are using something like 'domtemplate', then
14893         // the what gets used is:
14894         // t.applySubtemplate(NAME, data, wrapping data..)
14895         // the outer template then get' applied with
14896         //     the store 'extra data'
14897         // and the body get's added to the
14898         //      roo-name="data" node?
14899         //      <span class='roo-tpl-{name}'></span> ?????
14900         
14901         
14902         
14903         this.clearSelections();
14904         this.el.update("");
14905         var html = [];
14906         var records = this.store.getRange();
14907         if(records.length < 1) {
14908             
14909             // is this valid??  = should it render a template??
14910             
14911             this.el.update(this.emptyText);
14912             return;
14913         }
14914         var el = this.el;
14915         if (this.dataName) {
14916             this.el.update(t.apply(this.store.meta)); //????
14917             el = this.el.child('.roo-tpl-' + this.dataName);
14918         }
14919         
14920         for(var i = 0, len = records.length; i < len; i++){
14921             var data = this.prepareData(records[i].data, i, records[i]);
14922             this.fireEvent("preparedata", this, data, i, records[i]);
14923             
14924             var d = Roo.apply({}, data);
14925             
14926             if(this.tickable){
14927                 Roo.apply(d, {'roo-id' : Roo.id()});
14928                 
14929                 var _this = this;
14930             
14931                 Roo.each(this.parent.item, function(item){
14932                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14933                         return;
14934                     }
14935                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14936                 });
14937             }
14938             
14939             html[html.length] = Roo.util.Format.trim(
14940                 this.dataName ?
14941                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14942                     t.apply(d)
14943             );
14944         }
14945         
14946         
14947         
14948         el.update(html.join(""));
14949         this.nodes = el.dom.childNodes;
14950         this.updateIndexes(0);
14951     },
14952     
14953
14954     /**
14955      * Function to override to reformat the data that is sent to
14956      * the template for each node.
14957      * DEPRICATED - use the preparedata event handler.
14958      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14959      * a JSON object for an UpdateManager bound view).
14960      */
14961     prepareData : function(data, index, record)
14962     {
14963         this.fireEvent("preparedata", this, data, index, record);
14964         return data;
14965     },
14966
14967     onUpdate : function(ds, record){
14968         // Roo.log('on update');   
14969         this.clearSelections();
14970         var index = this.store.indexOf(record);
14971         var n = this.nodes[index];
14972         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14973         n.parentNode.removeChild(n);
14974         this.updateIndexes(index, index);
14975     },
14976
14977     
14978     
14979 // --------- FIXME     
14980     onAdd : function(ds, records, index)
14981     {
14982         //Roo.log(['on Add', ds, records, index] );        
14983         this.clearSelections();
14984         if(this.nodes.length == 0){
14985             this.refresh();
14986             return;
14987         }
14988         var n = this.nodes[index];
14989         for(var i = 0, len = records.length; i < len; i++){
14990             var d = this.prepareData(records[i].data, i, records[i]);
14991             if(n){
14992                 this.tpl.insertBefore(n, d);
14993             }else{
14994                 
14995                 this.tpl.append(this.el, d);
14996             }
14997         }
14998         this.updateIndexes(index);
14999     },
15000
15001     onRemove : function(ds, record, index){
15002        // Roo.log('onRemove');
15003         this.clearSelections();
15004         var el = this.dataName  ?
15005             this.el.child('.roo-tpl-' + this.dataName) :
15006             this.el; 
15007         
15008         el.dom.removeChild(this.nodes[index]);
15009         this.updateIndexes(index);
15010     },
15011
15012     /**
15013      * Refresh an individual node.
15014      * @param {Number} index
15015      */
15016     refreshNode : function(index){
15017         this.onUpdate(this.store, this.store.getAt(index));
15018     },
15019
15020     updateIndexes : function(startIndex, endIndex){
15021         var ns = this.nodes;
15022         startIndex = startIndex || 0;
15023         endIndex = endIndex || ns.length - 1;
15024         for(var i = startIndex; i <= endIndex; i++){
15025             ns[i].nodeIndex = i;
15026         }
15027     },
15028
15029     /**
15030      * Changes the data store this view uses and refresh the view.
15031      * @param {Store} store
15032      */
15033     setStore : function(store, initial){
15034         if(!initial && this.store){
15035             this.store.un("datachanged", this.refresh);
15036             this.store.un("add", this.onAdd);
15037             this.store.un("remove", this.onRemove);
15038             this.store.un("update", this.onUpdate);
15039             this.store.un("clear", this.refresh);
15040             this.store.un("beforeload", this.onBeforeLoad);
15041             this.store.un("load", this.onLoad);
15042             this.store.un("loadexception", this.onLoad);
15043         }
15044         if(store){
15045           
15046             store.on("datachanged", this.refresh, this);
15047             store.on("add", this.onAdd, this);
15048             store.on("remove", this.onRemove, this);
15049             store.on("update", this.onUpdate, this);
15050             store.on("clear", this.refresh, this);
15051             store.on("beforeload", this.onBeforeLoad, this);
15052             store.on("load", this.onLoad, this);
15053             store.on("loadexception", this.onLoad, this);
15054         }
15055         
15056         if(store){
15057             this.refresh();
15058         }
15059     },
15060     /**
15061      * onbeforeLoad - masks the loading area.
15062      *
15063      */
15064     onBeforeLoad : function(store,opts)
15065     {
15066          //Roo.log('onBeforeLoad');   
15067         if (!opts.add) {
15068             this.el.update("");
15069         }
15070         this.el.mask(this.mask ? this.mask : "Loading" ); 
15071     },
15072     onLoad : function ()
15073     {
15074         this.el.unmask();
15075     },
15076     
15077
15078     /**
15079      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15080      * @param {HTMLElement} node
15081      * @return {HTMLElement} The template node
15082      */
15083     findItemFromChild : function(node){
15084         var el = this.dataName  ?
15085             this.el.child('.roo-tpl-' + this.dataName,true) :
15086             this.el.dom; 
15087         
15088         if(!node || node.parentNode == el){
15089                     return node;
15090             }
15091             var p = node.parentNode;
15092             while(p && p != el){
15093             if(p.parentNode == el){
15094                 return p;
15095             }
15096             p = p.parentNode;
15097         }
15098             return null;
15099     },
15100
15101     /** @ignore */
15102     onClick : function(e){
15103         var item = this.findItemFromChild(e.getTarget());
15104         if(item){
15105             var index = this.indexOf(item);
15106             if(this.onItemClick(item, index, e) !== false){
15107                 this.fireEvent("click", this, index, item, e);
15108             }
15109         }else{
15110             this.clearSelections();
15111         }
15112     },
15113
15114     /** @ignore */
15115     onContextMenu : function(e){
15116         var item = this.findItemFromChild(e.getTarget());
15117         if(item){
15118             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15119         }
15120     },
15121
15122     /** @ignore */
15123     onDblClick : function(e){
15124         var item = this.findItemFromChild(e.getTarget());
15125         if(item){
15126             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15127         }
15128     },
15129
15130     onItemClick : function(item, index, e)
15131     {
15132         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15133             return false;
15134         }
15135         if (this.toggleSelect) {
15136             var m = this.isSelected(item) ? 'unselect' : 'select';
15137             //Roo.log(m);
15138             var _t = this;
15139             _t[m](item, true, false);
15140             return true;
15141         }
15142         if(this.multiSelect || this.singleSelect){
15143             if(this.multiSelect && e.shiftKey && this.lastSelection){
15144                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15145             }else{
15146                 this.select(item, this.multiSelect && e.ctrlKey);
15147                 this.lastSelection = item;
15148             }
15149             
15150             if(!this.tickable){
15151                 e.preventDefault();
15152             }
15153             
15154         }
15155         return true;
15156     },
15157
15158     /**
15159      * Get the number of selected nodes.
15160      * @return {Number}
15161      */
15162     getSelectionCount : function(){
15163         return this.selections.length;
15164     },
15165
15166     /**
15167      * Get the currently selected nodes.
15168      * @return {Array} An array of HTMLElements
15169      */
15170     getSelectedNodes : function(){
15171         return this.selections;
15172     },
15173
15174     /**
15175      * Get the indexes of the selected nodes.
15176      * @return {Array}
15177      */
15178     getSelectedIndexes : function(){
15179         var indexes = [], s = this.selections;
15180         for(var i = 0, len = s.length; i < len; i++){
15181             indexes.push(s[i].nodeIndex);
15182         }
15183         return indexes;
15184     },
15185
15186     /**
15187      * Clear all selections
15188      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15189      */
15190     clearSelections : function(suppressEvent){
15191         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15192             this.cmp.elements = this.selections;
15193             this.cmp.removeClass(this.selectedClass);
15194             this.selections = [];
15195             if(!suppressEvent){
15196                 this.fireEvent("selectionchange", this, this.selections);
15197             }
15198         }
15199     },
15200
15201     /**
15202      * Returns true if the passed node is selected
15203      * @param {HTMLElement/Number} node The node or node index
15204      * @return {Boolean}
15205      */
15206     isSelected : function(node){
15207         var s = this.selections;
15208         if(s.length < 1){
15209             return false;
15210         }
15211         node = this.getNode(node);
15212         return s.indexOf(node) !== -1;
15213     },
15214
15215     /**
15216      * Selects nodes.
15217      * @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
15218      * @param {Boolean} keepExisting (optional) true to keep existing selections
15219      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15220      */
15221     select : function(nodeInfo, keepExisting, suppressEvent){
15222         if(nodeInfo instanceof Array){
15223             if(!keepExisting){
15224                 this.clearSelections(true);
15225             }
15226             for(var i = 0, len = nodeInfo.length; i < len; i++){
15227                 this.select(nodeInfo[i], true, true);
15228             }
15229             return;
15230         } 
15231         var node = this.getNode(nodeInfo);
15232         if(!node || this.isSelected(node)){
15233             return; // already selected.
15234         }
15235         if(!keepExisting){
15236             this.clearSelections(true);
15237         }
15238         
15239         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15240             Roo.fly(node).addClass(this.selectedClass);
15241             this.selections.push(node);
15242             if(!suppressEvent){
15243                 this.fireEvent("selectionchange", this, this.selections);
15244             }
15245         }
15246         
15247         
15248     },
15249       /**
15250      * Unselects nodes.
15251      * @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
15252      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15253      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15254      */
15255     unselect : function(nodeInfo, keepExisting, suppressEvent)
15256     {
15257         if(nodeInfo instanceof Array){
15258             Roo.each(this.selections, function(s) {
15259                 this.unselect(s, nodeInfo);
15260             }, this);
15261             return;
15262         }
15263         var node = this.getNode(nodeInfo);
15264         if(!node || !this.isSelected(node)){
15265             //Roo.log("not selected");
15266             return; // not selected.
15267         }
15268         // fireevent???
15269         var ns = [];
15270         Roo.each(this.selections, function(s) {
15271             if (s == node ) {
15272                 Roo.fly(node).removeClass(this.selectedClass);
15273
15274                 return;
15275             }
15276             ns.push(s);
15277         },this);
15278         
15279         this.selections= ns;
15280         this.fireEvent("selectionchange", this, this.selections);
15281     },
15282
15283     /**
15284      * Gets a template node.
15285      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15286      * @return {HTMLElement} The node or null if it wasn't found
15287      */
15288     getNode : function(nodeInfo){
15289         if(typeof nodeInfo == "string"){
15290             return document.getElementById(nodeInfo);
15291         }else if(typeof nodeInfo == "number"){
15292             return this.nodes[nodeInfo];
15293         }
15294         return nodeInfo;
15295     },
15296
15297     /**
15298      * Gets a range template nodes.
15299      * @param {Number} startIndex
15300      * @param {Number} endIndex
15301      * @return {Array} An array of nodes
15302      */
15303     getNodes : function(start, end){
15304         var ns = this.nodes;
15305         start = start || 0;
15306         end = typeof end == "undefined" ? ns.length - 1 : end;
15307         var nodes = [];
15308         if(start <= end){
15309             for(var i = start; i <= end; i++){
15310                 nodes.push(ns[i]);
15311             }
15312         } else{
15313             for(var i = start; i >= end; i--){
15314                 nodes.push(ns[i]);
15315             }
15316         }
15317         return nodes;
15318     },
15319
15320     /**
15321      * Finds the index of the passed node
15322      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15323      * @return {Number} The index of the node or -1
15324      */
15325     indexOf : function(node){
15326         node = this.getNode(node);
15327         if(typeof node.nodeIndex == "number"){
15328             return node.nodeIndex;
15329         }
15330         var ns = this.nodes;
15331         for(var i = 0, len = ns.length; i < len; i++){
15332             if(ns[i] == node){
15333                 return i;
15334             }
15335         }
15336         return -1;
15337     }
15338 });
15339 /*
15340  * - LGPL
15341  *
15342  * based on jquery fullcalendar
15343  * 
15344  */
15345
15346 Roo.bootstrap = Roo.bootstrap || {};
15347 /**
15348  * @class Roo.bootstrap.Calendar
15349  * @extends Roo.bootstrap.Component
15350  * Bootstrap Calendar class
15351  * @cfg {Boolean} loadMask (true|false) default false
15352  * @cfg {Object} header generate the user specific header of the calendar, default false
15353
15354  * @constructor
15355  * Create a new Container
15356  * @param {Object} config The config object
15357  */
15358
15359
15360
15361 Roo.bootstrap.Calendar = function(config){
15362     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15363      this.addEvents({
15364         /**
15365              * @event select
15366              * Fires when a date is selected
15367              * @param {DatePicker} this
15368              * @param {Date} date The selected date
15369              */
15370         'select': true,
15371         /**
15372              * @event monthchange
15373              * Fires when the displayed month changes 
15374              * @param {DatePicker} this
15375              * @param {Date} date The selected month
15376              */
15377         'monthchange': true,
15378         /**
15379              * @event evententer
15380              * Fires when mouse over an event
15381              * @param {Calendar} this
15382              * @param {event} Event
15383              */
15384         'evententer': true,
15385         /**
15386              * @event eventleave
15387              * Fires when the mouse leaves an
15388              * @param {Calendar} this
15389              * @param {event}
15390              */
15391         'eventleave': true,
15392         /**
15393              * @event eventclick
15394              * Fires when the mouse click an
15395              * @param {Calendar} this
15396              * @param {event}
15397              */
15398         'eventclick': true
15399         
15400     });
15401
15402 };
15403
15404 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15405     
15406      /**
15407      * @cfg {Number} startDay
15408      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15409      */
15410     startDay : 0,
15411     
15412     loadMask : false,
15413     
15414     header : false,
15415       
15416     getAutoCreate : function(){
15417         
15418         
15419         var fc_button = function(name, corner, style, content ) {
15420             return Roo.apply({},{
15421                 tag : 'span',
15422                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15423                          (corner.length ?
15424                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15425                             ''
15426                         ),
15427                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15428                 unselectable: 'on'
15429             });
15430         };
15431         
15432         var header = {};
15433         
15434         if(!this.header){
15435             header = {
15436                 tag : 'table',
15437                 cls : 'fc-header',
15438                 style : 'width:100%',
15439                 cn : [
15440                     {
15441                         tag: 'tr',
15442                         cn : [
15443                             {
15444                                 tag : 'td',
15445                                 cls : 'fc-header-left',
15446                                 cn : [
15447                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15448                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15449                                     { tag: 'span', cls: 'fc-header-space' },
15450                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15451
15452
15453                                 ]
15454                             },
15455
15456                             {
15457                                 tag : 'td',
15458                                 cls : 'fc-header-center',
15459                                 cn : [
15460                                     {
15461                                         tag: 'span',
15462                                         cls: 'fc-header-title',
15463                                         cn : {
15464                                             tag: 'H2',
15465                                             html : 'month / year'
15466                                         }
15467                                     }
15468
15469                                 ]
15470                             },
15471                             {
15472                                 tag : 'td',
15473                                 cls : 'fc-header-right',
15474                                 cn : [
15475                               /*      fc_button('month', 'left', '', 'month' ),
15476                                     fc_button('week', '', '', 'week' ),
15477                                     fc_button('day', 'right', '', 'day' )
15478                                 */    
15479
15480                                 ]
15481                             }
15482
15483                         ]
15484                     }
15485                 ]
15486             };
15487         }
15488         
15489         header = this.header;
15490         
15491        
15492         var cal_heads = function() {
15493             var ret = [];
15494             // fixme - handle this.
15495             
15496             for (var i =0; i < Date.dayNames.length; i++) {
15497                 var d = Date.dayNames[i];
15498                 ret.push({
15499                     tag: 'th',
15500                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15501                     html : d.substring(0,3)
15502                 });
15503                 
15504             }
15505             ret[0].cls += ' fc-first';
15506             ret[6].cls += ' fc-last';
15507             return ret;
15508         };
15509         var cal_cell = function(n) {
15510             return  {
15511                 tag: 'td',
15512                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15513                 cn : [
15514                     {
15515                         cn : [
15516                             {
15517                                 cls: 'fc-day-number',
15518                                 html: 'D'
15519                             },
15520                             {
15521                                 cls: 'fc-day-content',
15522                              
15523                                 cn : [
15524                                      {
15525                                         style: 'position: relative;' // height: 17px;
15526                                     }
15527                                 ]
15528                             }
15529                             
15530                             
15531                         ]
15532                     }
15533                 ]
15534                 
15535             }
15536         };
15537         var cal_rows = function() {
15538             
15539             var ret = [];
15540             for (var r = 0; r < 6; r++) {
15541                 var row= {
15542                     tag : 'tr',
15543                     cls : 'fc-week',
15544                     cn : []
15545                 };
15546                 
15547                 for (var i =0; i < Date.dayNames.length; i++) {
15548                     var d = Date.dayNames[i];
15549                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15550
15551                 }
15552                 row.cn[0].cls+=' fc-first';
15553                 row.cn[0].cn[0].style = 'min-height:90px';
15554                 row.cn[6].cls+=' fc-last';
15555                 ret.push(row);
15556                 
15557             }
15558             ret[0].cls += ' fc-first';
15559             ret[4].cls += ' fc-prev-last';
15560             ret[5].cls += ' fc-last';
15561             return ret;
15562             
15563         };
15564         
15565         var cal_table = {
15566             tag: 'table',
15567             cls: 'fc-border-separate',
15568             style : 'width:100%',
15569             cellspacing  : 0,
15570             cn : [
15571                 { 
15572                     tag: 'thead',
15573                     cn : [
15574                         { 
15575                             tag: 'tr',
15576                             cls : 'fc-first fc-last',
15577                             cn : cal_heads()
15578                         }
15579                     ]
15580                 },
15581                 { 
15582                     tag: 'tbody',
15583                     cn : cal_rows()
15584                 }
15585                   
15586             ]
15587         };
15588          
15589          var cfg = {
15590             cls : 'fc fc-ltr',
15591             cn : [
15592                 header,
15593                 {
15594                     cls : 'fc-content',
15595                     style : "position: relative;",
15596                     cn : [
15597                         {
15598                             cls : 'fc-view fc-view-month fc-grid',
15599                             style : 'position: relative',
15600                             unselectable : 'on',
15601                             cn : [
15602                                 {
15603                                     cls : 'fc-event-container',
15604                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15605                                 },
15606                                 cal_table
15607                             ]
15608                         }
15609                     ]
15610     
15611                 }
15612            ] 
15613             
15614         };
15615         
15616          
15617         
15618         return cfg;
15619     },
15620     
15621     
15622     initEvents : function()
15623     {
15624         if(!this.store){
15625             throw "can not find store for calendar";
15626         }
15627         
15628         var mark = {
15629             tag: "div",
15630             cls:"x-dlg-mask",
15631             style: "text-align:center",
15632             cn: [
15633                 {
15634                     tag: "div",
15635                     style: "background-color:white;width:50%;margin:250 auto",
15636                     cn: [
15637                         {
15638                             tag: "img",
15639                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15640                         },
15641                         {
15642                             tag: "span",
15643                             html: "Loading"
15644                         }
15645                         
15646                     ]
15647                 }
15648             ]
15649         };
15650         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15651         
15652         var size = this.el.select('.fc-content', true).first().getSize();
15653         this.maskEl.setSize(size.width, size.height);
15654         this.maskEl.enableDisplayMode("block");
15655         if(!this.loadMask){
15656             this.maskEl.hide();
15657         }
15658         
15659         this.store = Roo.factory(this.store, Roo.data);
15660         this.store.on('load', this.onLoad, this);
15661         this.store.on('beforeload', this.onBeforeLoad, this);
15662         
15663         this.resize();
15664         
15665         this.cells = this.el.select('.fc-day',true);
15666         //Roo.log(this.cells);
15667         this.textNodes = this.el.query('.fc-day-number');
15668         this.cells.addClassOnOver('fc-state-hover');
15669         
15670         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15671         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15672         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15673         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15674         
15675         this.on('monthchange', this.onMonthChange, this);
15676         
15677         this.update(new Date().clearTime());
15678     },
15679     
15680     resize : function() {
15681         var sz  = this.el.getSize();
15682         
15683         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15684         this.el.select('.fc-day-content div',true).setHeight(34);
15685     },
15686     
15687     
15688     // private
15689     showPrevMonth : function(e){
15690         this.update(this.activeDate.add("mo", -1));
15691     },
15692     showToday : function(e){
15693         this.update(new Date().clearTime());
15694     },
15695     // private
15696     showNextMonth : function(e){
15697         this.update(this.activeDate.add("mo", 1));
15698     },
15699
15700     // private
15701     showPrevYear : function(){
15702         this.update(this.activeDate.add("y", -1));
15703     },
15704
15705     // private
15706     showNextYear : function(){
15707         this.update(this.activeDate.add("y", 1));
15708     },
15709
15710     
15711    // private
15712     update : function(date)
15713     {
15714         var vd = this.activeDate;
15715         this.activeDate = date;
15716 //        if(vd && this.el){
15717 //            var t = date.getTime();
15718 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15719 //                Roo.log('using add remove');
15720 //                
15721 //                this.fireEvent('monthchange', this, date);
15722 //                
15723 //                this.cells.removeClass("fc-state-highlight");
15724 //                this.cells.each(function(c){
15725 //                   if(c.dateValue == t){
15726 //                       c.addClass("fc-state-highlight");
15727 //                       setTimeout(function(){
15728 //                            try{c.dom.firstChild.focus();}catch(e){}
15729 //                       }, 50);
15730 //                       return false;
15731 //                   }
15732 //                   return true;
15733 //                });
15734 //                return;
15735 //            }
15736 //        }
15737         
15738         var days = date.getDaysInMonth();
15739         
15740         var firstOfMonth = date.getFirstDateOfMonth();
15741         var startingPos = firstOfMonth.getDay()-this.startDay;
15742         
15743         if(startingPos < this.startDay){
15744             startingPos += 7;
15745         }
15746         
15747         var pm = date.add(Date.MONTH, -1);
15748         var prevStart = pm.getDaysInMonth()-startingPos;
15749 //        
15750         this.cells = this.el.select('.fc-day',true);
15751         this.textNodes = this.el.query('.fc-day-number');
15752         this.cells.addClassOnOver('fc-state-hover');
15753         
15754         var cells = this.cells.elements;
15755         var textEls = this.textNodes;
15756         
15757         Roo.each(cells, function(cell){
15758             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15759         });
15760         
15761         days += startingPos;
15762
15763         // convert everything to numbers so it's fast
15764         var day = 86400000;
15765         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15766         //Roo.log(d);
15767         //Roo.log(pm);
15768         //Roo.log(prevStart);
15769         
15770         var today = new Date().clearTime().getTime();
15771         var sel = date.clearTime().getTime();
15772         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15773         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15774         var ddMatch = this.disabledDatesRE;
15775         var ddText = this.disabledDatesText;
15776         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15777         var ddaysText = this.disabledDaysText;
15778         var format = this.format;
15779         
15780         var setCellClass = function(cal, cell){
15781             cell.row = 0;
15782             cell.events = [];
15783             cell.more = [];
15784             //Roo.log('set Cell Class');
15785             cell.title = "";
15786             var t = d.getTime();
15787             
15788             //Roo.log(d);
15789             
15790             cell.dateValue = t;
15791             if(t == today){
15792                 cell.className += " fc-today";
15793                 cell.className += " fc-state-highlight";
15794                 cell.title = cal.todayText;
15795             }
15796             if(t == sel){
15797                 // disable highlight in other month..
15798                 //cell.className += " fc-state-highlight";
15799                 
15800             }
15801             // disabling
15802             if(t < min) {
15803                 cell.className = " fc-state-disabled";
15804                 cell.title = cal.minText;
15805                 return;
15806             }
15807             if(t > max) {
15808                 cell.className = " fc-state-disabled";
15809                 cell.title = cal.maxText;
15810                 return;
15811             }
15812             if(ddays){
15813                 if(ddays.indexOf(d.getDay()) != -1){
15814                     cell.title = ddaysText;
15815                     cell.className = " fc-state-disabled";
15816                 }
15817             }
15818             if(ddMatch && format){
15819                 var fvalue = d.dateFormat(format);
15820                 if(ddMatch.test(fvalue)){
15821                     cell.title = ddText.replace("%0", fvalue);
15822                     cell.className = " fc-state-disabled";
15823                 }
15824             }
15825             
15826             if (!cell.initialClassName) {
15827                 cell.initialClassName = cell.dom.className;
15828             }
15829             
15830             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15831         };
15832
15833         var i = 0;
15834         
15835         for(; i < startingPos; i++) {
15836             textEls[i].innerHTML = (++prevStart);
15837             d.setDate(d.getDate()+1);
15838             
15839             cells[i].className = "fc-past fc-other-month";
15840             setCellClass(this, cells[i]);
15841         }
15842         
15843         var intDay = 0;
15844         
15845         for(; i < days; i++){
15846             intDay = i - startingPos + 1;
15847             textEls[i].innerHTML = (intDay);
15848             d.setDate(d.getDate()+1);
15849             
15850             cells[i].className = ''; // "x-date-active";
15851             setCellClass(this, cells[i]);
15852         }
15853         var extraDays = 0;
15854         
15855         for(; i < 42; i++) {
15856             textEls[i].innerHTML = (++extraDays);
15857             d.setDate(d.getDate()+1);
15858             
15859             cells[i].className = "fc-future fc-other-month";
15860             setCellClass(this, cells[i]);
15861         }
15862         
15863         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15864         
15865         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15866         
15867         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15868         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15869         
15870         if(totalRows != 6){
15871             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15872             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15873         }
15874         
15875         this.fireEvent('monthchange', this, date);
15876         
15877         
15878         /*
15879         if(!this.internalRender){
15880             var main = this.el.dom.firstChild;
15881             var w = main.offsetWidth;
15882             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15883             Roo.fly(main).setWidth(w);
15884             this.internalRender = true;
15885             // opera does not respect the auto grow header center column
15886             // then, after it gets a width opera refuses to recalculate
15887             // without a second pass
15888             if(Roo.isOpera && !this.secondPass){
15889                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15890                 this.secondPass = true;
15891                 this.update.defer(10, this, [date]);
15892             }
15893         }
15894         */
15895         
15896     },
15897     
15898     findCell : function(dt) {
15899         dt = dt.clearTime().getTime();
15900         var ret = false;
15901         this.cells.each(function(c){
15902             //Roo.log("check " +c.dateValue + '?=' + dt);
15903             if(c.dateValue == dt){
15904                 ret = c;
15905                 return false;
15906             }
15907             return true;
15908         });
15909         
15910         return ret;
15911     },
15912     
15913     findCells : function(ev) {
15914         var s = ev.start.clone().clearTime().getTime();
15915        // Roo.log(s);
15916         var e= ev.end.clone().clearTime().getTime();
15917        // Roo.log(e);
15918         var ret = [];
15919         this.cells.each(function(c){
15920              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15921             
15922             if(c.dateValue > e){
15923                 return ;
15924             }
15925             if(c.dateValue < s){
15926                 return ;
15927             }
15928             ret.push(c);
15929         });
15930         
15931         return ret;    
15932     },
15933     
15934 //    findBestRow: function(cells)
15935 //    {
15936 //        var ret = 0;
15937 //        
15938 //        for (var i =0 ; i < cells.length;i++) {
15939 //            ret  = Math.max(cells[i].rows || 0,ret);
15940 //        }
15941 //        return ret;
15942 //        
15943 //    },
15944     
15945     
15946     addItem : function(ev)
15947     {
15948         // look for vertical location slot in
15949         var cells = this.findCells(ev);
15950         
15951 //        ev.row = this.findBestRow(cells);
15952         
15953         // work out the location.
15954         
15955         var crow = false;
15956         var rows = [];
15957         for(var i =0; i < cells.length; i++) {
15958             
15959             cells[i].row = cells[0].row;
15960             
15961             if(i == 0){
15962                 cells[i].row = cells[i].row + 1;
15963             }
15964             
15965             if (!crow) {
15966                 crow = {
15967                     start : cells[i],
15968                     end :  cells[i]
15969                 };
15970                 continue;
15971             }
15972             if (crow.start.getY() == cells[i].getY()) {
15973                 // on same row.
15974                 crow.end = cells[i];
15975                 continue;
15976             }
15977             // different row.
15978             rows.push(crow);
15979             crow = {
15980                 start: cells[i],
15981                 end : cells[i]
15982             };
15983             
15984         }
15985         
15986         rows.push(crow);
15987         ev.els = [];
15988         ev.rows = rows;
15989         ev.cells = cells;
15990         
15991         cells[0].events.push(ev);
15992         
15993         this.calevents.push(ev);
15994     },
15995     
15996     clearEvents: function() {
15997         
15998         if(!this.calevents){
15999             return;
16000         }
16001         
16002         Roo.each(this.cells.elements, function(c){
16003             c.row = 0;
16004             c.events = [];
16005             c.more = [];
16006         });
16007         
16008         Roo.each(this.calevents, function(e) {
16009             Roo.each(e.els, function(el) {
16010                 el.un('mouseenter' ,this.onEventEnter, this);
16011                 el.un('mouseleave' ,this.onEventLeave, this);
16012                 el.remove();
16013             },this);
16014         },this);
16015         
16016         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16017             e.remove();
16018         });
16019         
16020     },
16021     
16022     renderEvents: function()
16023     {   
16024         var _this = this;
16025         
16026         this.cells.each(function(c) {
16027             
16028             if(c.row < 5){
16029                 return;
16030             }
16031             
16032             var ev = c.events;
16033             
16034             var r = 4;
16035             if(c.row != c.events.length){
16036                 r = 4 - (4 - (c.row - c.events.length));
16037             }
16038             
16039             c.events = ev.slice(0, r);
16040             c.more = ev.slice(r);
16041             
16042             if(c.more.length && c.more.length == 1){
16043                 c.events.push(c.more.pop());
16044             }
16045             
16046             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16047             
16048         });
16049             
16050         this.cells.each(function(c) {
16051             
16052             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16053             
16054             
16055             for (var e = 0; e < c.events.length; e++){
16056                 var ev = c.events[e];
16057                 var rows = ev.rows;
16058                 
16059                 for(var i = 0; i < rows.length; i++) {
16060                 
16061                     // how many rows should it span..
16062
16063                     var  cfg = {
16064                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16065                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16066
16067                         unselectable : "on",
16068                         cn : [
16069                             {
16070                                 cls: 'fc-event-inner',
16071                                 cn : [
16072     //                                {
16073     //                                  tag:'span',
16074     //                                  cls: 'fc-event-time',
16075     //                                  html : cells.length > 1 ? '' : ev.time
16076     //                                },
16077                                     {
16078                                       tag:'span',
16079                                       cls: 'fc-event-title',
16080                                       html : String.format('{0}', ev.title)
16081                                     }
16082
16083
16084                                 ]
16085                             },
16086                             {
16087                                 cls: 'ui-resizable-handle ui-resizable-e',
16088                                 html : '&nbsp;&nbsp;&nbsp'
16089                             }
16090
16091                         ]
16092                     };
16093
16094                     if (i == 0) {
16095                         cfg.cls += ' fc-event-start';
16096                     }
16097                     if ((i+1) == rows.length) {
16098                         cfg.cls += ' fc-event-end';
16099                     }
16100
16101                     var ctr = _this.el.select('.fc-event-container',true).first();
16102                     var cg = ctr.createChild(cfg);
16103
16104                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16105                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16106
16107                     var r = (c.more.length) ? 1 : 0;
16108                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16109                     cg.setWidth(ebox.right - sbox.x -2);
16110
16111                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16112                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16113                     cg.on('click', _this.onEventClick, _this, ev);
16114
16115                     ev.els.push(cg);
16116                     
16117                 }
16118                 
16119             }
16120             
16121             
16122             if(c.more.length){
16123                 var  cfg = {
16124                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16125                     style : 'position: absolute',
16126                     unselectable : "on",
16127                     cn : [
16128                         {
16129                             cls: 'fc-event-inner',
16130                             cn : [
16131                                 {
16132                                   tag:'span',
16133                                   cls: 'fc-event-title',
16134                                   html : 'More'
16135                                 }
16136
16137
16138                             ]
16139                         },
16140                         {
16141                             cls: 'ui-resizable-handle ui-resizable-e',
16142                             html : '&nbsp;&nbsp;&nbsp'
16143                         }
16144
16145                     ]
16146                 };
16147
16148                 var ctr = _this.el.select('.fc-event-container',true).first();
16149                 var cg = ctr.createChild(cfg);
16150
16151                 var sbox = c.select('.fc-day-content',true).first().getBox();
16152                 var ebox = c.select('.fc-day-content',true).first().getBox();
16153                 //Roo.log(cg);
16154                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16155                 cg.setWidth(ebox.right - sbox.x -2);
16156
16157                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16158                 
16159             }
16160             
16161         });
16162         
16163         
16164         
16165     },
16166     
16167     onEventEnter: function (e, el,event,d) {
16168         this.fireEvent('evententer', this, el, event);
16169     },
16170     
16171     onEventLeave: function (e, el,event,d) {
16172         this.fireEvent('eventleave', this, el, event);
16173     },
16174     
16175     onEventClick: function (e, el,event,d) {
16176         this.fireEvent('eventclick', this, el, event);
16177     },
16178     
16179     onMonthChange: function () {
16180         this.store.load();
16181     },
16182     
16183     onMoreEventClick: function(e, el, more)
16184     {
16185         var _this = this;
16186         
16187         this.calpopover.placement = 'right';
16188         this.calpopover.setTitle('More');
16189         
16190         this.calpopover.setContent('');
16191         
16192         var ctr = this.calpopover.el.select('.popover-content', true).first();
16193         
16194         Roo.each(more, function(m){
16195             var cfg = {
16196                 cls : 'fc-event-hori fc-event-draggable',
16197                 html : m.title
16198             };
16199             var cg = ctr.createChild(cfg);
16200             
16201             cg.on('click', _this.onEventClick, _this, m);
16202         });
16203         
16204         this.calpopover.show(el);
16205         
16206         
16207     },
16208     
16209     onLoad: function () 
16210     {   
16211         this.calevents = [];
16212         var cal = this;
16213         
16214         if(this.store.getCount() > 0){
16215             this.store.data.each(function(d){
16216                cal.addItem({
16217                     id : d.data.id,
16218                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16219                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16220                     time : d.data.start_time,
16221                     title : d.data.title,
16222                     description : d.data.description,
16223                     venue : d.data.venue
16224                 });
16225             });
16226         }
16227         
16228         this.renderEvents();
16229         
16230         if(this.calevents.length && this.loadMask){
16231             this.maskEl.hide();
16232         }
16233     },
16234     
16235     onBeforeLoad: function()
16236     {
16237         this.clearEvents();
16238         if(this.loadMask){
16239             this.maskEl.show();
16240         }
16241     }
16242 });
16243
16244  
16245  /*
16246  * - LGPL
16247  *
16248  * element
16249  * 
16250  */
16251
16252 /**
16253  * @class Roo.bootstrap.Popover
16254  * @extends Roo.bootstrap.Component
16255  * Bootstrap Popover class
16256  * @cfg {String} html contents of the popover   (or false to use children..)
16257  * @cfg {String} title of popover (or false to hide)
16258  * @cfg {String} placement how it is placed
16259  * @cfg {String} trigger click || hover (or false to trigger manually)
16260  * @cfg {String} over what (parent or false to trigger manually.)
16261  * @cfg {Number} delay - delay before showing
16262  
16263  * @constructor
16264  * Create a new Popover
16265  * @param {Object} config The config object
16266  */
16267
16268 Roo.bootstrap.Popover = function(config){
16269     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16270     
16271     this.addEvents({
16272         // raw events
16273          /**
16274          * @event show
16275          * After the popover show
16276          * 
16277          * @param {Roo.bootstrap.Popover} this
16278          */
16279         "show" : true,
16280         /**
16281          * @event hide
16282          * After the popover hide
16283          * 
16284          * @param {Roo.bootstrap.Popover} this
16285          */
16286         "hide" : true
16287     });
16288 };
16289
16290 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16291     
16292     title: 'Fill in a title',
16293     html: false,
16294     
16295     placement : 'right',
16296     trigger : 'hover', // hover
16297     
16298     delay : 0,
16299     
16300     over: 'parent',
16301     
16302     can_build_overlaid : false,
16303     
16304     getChildContainer : function()
16305     {
16306         return this.el.select('.popover-content',true).first();
16307     },
16308     
16309     getAutoCreate : function(){
16310          
16311         var cfg = {
16312            cls : 'popover roo-dynamic',
16313            style: 'display:block',
16314            cn : [
16315                 {
16316                     cls : 'arrow'
16317                 },
16318                 {
16319                     cls : 'popover-inner',
16320                     cn : [
16321                         {
16322                             tag: 'h3',
16323                             cls: 'popover-title',
16324                             html : this.title
16325                         },
16326                         {
16327                             cls : 'popover-content',
16328                             html : this.html
16329                         }
16330                     ]
16331                     
16332                 }
16333            ]
16334         };
16335         
16336         return cfg;
16337     },
16338     setTitle: function(str)
16339     {
16340         this.title = str;
16341         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16342     },
16343     setContent: function(str)
16344     {
16345         this.html = str;
16346         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16347     },
16348     // as it get's added to the bottom of the page.
16349     onRender : function(ct, position)
16350     {
16351         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16352         if(!this.el){
16353             var cfg = Roo.apply({},  this.getAutoCreate());
16354             cfg.id = Roo.id();
16355             
16356             if (this.cls) {
16357                 cfg.cls += ' ' + this.cls;
16358             }
16359             if (this.style) {
16360                 cfg.style = this.style;
16361             }
16362             //Roo.log("adding to ");
16363             this.el = Roo.get(document.body).createChild(cfg, position);
16364 //            Roo.log(this.el);
16365         }
16366         this.initEvents();
16367     },
16368     
16369     initEvents : function()
16370     {
16371         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16372         this.el.enableDisplayMode('block');
16373         this.el.hide();
16374         if (this.over === false) {
16375             return; 
16376         }
16377         if (this.triggers === false) {
16378             return;
16379         }
16380         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16381         var triggers = this.trigger ? this.trigger.split(' ') : [];
16382         Roo.each(triggers, function(trigger) {
16383         
16384             if (trigger == 'click') {
16385                 on_el.on('click', this.toggle, this);
16386             } else if (trigger != 'manual') {
16387                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16388                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16389       
16390                 on_el.on(eventIn  ,this.enter, this);
16391                 on_el.on(eventOut, this.leave, this);
16392             }
16393         }, this);
16394         
16395     },
16396     
16397     
16398     // private
16399     timeout : null,
16400     hoverState : null,
16401     
16402     toggle : function () {
16403         this.hoverState == 'in' ? this.leave() : this.enter();
16404     },
16405     
16406     enter : function () {
16407         
16408         clearTimeout(this.timeout);
16409     
16410         this.hoverState = 'in';
16411     
16412         if (!this.delay || !this.delay.show) {
16413             this.show();
16414             return;
16415         }
16416         var _t = this;
16417         this.timeout = setTimeout(function () {
16418             if (_t.hoverState == 'in') {
16419                 _t.show();
16420             }
16421         }, this.delay.show)
16422     },
16423     
16424     leave : function() {
16425         clearTimeout(this.timeout);
16426     
16427         this.hoverState = 'out';
16428     
16429         if (!this.delay || !this.delay.hide) {
16430             this.hide();
16431             return;
16432         }
16433         var _t = this;
16434         this.timeout = setTimeout(function () {
16435             if (_t.hoverState == 'out') {
16436                 _t.hide();
16437             }
16438         }, this.delay.hide)
16439     },
16440     
16441     show : function (on_el)
16442     {
16443         if (!on_el) {
16444             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16445         }
16446         
16447         // set content.
16448         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16449         if (this.html !== false) {
16450             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16451         }
16452         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16453         if (!this.title.length) {
16454             this.el.select('.popover-title',true).hide();
16455         }
16456         
16457         var placement = typeof this.placement == 'function' ?
16458             this.placement.call(this, this.el, on_el) :
16459             this.placement;
16460             
16461         var autoToken = /\s?auto?\s?/i;
16462         var autoPlace = autoToken.test(placement);
16463         if (autoPlace) {
16464             placement = placement.replace(autoToken, '') || 'top';
16465         }
16466         
16467         //this.el.detach()
16468         //this.el.setXY([0,0]);
16469         this.el.show();
16470         this.el.dom.style.display='block';
16471         this.el.addClass(placement);
16472         
16473         //this.el.appendTo(on_el);
16474         
16475         var p = this.getPosition();
16476         var box = this.el.getBox();
16477         
16478         if (autoPlace) {
16479             // fixme..
16480         }
16481         var align = Roo.bootstrap.Popover.alignment[placement];
16482         this.el.alignTo(on_el, align[0],align[1]);
16483         //var arrow = this.el.select('.arrow',true).first();
16484         //arrow.set(align[2], 
16485         
16486         this.el.addClass('in');
16487         
16488         
16489         if (this.el.hasClass('fade')) {
16490             // fade it?
16491         }
16492         
16493         this.hoverState = 'in';
16494         
16495         this.fireEvent('show', this);
16496         
16497     },
16498     hide : function()
16499     {
16500         this.el.setXY([0,0]);
16501         this.el.removeClass('in');
16502         this.el.hide();
16503         this.hoverState = null;
16504         
16505         this.fireEvent('hide', this);
16506     }
16507     
16508 });
16509
16510 Roo.bootstrap.Popover.alignment = {
16511     'left' : ['r-l', [-10,0], 'right'],
16512     'right' : ['l-r', [10,0], 'left'],
16513     'bottom' : ['t-b', [0,10], 'top'],
16514     'top' : [ 'b-t', [0,-10], 'bottom']
16515 };
16516
16517  /*
16518  * - LGPL
16519  *
16520  * Progress
16521  * 
16522  */
16523
16524 /**
16525  * @class Roo.bootstrap.Progress
16526  * @extends Roo.bootstrap.Component
16527  * Bootstrap Progress class
16528  * @cfg {Boolean} striped striped of the progress bar
16529  * @cfg {Boolean} active animated of the progress bar
16530  * 
16531  * 
16532  * @constructor
16533  * Create a new Progress
16534  * @param {Object} config The config object
16535  */
16536
16537 Roo.bootstrap.Progress = function(config){
16538     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16539 };
16540
16541 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16542     
16543     striped : false,
16544     active: false,
16545     
16546     getAutoCreate : function(){
16547         var cfg = {
16548             tag: 'div',
16549             cls: 'progress'
16550         };
16551         
16552         
16553         if(this.striped){
16554             cfg.cls += ' progress-striped';
16555         }
16556       
16557         if(this.active){
16558             cfg.cls += ' active';
16559         }
16560         
16561         
16562         return cfg;
16563     }
16564    
16565 });
16566
16567  
16568
16569  /*
16570  * - LGPL
16571  *
16572  * ProgressBar
16573  * 
16574  */
16575
16576 /**
16577  * @class Roo.bootstrap.ProgressBar
16578  * @extends Roo.bootstrap.Component
16579  * Bootstrap ProgressBar class
16580  * @cfg {Number} aria_valuenow aria-value now
16581  * @cfg {Number} aria_valuemin aria-value min
16582  * @cfg {Number} aria_valuemax aria-value max
16583  * @cfg {String} label label for the progress bar
16584  * @cfg {String} panel (success | info | warning | danger )
16585  * @cfg {String} role role of the progress bar
16586  * @cfg {String} sr_only text
16587  * 
16588  * 
16589  * @constructor
16590  * Create a new ProgressBar
16591  * @param {Object} config The config object
16592  */
16593
16594 Roo.bootstrap.ProgressBar = function(config){
16595     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16596 };
16597
16598 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16599     
16600     aria_valuenow : 0,
16601     aria_valuemin : 0,
16602     aria_valuemax : 100,
16603     label : false,
16604     panel : false,
16605     role : false,
16606     sr_only: false,
16607     
16608     getAutoCreate : function()
16609     {
16610         
16611         var cfg = {
16612             tag: 'div',
16613             cls: 'progress-bar',
16614             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16615         };
16616         
16617         if(this.sr_only){
16618             cfg.cn = {
16619                 tag: 'span',
16620                 cls: 'sr-only',
16621                 html: this.sr_only
16622             }
16623         }
16624         
16625         if(this.role){
16626             cfg.role = this.role;
16627         }
16628         
16629         if(this.aria_valuenow){
16630             cfg['aria-valuenow'] = this.aria_valuenow;
16631         }
16632         
16633         if(this.aria_valuemin){
16634             cfg['aria-valuemin'] = this.aria_valuemin;
16635         }
16636         
16637         if(this.aria_valuemax){
16638             cfg['aria-valuemax'] = this.aria_valuemax;
16639         }
16640         
16641         if(this.label && !this.sr_only){
16642             cfg.html = this.label;
16643         }
16644         
16645         if(this.panel){
16646             cfg.cls += ' progress-bar-' + this.panel;
16647         }
16648         
16649         return cfg;
16650     },
16651     
16652     update : function(aria_valuenow)
16653     {
16654         this.aria_valuenow = aria_valuenow;
16655         
16656         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16657     }
16658    
16659 });
16660
16661  
16662
16663  /*
16664  * - LGPL
16665  *
16666  * column
16667  * 
16668  */
16669
16670 /**
16671  * @class Roo.bootstrap.TabGroup
16672  * @extends Roo.bootstrap.Column
16673  * Bootstrap Column class
16674  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16675  * @cfg {Boolean} carousel true to make the group behave like a carousel
16676  * @cfg {Boolean} bullets show bullets for the panels
16677  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16678  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16679  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16680  * @cfg {Boolean} showarrow (true|false) show arrow default true
16681  * 
16682  * @constructor
16683  * Create a new TabGroup
16684  * @param {Object} config The config object
16685  */
16686
16687 Roo.bootstrap.TabGroup = function(config){
16688     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16689     if (!this.navId) {
16690         this.navId = Roo.id();
16691     }
16692     this.tabs = [];
16693     Roo.bootstrap.TabGroup.register(this);
16694     
16695 };
16696
16697 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16698     
16699     carousel : false,
16700     transition : false,
16701     bullets : 0,
16702     timer : 0,
16703     autoslide : false,
16704     slideFn : false,
16705     slideOnTouch : false,
16706     showarrow : true,
16707     
16708     getAutoCreate : function()
16709     {
16710         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16711         
16712         cfg.cls += ' tab-content';
16713         
16714         if (this.carousel) {
16715             cfg.cls += ' carousel slide';
16716             
16717             cfg.cn = [{
16718                cls : 'carousel-inner',
16719                cn : []
16720             }];
16721         
16722             if(this.bullets  && !Roo.isTouch){
16723                 
16724                 var bullets = {
16725                     cls : 'carousel-bullets',
16726                     cn : []
16727                 };
16728                
16729                 if(this.bullets_cls){
16730                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16731                 }
16732                 
16733                 bullets.cn.push({
16734                     cls : 'clear'
16735                 });
16736                 
16737                 cfg.cn[0].cn.push(bullets);
16738             }
16739             
16740             if(this.showarrow){
16741                 cfg.cn[0].cn.push({
16742                     tag : 'div',
16743                     class : 'carousel-arrow',
16744                     cn : [
16745                         {
16746                             tag : 'div',
16747                             class : 'carousel-prev',
16748                             cn : [
16749                                 {
16750                                     tag : 'i',
16751                                     class : 'fa fa-chevron-left'
16752                                 }
16753                             ]
16754                         },
16755                         {
16756                             tag : 'div',
16757                             class : 'carousel-next',
16758                             cn : [
16759                                 {
16760                                     tag : 'i',
16761                                     class : 'fa fa-chevron-right'
16762                                 }
16763                             ]
16764                         }
16765                     ]
16766                 });
16767             }
16768             
16769         }
16770         
16771         return cfg;
16772     },
16773     
16774     initEvents:  function()
16775     {
16776         if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16777             this.el.on("touchstart", this.onTouchStart, this);
16778         }
16779         
16780         if(this.autoslide){
16781             var _this = this;
16782             
16783             this.slideFn = window.setInterval(function() {
16784                 _this.showPanelNext();
16785             }, this.timer);
16786         }
16787         
16788         if(this.showarrow){
16789             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16790             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16791         }
16792         
16793         
16794     },
16795     
16796     onTouchStart : function(e, el, o)
16797     {
16798         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16799             return;
16800         }
16801         
16802         this.showPanelNext();
16803     },
16804     
16805     getChildContainer : function()
16806     {
16807         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16808     },
16809     
16810     /**
16811     * register a Navigation item
16812     * @param {Roo.bootstrap.NavItem} the navitem to add
16813     */
16814     register : function(item)
16815     {
16816         this.tabs.push( item);
16817         item.navId = this.navId; // not really needed..
16818         this.addBullet();
16819     
16820     },
16821     
16822     getActivePanel : function()
16823     {
16824         var r = false;
16825         Roo.each(this.tabs, function(t) {
16826             if (t.active) {
16827                 r = t;
16828                 return false;
16829             }
16830             return null;
16831         });
16832         return r;
16833         
16834     },
16835     getPanelByName : function(n)
16836     {
16837         var r = false;
16838         Roo.each(this.tabs, function(t) {
16839             if (t.tabId == n) {
16840                 r = t;
16841                 return false;
16842             }
16843             return null;
16844         });
16845         return r;
16846     },
16847     indexOfPanel : function(p)
16848     {
16849         var r = false;
16850         Roo.each(this.tabs, function(t,i) {
16851             if (t.tabId == p.tabId) {
16852                 r = i;
16853                 return false;
16854             }
16855             return null;
16856         });
16857         return r;
16858     },
16859     /**
16860      * show a specific panel
16861      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16862      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16863      */
16864     showPanel : function (pan)
16865     {
16866         if(this.transition || typeof(pan) == 'undefined'){
16867             Roo.log("waiting for the transitionend");
16868             return;
16869         }
16870         
16871         if (typeof(pan) == 'number') {
16872             pan = this.tabs[pan];
16873         }
16874         
16875         if (typeof(pan) == 'string') {
16876             pan = this.getPanelByName(pan);
16877         }
16878         
16879         var cur = this.getActivePanel();
16880         
16881         if(!pan || !cur){
16882             Roo.log('pan or acitve pan is undefined');
16883             return false;
16884         }
16885         
16886         if (pan.tabId == this.getActivePanel().tabId) {
16887             return true;
16888         }
16889         
16890         if (false === cur.fireEvent('beforedeactivate')) {
16891             return false;
16892         }
16893         
16894         if(this.bullets > 0 && !Roo.isTouch){
16895             this.setActiveBullet(this.indexOfPanel(pan));
16896         }
16897         
16898         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16899             
16900             this.transition = true;
16901             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16902             var lr = dir == 'next' ? 'left' : 'right';
16903             pan.el.addClass(dir); // or prev
16904             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16905             cur.el.addClass(lr); // or right
16906             pan.el.addClass(lr);
16907             
16908             var _this = this;
16909             cur.el.on('transitionend', function() {
16910                 Roo.log("trans end?");
16911                 
16912                 pan.el.removeClass([lr,dir]);
16913                 pan.setActive(true);
16914                 
16915                 cur.el.removeClass([lr]);
16916                 cur.setActive(false);
16917                 
16918                 _this.transition = false;
16919                 
16920             }, this, { single:  true } );
16921             
16922             return true;
16923         }
16924         
16925         cur.setActive(false);
16926         pan.setActive(true);
16927         
16928         return true;
16929         
16930     },
16931     showPanelNext : function()
16932     {
16933         var i = this.indexOfPanel(this.getActivePanel());
16934         
16935         if (i >= this.tabs.length - 1 && !this.autoslide) {
16936             return;
16937         }
16938         
16939         if (i >= this.tabs.length - 1 && this.autoslide) {
16940             i = -1;
16941         }
16942         
16943         this.showPanel(this.tabs[i+1]);
16944     },
16945     
16946     showPanelPrev : function()
16947     {
16948         var i = this.indexOfPanel(this.getActivePanel());
16949         
16950         if (i  < 1 && !this.autoslide) {
16951             return;
16952         }
16953         
16954         if (i < 1 && this.autoslide) {
16955             i = this.tabs.length;
16956         }
16957         
16958         this.showPanel(this.tabs[i-1]);
16959     },
16960     
16961     
16962     addBullet: function()
16963     {
16964         if(!this.bullets || Roo.isTouch){
16965             return;
16966         }
16967         var ctr = this.el.select('.carousel-bullets',true).first();
16968         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16969         var bullet = ctr.createChild({
16970             cls : 'bullet bullet-' + i
16971         },ctr.dom.lastChild);
16972         
16973         
16974         var _this = this;
16975         
16976         bullet.on('click', (function(e, el, o, ii, t){
16977
16978             e.preventDefault();
16979
16980             this.showPanel(ii);
16981
16982             if(this.autoslide && this.slideFn){
16983                 clearInterval(this.slideFn);
16984                 this.slideFn = window.setInterval(function() {
16985                     _this.showPanelNext();
16986                 }, this.timer);
16987             }
16988
16989         }).createDelegate(this, [i, bullet], true));
16990                 
16991         
16992     },
16993      
16994     setActiveBullet : function(i)
16995     {
16996         if(Roo.isTouch){
16997             return;
16998         }
16999         
17000         Roo.each(this.el.select('.bullet', true).elements, function(el){
17001             el.removeClass('selected');
17002         });
17003
17004         var bullet = this.el.select('.bullet-' + i, true).first();
17005         
17006         if(!bullet){
17007             return;
17008         }
17009         
17010         bullet.addClass('selected');
17011     }
17012     
17013     
17014   
17015 });
17016
17017  
17018
17019  
17020  
17021 Roo.apply(Roo.bootstrap.TabGroup, {
17022     
17023     groups: {},
17024      /**
17025     * register a Navigation Group
17026     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17027     */
17028     register : function(navgrp)
17029     {
17030         this.groups[navgrp.navId] = navgrp;
17031         
17032     },
17033     /**
17034     * fetch a Navigation Group based on the navigation ID
17035     * if one does not exist , it will get created.
17036     * @param {string} the navgroup to add
17037     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17038     */
17039     get: function(navId) {
17040         if (typeof(this.groups[navId]) == 'undefined') {
17041             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17042         }
17043         return this.groups[navId] ;
17044     }
17045     
17046     
17047     
17048 });
17049
17050  /*
17051  * - LGPL
17052  *
17053  * TabPanel
17054  * 
17055  */
17056
17057 /**
17058  * @class Roo.bootstrap.TabPanel
17059  * @extends Roo.bootstrap.Component
17060  * Bootstrap TabPanel class
17061  * @cfg {Boolean} active panel active
17062  * @cfg {String} html panel content
17063  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17064  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17065  * @cfg {String} href click to link..
17066  * 
17067  * 
17068  * @constructor
17069  * Create a new TabPanel
17070  * @param {Object} config The config object
17071  */
17072
17073 Roo.bootstrap.TabPanel = function(config){
17074     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17075     this.addEvents({
17076         /**
17077              * @event changed
17078              * Fires when the active status changes
17079              * @param {Roo.bootstrap.TabPanel} this
17080              * @param {Boolean} state the new state
17081             
17082          */
17083         'changed': true,
17084         /**
17085              * @event beforedeactivate
17086              * Fires before a tab is de-activated - can be used to do validation on a form.
17087              * @param {Roo.bootstrap.TabPanel} this
17088              * @return {Boolean} false if there is an error
17089             
17090          */
17091         'beforedeactivate': true
17092      });
17093     
17094     this.tabId = this.tabId || Roo.id();
17095   
17096 };
17097
17098 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17099     
17100     active: false,
17101     html: false,
17102     tabId: false,
17103     navId : false,
17104     href : '',
17105     
17106     getAutoCreate : function(){
17107         var cfg = {
17108             tag: 'div',
17109             // item is needed for carousel - not sure if it has any effect otherwise
17110             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17111             html: this.html || ''
17112         };
17113         
17114         if(this.active){
17115             cfg.cls += ' active';
17116         }
17117         
17118         if(this.tabId){
17119             cfg.tabId = this.tabId;
17120         }
17121         
17122         
17123         return cfg;
17124     },
17125     
17126     initEvents:  function()
17127     {
17128         var p = this.parent();
17129         this.navId = this.navId || p.navId;
17130         
17131         if (typeof(this.navId) != 'undefined') {
17132             // not really needed.. but just in case.. parent should be a NavGroup.
17133             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17134             
17135             tg.register(this);
17136             
17137             var i = tg.tabs.length - 1;
17138             
17139             if(this.active && tg.bullets > 0 && i < tg.bullets){
17140                 tg.setActiveBullet(i);
17141             }
17142         }
17143         
17144         if(this.href.length){
17145             this.el.on('click', this.onClick, this);
17146         }
17147         
17148     },
17149     
17150     onRender : function(ct, position)
17151     {
17152        // Roo.log("Call onRender: " + this.xtype);
17153         
17154         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17155         
17156         
17157         
17158         
17159         
17160     },
17161     
17162     setActive: function(state)
17163     {
17164         Roo.log("panel - set active " + this.tabId + "=" + state);
17165         
17166         this.active = state;
17167         if (!state) {
17168             this.el.removeClass('active');
17169             
17170         } else  if (!this.el.hasClass('active')) {
17171             this.el.addClass('active');
17172         }
17173         
17174         this.fireEvent('changed', this, state);
17175     },
17176     
17177     onClick: function(e)
17178     {
17179         e.preventDefault();
17180         
17181         window.location.href = this.href;
17182     }
17183     
17184     
17185 });
17186  
17187
17188  
17189
17190  /*
17191  * - LGPL
17192  *
17193  * DateField
17194  * 
17195  */
17196
17197 /**
17198  * @class Roo.bootstrap.DateField
17199  * @extends Roo.bootstrap.Input
17200  * Bootstrap DateField class
17201  * @cfg {Number} weekStart default 0
17202  * @cfg {String} viewMode default empty, (months|years)
17203  * @cfg {String} minViewMode default empty, (months|years)
17204  * @cfg {Number} startDate default -Infinity
17205  * @cfg {Number} endDate default Infinity
17206  * @cfg {Boolean} todayHighlight default false
17207  * @cfg {Boolean} todayBtn default false
17208  * @cfg {Boolean} calendarWeeks default false
17209  * @cfg {Object} daysOfWeekDisabled default empty
17210  * @cfg {Boolean} singleMode default false (true | false)
17211  * 
17212  * @cfg {Boolean} keyboardNavigation default true
17213  * @cfg {String} language default en
17214  * 
17215  * @constructor
17216  * Create a new DateField
17217  * @param {Object} config The config object
17218  */
17219
17220 Roo.bootstrap.DateField = function(config){
17221     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17222      this.addEvents({
17223             /**
17224              * @event show
17225              * Fires when this field show.
17226              * @param {Roo.bootstrap.DateField} this
17227              * @param {Mixed} date The date value
17228              */
17229             show : true,
17230             /**
17231              * @event show
17232              * Fires when this field hide.
17233              * @param {Roo.bootstrap.DateField} this
17234              * @param {Mixed} date The date value
17235              */
17236             hide : true,
17237             /**
17238              * @event select
17239              * Fires when select a date.
17240              * @param {Roo.bootstrap.DateField} this
17241              * @param {Mixed} date The date value
17242              */
17243             select : true,
17244             /**
17245              * @event beforeselect
17246              * Fires when before select a date.
17247              * @param {Roo.bootstrap.DateField} this
17248              * @param {Mixed} date The date value
17249              */
17250             beforeselect : true
17251         });
17252 };
17253
17254 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17255     
17256     /**
17257      * @cfg {String} format
17258      * The default date format string which can be overriden for localization support.  The format must be
17259      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17260      */
17261     format : "m/d/y",
17262     /**
17263      * @cfg {String} altFormats
17264      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17265      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17266      */
17267     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17268     
17269     weekStart : 0,
17270     
17271     viewMode : '',
17272     
17273     minViewMode : '',
17274     
17275     todayHighlight : false,
17276     
17277     todayBtn: false,
17278     
17279     language: 'en',
17280     
17281     keyboardNavigation: true,
17282     
17283     calendarWeeks: false,
17284     
17285     startDate: -Infinity,
17286     
17287     endDate: Infinity,
17288     
17289     daysOfWeekDisabled: [],
17290     
17291     _events: [],
17292     
17293     singleMode : false,
17294     
17295     UTCDate: function()
17296     {
17297         return new Date(Date.UTC.apply(Date, arguments));
17298     },
17299     
17300     UTCToday: function()
17301     {
17302         var today = new Date();
17303         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17304     },
17305     
17306     getDate: function() {
17307             var d = this.getUTCDate();
17308             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17309     },
17310     
17311     getUTCDate: function() {
17312             return this.date;
17313     },
17314     
17315     setDate: function(d) {
17316             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17317     },
17318     
17319     setUTCDate: function(d) {
17320             this.date = d;
17321             this.setValue(this.formatDate(this.date));
17322     },
17323         
17324     onRender: function(ct, position)
17325     {
17326         
17327         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17328         
17329         this.language = this.language || 'en';
17330         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17331         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17332         
17333         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17334         this.format = this.format || 'm/d/y';
17335         this.isInline = false;
17336         this.isInput = true;
17337         this.component = this.el.select('.add-on', true).first() || false;
17338         this.component = (this.component && this.component.length === 0) ? false : this.component;
17339         this.hasInput = this.component && this.inputEl().length;
17340         
17341         if (typeof(this.minViewMode === 'string')) {
17342             switch (this.minViewMode) {
17343                 case 'months':
17344                     this.minViewMode = 1;
17345                     break;
17346                 case 'years':
17347                     this.minViewMode = 2;
17348                     break;
17349                 default:
17350                     this.minViewMode = 0;
17351                     break;
17352             }
17353         }
17354         
17355         if (typeof(this.viewMode === 'string')) {
17356             switch (this.viewMode) {
17357                 case 'months':
17358                     this.viewMode = 1;
17359                     break;
17360                 case 'years':
17361                     this.viewMode = 2;
17362                     break;
17363                 default:
17364                     this.viewMode = 0;
17365                     break;
17366             }
17367         }
17368                 
17369         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17370         
17371 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17372         
17373         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17374         
17375         this.picker().on('mousedown', this.onMousedown, this);
17376         this.picker().on('click', this.onClick, this);
17377         
17378         this.picker().addClass('datepicker-dropdown');
17379         
17380         this.startViewMode = this.viewMode;
17381         
17382         if(this.singleMode){
17383             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17384                 v.setVisibilityMode(Roo.Element.DISPLAY);
17385                 v.hide();
17386             });
17387             
17388             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17389                 v.setStyle('width', '189px');
17390             });
17391         }
17392         
17393         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17394             if(!this.calendarWeeks){
17395                 v.remove();
17396                 return;
17397             }
17398             
17399             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17400             v.attr('colspan', function(i, val){
17401                 return parseInt(val) + 1;
17402             });
17403         });
17404                         
17405         
17406         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17407         
17408         this.setStartDate(this.startDate);
17409         this.setEndDate(this.endDate);
17410         
17411         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17412         
17413         this.fillDow();
17414         this.fillMonths();
17415         this.update();
17416         this.showMode();
17417         
17418         if(this.isInline) {
17419             this.show();
17420         }
17421     },
17422     
17423     picker : function()
17424     {
17425         return this.pickerEl;
17426 //        return this.el.select('.datepicker', true).first();
17427     },
17428     
17429     fillDow: function()
17430     {
17431         var dowCnt = this.weekStart;
17432         
17433         var dow = {
17434             tag: 'tr',
17435             cn: [
17436                 
17437             ]
17438         };
17439         
17440         if(this.calendarWeeks){
17441             dow.cn.push({
17442                 tag: 'th',
17443                 cls: 'cw',
17444                 html: '&nbsp;'
17445             })
17446         }
17447         
17448         while (dowCnt < this.weekStart + 7) {
17449             dow.cn.push({
17450                 tag: 'th',
17451                 cls: 'dow',
17452                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17453             });
17454         }
17455         
17456         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17457     },
17458     
17459     fillMonths: function()
17460     {    
17461         var i = 0;
17462         var months = this.picker().select('>.datepicker-months td', true).first();
17463         
17464         months.dom.innerHTML = '';
17465         
17466         while (i < 12) {
17467             var month = {
17468                 tag: 'span',
17469                 cls: 'month',
17470                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17471             };
17472             
17473             months.createChild(month);
17474         }
17475         
17476     },
17477     
17478     update: function()
17479     {
17480         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;
17481         
17482         if (this.date < this.startDate) {
17483             this.viewDate = new Date(this.startDate);
17484         } else if (this.date > this.endDate) {
17485             this.viewDate = new Date(this.endDate);
17486         } else {
17487             this.viewDate = new Date(this.date);
17488         }
17489         
17490         this.fill();
17491     },
17492     
17493     fill: function() 
17494     {
17495         var d = new Date(this.viewDate),
17496                 year = d.getUTCFullYear(),
17497                 month = d.getUTCMonth(),
17498                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17499                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17500                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17501                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17502                 currentDate = this.date && this.date.valueOf(),
17503                 today = this.UTCToday();
17504         
17505         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17506         
17507 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17508         
17509 //        this.picker.select('>tfoot th.today').
17510 //                                              .text(dates[this.language].today)
17511 //                                              .toggle(this.todayBtn !== false);
17512     
17513         this.updateNavArrows();
17514         this.fillMonths();
17515                                                 
17516         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17517         
17518         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17519          
17520         prevMonth.setUTCDate(day);
17521         
17522         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17523         
17524         var nextMonth = new Date(prevMonth);
17525         
17526         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17527         
17528         nextMonth = nextMonth.valueOf();
17529         
17530         var fillMonths = false;
17531         
17532         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17533         
17534         while(prevMonth.valueOf() < nextMonth) {
17535             var clsName = '';
17536             
17537             if (prevMonth.getUTCDay() === this.weekStart) {
17538                 if(fillMonths){
17539                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17540                 }
17541                     
17542                 fillMonths = {
17543                     tag: 'tr',
17544                     cn: []
17545                 };
17546                 
17547                 if(this.calendarWeeks){
17548                     // ISO 8601: First week contains first thursday.
17549                     // ISO also states week starts on Monday, but we can be more abstract here.
17550                     var
17551                     // Start of current week: based on weekstart/current date
17552                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17553                     // Thursday of this week
17554                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17555                     // First Thursday of year, year from thursday
17556                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17557                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17558                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17559                     
17560                     fillMonths.cn.push({
17561                         tag: 'td',
17562                         cls: 'cw',
17563                         html: calWeek
17564                     });
17565                 }
17566             }
17567             
17568             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17569                 clsName += ' old';
17570             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17571                 clsName += ' new';
17572             }
17573             if (this.todayHighlight &&
17574                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17575                 prevMonth.getUTCMonth() == today.getMonth() &&
17576                 prevMonth.getUTCDate() == today.getDate()) {
17577                 clsName += ' today';
17578             }
17579             
17580             if (currentDate && prevMonth.valueOf() === currentDate) {
17581                 clsName += ' active';
17582             }
17583             
17584             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17585                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17586                     clsName += ' disabled';
17587             }
17588             
17589             fillMonths.cn.push({
17590                 tag: 'td',
17591                 cls: 'day ' + clsName,
17592                 html: prevMonth.getDate()
17593             });
17594             
17595             prevMonth.setDate(prevMonth.getDate()+1);
17596         }
17597           
17598         var currentYear = this.date && this.date.getUTCFullYear();
17599         var currentMonth = this.date && this.date.getUTCMonth();
17600         
17601         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17602         
17603         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17604             v.removeClass('active');
17605             
17606             if(currentYear === year && k === currentMonth){
17607                 v.addClass('active');
17608             }
17609             
17610             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17611                 v.addClass('disabled');
17612             }
17613             
17614         });
17615         
17616         
17617         year = parseInt(year/10, 10) * 10;
17618         
17619         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17620         
17621         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17622         
17623         year -= 1;
17624         for (var i = -1; i < 11; i++) {
17625             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17626                 tag: 'span',
17627                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17628                 html: year
17629             });
17630             
17631             year += 1;
17632         }
17633     },
17634     
17635     showMode: function(dir) 
17636     {
17637         if (dir) {
17638             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17639         }
17640         
17641         Roo.each(this.picker().select('>div',true).elements, function(v){
17642             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17643             v.hide();
17644         });
17645         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17646     },
17647     
17648     place: function()
17649     {
17650         if(this.isInline) {
17651             return;
17652         }
17653         
17654         this.picker().removeClass(['bottom', 'top']);
17655         
17656         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17657             /*
17658              * place to the top of element!
17659              *
17660              */
17661             
17662             this.picker().addClass('top');
17663             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17664             
17665             return;
17666         }
17667         
17668         this.picker().addClass('bottom');
17669         
17670         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17671     },
17672     
17673     parseDate : function(value)
17674     {
17675         if(!value || value instanceof Date){
17676             return value;
17677         }
17678         var v = Date.parseDate(value, this.format);
17679         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17680             v = Date.parseDate(value, 'Y-m-d');
17681         }
17682         if(!v && this.altFormats){
17683             if(!this.altFormatsArray){
17684                 this.altFormatsArray = this.altFormats.split("|");
17685             }
17686             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17687                 v = Date.parseDate(value, this.altFormatsArray[i]);
17688             }
17689         }
17690         return v;
17691     },
17692     
17693     formatDate : function(date, fmt)
17694     {   
17695         return (!date || !(date instanceof Date)) ?
17696         date : date.dateFormat(fmt || this.format);
17697     },
17698     
17699     onFocus : function()
17700     {
17701         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17702         this.show();
17703     },
17704     
17705     onBlur : function()
17706     {
17707         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17708         
17709         var d = this.inputEl().getValue();
17710         
17711         this.setValue(d);
17712                 
17713         this.hide();
17714     },
17715     
17716     show : function()
17717     {
17718         this.picker().show();
17719         this.update();
17720         this.place();
17721         
17722         this.fireEvent('show', this, this.date);
17723     },
17724     
17725     hide : function()
17726     {
17727         if(this.isInline) {
17728             return;
17729         }
17730         this.picker().hide();
17731         this.viewMode = this.startViewMode;
17732         this.showMode();
17733         
17734         this.fireEvent('hide', this, this.date);
17735         
17736     },
17737     
17738     onMousedown: function(e)
17739     {
17740         e.stopPropagation();
17741         e.preventDefault();
17742     },
17743     
17744     keyup: function(e)
17745     {
17746         Roo.bootstrap.DateField.superclass.keyup.call(this);
17747         this.update();
17748     },
17749
17750     setValue: function(v)
17751     {
17752         if(this.fireEvent('beforeselect', this, v) !== false){
17753             var d = new Date(this.parseDate(v) ).clearTime();
17754         
17755             if(isNaN(d.getTime())){
17756                 this.date = this.viewDate = '';
17757                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17758                 return;
17759             }
17760
17761             v = this.formatDate(d);
17762
17763             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17764
17765             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17766
17767             this.update();
17768
17769             this.fireEvent('select', this, this.date);
17770         }
17771     },
17772     
17773     getValue: function()
17774     {
17775         return this.formatDate(this.date);
17776     },
17777     
17778     fireKey: function(e)
17779     {
17780         if (!this.picker().isVisible()){
17781             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17782                 this.show();
17783             }
17784             return;
17785         }
17786         
17787         var dateChanged = false,
17788         dir, day, month,
17789         newDate, newViewDate;
17790         
17791         switch(e.keyCode){
17792             case 27: // escape
17793                 this.hide();
17794                 e.preventDefault();
17795                 break;
17796             case 37: // left
17797             case 39: // right
17798                 if (!this.keyboardNavigation) {
17799                     break;
17800                 }
17801                 dir = e.keyCode == 37 ? -1 : 1;
17802                 
17803                 if (e.ctrlKey){
17804                     newDate = this.moveYear(this.date, dir);
17805                     newViewDate = this.moveYear(this.viewDate, dir);
17806                 } else if (e.shiftKey){
17807                     newDate = this.moveMonth(this.date, dir);
17808                     newViewDate = this.moveMonth(this.viewDate, dir);
17809                 } else {
17810                     newDate = new Date(this.date);
17811                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17812                     newViewDate = new Date(this.viewDate);
17813                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17814                 }
17815                 if (this.dateWithinRange(newDate)){
17816                     this.date = newDate;
17817                     this.viewDate = newViewDate;
17818                     this.setValue(this.formatDate(this.date));
17819 //                    this.update();
17820                     e.preventDefault();
17821                     dateChanged = true;
17822                 }
17823                 break;
17824             case 38: // up
17825             case 40: // down
17826                 if (!this.keyboardNavigation) {
17827                     break;
17828                 }
17829                 dir = e.keyCode == 38 ? -1 : 1;
17830                 if (e.ctrlKey){
17831                     newDate = this.moveYear(this.date, dir);
17832                     newViewDate = this.moveYear(this.viewDate, dir);
17833                 } else if (e.shiftKey){
17834                     newDate = this.moveMonth(this.date, dir);
17835                     newViewDate = this.moveMonth(this.viewDate, dir);
17836                 } else {
17837                     newDate = new Date(this.date);
17838                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17839                     newViewDate = new Date(this.viewDate);
17840                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17841                 }
17842                 if (this.dateWithinRange(newDate)){
17843                     this.date = newDate;
17844                     this.viewDate = newViewDate;
17845                     this.setValue(this.formatDate(this.date));
17846 //                    this.update();
17847                     e.preventDefault();
17848                     dateChanged = true;
17849                 }
17850                 break;
17851             case 13: // enter
17852                 this.setValue(this.formatDate(this.date));
17853                 this.hide();
17854                 e.preventDefault();
17855                 break;
17856             case 9: // tab
17857                 this.setValue(this.formatDate(this.date));
17858                 this.hide();
17859                 break;
17860             case 16: // shift
17861             case 17: // ctrl
17862             case 18: // alt
17863                 break;
17864             default :
17865                 this.hide();
17866                 
17867         }
17868     },
17869     
17870     
17871     onClick: function(e) 
17872     {
17873         e.stopPropagation();
17874         e.preventDefault();
17875         
17876         var target = e.getTarget();
17877         
17878         if(target.nodeName.toLowerCase() === 'i'){
17879             target = Roo.get(target).dom.parentNode;
17880         }
17881         
17882         var nodeName = target.nodeName;
17883         var className = target.className;
17884         var html = target.innerHTML;
17885         //Roo.log(nodeName);
17886         
17887         switch(nodeName.toLowerCase()) {
17888             case 'th':
17889                 switch(className) {
17890                     case 'switch':
17891                         this.showMode(1);
17892                         break;
17893                     case 'prev':
17894                     case 'next':
17895                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17896                         switch(this.viewMode){
17897                                 case 0:
17898                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17899                                         break;
17900                                 case 1:
17901                                 case 2:
17902                                         this.viewDate = this.moveYear(this.viewDate, dir);
17903                                         break;
17904                         }
17905                         this.fill();
17906                         break;
17907                     case 'today':
17908                         var date = new Date();
17909                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17910 //                        this.fill()
17911                         this.setValue(this.formatDate(this.date));
17912                         
17913                         this.hide();
17914                         break;
17915                 }
17916                 break;
17917             case 'span':
17918                 if (className.indexOf('disabled') < 0) {
17919                     this.viewDate.setUTCDate(1);
17920                     if (className.indexOf('month') > -1) {
17921                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17922                     } else {
17923                         var year = parseInt(html, 10) || 0;
17924                         this.viewDate.setUTCFullYear(year);
17925                         
17926                     }
17927                     
17928                     if(this.singleMode){
17929                         this.setValue(this.formatDate(this.viewDate));
17930                         this.hide();
17931                         return;
17932                     }
17933                     
17934                     this.showMode(-1);
17935                     this.fill();
17936                 }
17937                 break;
17938                 
17939             case 'td':
17940                 //Roo.log(className);
17941                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17942                     var day = parseInt(html, 10) || 1;
17943                     var year = this.viewDate.getUTCFullYear(),
17944                         month = this.viewDate.getUTCMonth();
17945
17946                     if (className.indexOf('old') > -1) {
17947                         if(month === 0 ){
17948                             month = 11;
17949                             year -= 1;
17950                         }else{
17951                             month -= 1;
17952                         }
17953                     } else if (className.indexOf('new') > -1) {
17954                         if (month == 11) {
17955                             month = 0;
17956                             year += 1;
17957                         } else {
17958                             month += 1;
17959                         }
17960                     }
17961                     //Roo.log([year,month,day]);
17962                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17963                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17964 //                    this.fill();
17965                     //Roo.log(this.formatDate(this.date));
17966                     this.setValue(this.formatDate(this.date));
17967                     this.hide();
17968                 }
17969                 break;
17970         }
17971     },
17972     
17973     setStartDate: function(startDate)
17974     {
17975         this.startDate = startDate || -Infinity;
17976         if (this.startDate !== -Infinity) {
17977             this.startDate = this.parseDate(this.startDate);
17978         }
17979         this.update();
17980         this.updateNavArrows();
17981     },
17982
17983     setEndDate: function(endDate)
17984     {
17985         this.endDate = endDate || Infinity;
17986         if (this.endDate !== Infinity) {
17987             this.endDate = this.parseDate(this.endDate);
17988         }
17989         this.update();
17990         this.updateNavArrows();
17991     },
17992     
17993     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17994     {
17995         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17996         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17997             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17998         }
17999         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18000             return parseInt(d, 10);
18001         });
18002         this.update();
18003         this.updateNavArrows();
18004     },
18005     
18006     updateNavArrows: function() 
18007     {
18008         if(this.singleMode){
18009             return;
18010         }
18011         
18012         var d = new Date(this.viewDate),
18013         year = d.getUTCFullYear(),
18014         month = d.getUTCMonth();
18015         
18016         Roo.each(this.picker().select('.prev', true).elements, function(v){
18017             v.show();
18018             switch (this.viewMode) {
18019                 case 0:
18020
18021                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18022                         v.hide();
18023                     }
18024                     break;
18025                 case 1:
18026                 case 2:
18027                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18028                         v.hide();
18029                     }
18030                     break;
18031             }
18032         });
18033         
18034         Roo.each(this.picker().select('.next', true).elements, function(v){
18035             v.show();
18036             switch (this.viewMode) {
18037                 case 0:
18038
18039                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18040                         v.hide();
18041                     }
18042                     break;
18043                 case 1:
18044                 case 2:
18045                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18046                         v.hide();
18047                     }
18048                     break;
18049             }
18050         })
18051     },
18052     
18053     moveMonth: function(date, dir)
18054     {
18055         if (!dir) {
18056             return date;
18057         }
18058         var new_date = new Date(date.valueOf()),
18059         day = new_date.getUTCDate(),
18060         month = new_date.getUTCMonth(),
18061         mag = Math.abs(dir),
18062         new_month, test;
18063         dir = dir > 0 ? 1 : -1;
18064         if (mag == 1){
18065             test = dir == -1
18066             // If going back one month, make sure month is not current month
18067             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18068             ? function(){
18069                 return new_date.getUTCMonth() == month;
18070             }
18071             // If going forward one month, make sure month is as expected
18072             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18073             : function(){
18074                 return new_date.getUTCMonth() != new_month;
18075             };
18076             new_month = month + dir;
18077             new_date.setUTCMonth(new_month);
18078             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18079             if (new_month < 0 || new_month > 11) {
18080                 new_month = (new_month + 12) % 12;
18081             }
18082         } else {
18083             // For magnitudes >1, move one month at a time...
18084             for (var i=0; i<mag; i++) {
18085                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18086                 new_date = this.moveMonth(new_date, dir);
18087             }
18088             // ...then reset the day, keeping it in the new month
18089             new_month = new_date.getUTCMonth();
18090             new_date.setUTCDate(day);
18091             test = function(){
18092                 return new_month != new_date.getUTCMonth();
18093             };
18094         }
18095         // Common date-resetting loop -- if date is beyond end of month, make it
18096         // end of month
18097         while (test()){
18098             new_date.setUTCDate(--day);
18099             new_date.setUTCMonth(new_month);
18100         }
18101         return new_date;
18102     },
18103
18104     moveYear: function(date, dir)
18105     {
18106         return this.moveMonth(date, dir*12);
18107     },
18108
18109     dateWithinRange: function(date)
18110     {
18111         return date >= this.startDate && date <= this.endDate;
18112     },
18113
18114     
18115     remove: function() 
18116     {
18117         this.picker().remove();
18118     },
18119     
18120     validateValue : function(value)
18121     {
18122         if(value.length < 1)  {
18123             if(this.allowBlank){
18124                 return true;
18125             }
18126             return false;
18127         }
18128         
18129         if(value.length < this.minLength){
18130             return false;
18131         }
18132         if(value.length > this.maxLength){
18133             return false;
18134         }
18135         if(this.vtype){
18136             var vt = Roo.form.VTypes;
18137             if(!vt[this.vtype](value, this)){
18138                 return false;
18139             }
18140         }
18141         if(typeof this.validator == "function"){
18142             var msg = this.validator(value);
18143             if(msg !== true){
18144                 return false;
18145             }
18146         }
18147         
18148         if(this.regex && !this.regex.test(value)){
18149             return false;
18150         }
18151         
18152         if(typeof(this.parseDate(value)) == 'undefined'){
18153             return false;
18154         }
18155         
18156         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18157             return false;
18158         }      
18159         
18160         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18161             return false;
18162         } 
18163         
18164         
18165         return true;
18166     }
18167    
18168 });
18169
18170 Roo.apply(Roo.bootstrap.DateField,  {
18171     
18172     head : {
18173         tag: 'thead',
18174         cn: [
18175         {
18176             tag: 'tr',
18177             cn: [
18178             {
18179                 tag: 'th',
18180                 cls: 'prev',
18181                 html: '<i class="fa fa-arrow-left"/>'
18182             },
18183             {
18184                 tag: 'th',
18185                 cls: 'switch',
18186                 colspan: '5'
18187             },
18188             {
18189                 tag: 'th',
18190                 cls: 'next',
18191                 html: '<i class="fa fa-arrow-right"/>'
18192             }
18193
18194             ]
18195         }
18196         ]
18197     },
18198     
18199     content : {
18200         tag: 'tbody',
18201         cn: [
18202         {
18203             tag: 'tr',
18204             cn: [
18205             {
18206                 tag: 'td',
18207                 colspan: '7'
18208             }
18209             ]
18210         }
18211         ]
18212     },
18213     
18214     footer : {
18215         tag: 'tfoot',
18216         cn: [
18217         {
18218             tag: 'tr',
18219             cn: [
18220             {
18221                 tag: 'th',
18222                 colspan: '7',
18223                 cls: 'today'
18224             }
18225                     
18226             ]
18227         }
18228         ]
18229     },
18230     
18231     dates:{
18232         en: {
18233             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18234             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18235             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18236             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18237             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18238             today: "Today"
18239         }
18240     },
18241     
18242     modes: [
18243     {
18244         clsName: 'days',
18245         navFnc: 'Month',
18246         navStep: 1
18247     },
18248     {
18249         clsName: 'months',
18250         navFnc: 'FullYear',
18251         navStep: 1
18252     },
18253     {
18254         clsName: 'years',
18255         navFnc: 'FullYear',
18256         navStep: 10
18257     }]
18258 });
18259
18260 Roo.apply(Roo.bootstrap.DateField,  {
18261   
18262     template : {
18263         tag: 'div',
18264         cls: 'datepicker dropdown-menu roo-dynamic',
18265         cn: [
18266         {
18267             tag: 'div',
18268             cls: 'datepicker-days',
18269             cn: [
18270             {
18271                 tag: 'table',
18272                 cls: 'table-condensed',
18273                 cn:[
18274                 Roo.bootstrap.DateField.head,
18275                 {
18276                     tag: 'tbody'
18277                 },
18278                 Roo.bootstrap.DateField.footer
18279                 ]
18280             }
18281             ]
18282         },
18283         {
18284             tag: 'div',
18285             cls: 'datepicker-months',
18286             cn: [
18287             {
18288                 tag: 'table',
18289                 cls: 'table-condensed',
18290                 cn:[
18291                 Roo.bootstrap.DateField.head,
18292                 Roo.bootstrap.DateField.content,
18293                 Roo.bootstrap.DateField.footer
18294                 ]
18295             }
18296             ]
18297         },
18298         {
18299             tag: 'div',
18300             cls: 'datepicker-years',
18301             cn: [
18302             {
18303                 tag: 'table',
18304                 cls: 'table-condensed',
18305                 cn:[
18306                 Roo.bootstrap.DateField.head,
18307                 Roo.bootstrap.DateField.content,
18308                 Roo.bootstrap.DateField.footer
18309                 ]
18310             }
18311             ]
18312         }
18313         ]
18314     }
18315 });
18316
18317  
18318
18319  /*
18320  * - LGPL
18321  *
18322  * TimeField
18323  * 
18324  */
18325
18326 /**
18327  * @class Roo.bootstrap.TimeField
18328  * @extends Roo.bootstrap.Input
18329  * Bootstrap DateField class
18330  * 
18331  * 
18332  * @constructor
18333  * Create a new TimeField
18334  * @param {Object} config The config object
18335  */
18336
18337 Roo.bootstrap.TimeField = function(config){
18338     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18339     this.addEvents({
18340             /**
18341              * @event show
18342              * Fires when this field show.
18343              * @param {Roo.bootstrap.DateField} thisthis
18344              * @param {Mixed} date The date value
18345              */
18346             show : true,
18347             /**
18348              * @event show
18349              * Fires when this field hide.
18350              * @param {Roo.bootstrap.DateField} this
18351              * @param {Mixed} date The date value
18352              */
18353             hide : true,
18354             /**
18355              * @event select
18356              * Fires when select a date.
18357              * @param {Roo.bootstrap.DateField} this
18358              * @param {Mixed} date The date value
18359              */
18360             select : true
18361         });
18362 };
18363
18364 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18365     
18366     /**
18367      * @cfg {String} format
18368      * The default time format string which can be overriden for localization support.  The format must be
18369      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18370      */
18371     format : "H:i",
18372        
18373     onRender: function(ct, position)
18374     {
18375         
18376         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18377                 
18378         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18379         
18380         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18381         
18382         this.pop = this.picker().select('>.datepicker-time',true).first();
18383         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18384         
18385         this.picker().on('mousedown', this.onMousedown, this);
18386         this.picker().on('click', this.onClick, this);
18387         
18388         this.picker().addClass('datepicker-dropdown');
18389     
18390         this.fillTime();
18391         this.update();
18392             
18393         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18394         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18395         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18396         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18397         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18398         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18399
18400     },
18401     
18402     fireKey: function(e){
18403         if (!this.picker().isVisible()){
18404             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18405                 this.show();
18406             }
18407             return;
18408         }
18409
18410         e.preventDefault();
18411         
18412         switch(e.keyCode){
18413             case 27: // escape
18414                 this.hide();
18415                 break;
18416             case 37: // left
18417             case 39: // right
18418                 this.onTogglePeriod();
18419                 break;
18420             case 38: // up
18421                 this.onIncrementMinutes();
18422                 break;
18423             case 40: // down
18424                 this.onDecrementMinutes();
18425                 break;
18426             case 13: // enter
18427             case 9: // tab
18428                 this.setTime();
18429                 break;
18430         }
18431     },
18432     
18433     onClick: function(e) {
18434         e.stopPropagation();
18435         e.preventDefault();
18436     },
18437     
18438     picker : function()
18439     {
18440         return this.el.select('.datepicker', true).first();
18441     },
18442     
18443     fillTime: function()
18444     {    
18445         var time = this.pop.select('tbody', true).first();
18446         
18447         time.dom.innerHTML = '';
18448         
18449         time.createChild({
18450             tag: 'tr',
18451             cn: [
18452                 {
18453                     tag: 'td',
18454                     cn: [
18455                         {
18456                             tag: 'a',
18457                             href: '#',
18458                             cls: 'btn',
18459                             cn: [
18460                                 {
18461                                     tag: 'span',
18462                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18463                                 }
18464                             ]
18465                         } 
18466                     ]
18467                 },
18468                 {
18469                     tag: 'td',
18470                     cls: 'separator'
18471                 },
18472                 {
18473                     tag: 'td',
18474                     cn: [
18475                         {
18476                             tag: 'a',
18477                             href: '#',
18478                             cls: 'btn',
18479                             cn: [
18480                                 {
18481                                     tag: 'span',
18482                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18483                                 }
18484                             ]
18485                         }
18486                     ]
18487                 },
18488                 {
18489                     tag: 'td',
18490                     cls: 'separator'
18491                 }
18492             ]
18493         });
18494         
18495         time.createChild({
18496             tag: 'tr',
18497             cn: [
18498                 {
18499                     tag: 'td',
18500                     cn: [
18501                         {
18502                             tag: 'span',
18503                             cls: 'timepicker-hour',
18504                             html: '00'
18505                         }  
18506                     ]
18507                 },
18508                 {
18509                     tag: 'td',
18510                     cls: 'separator',
18511                     html: ':'
18512                 },
18513                 {
18514                     tag: 'td',
18515                     cn: [
18516                         {
18517                             tag: 'span',
18518                             cls: 'timepicker-minute',
18519                             html: '00'
18520                         }  
18521                     ]
18522                 },
18523                 {
18524                     tag: 'td',
18525                     cls: 'separator'
18526                 },
18527                 {
18528                     tag: 'td',
18529                     cn: [
18530                         {
18531                             tag: 'button',
18532                             type: 'button',
18533                             cls: 'btn btn-primary period',
18534                             html: 'AM'
18535                             
18536                         }
18537                     ]
18538                 }
18539             ]
18540         });
18541         
18542         time.createChild({
18543             tag: 'tr',
18544             cn: [
18545                 {
18546                     tag: 'td',
18547                     cn: [
18548                         {
18549                             tag: 'a',
18550                             href: '#',
18551                             cls: 'btn',
18552                             cn: [
18553                                 {
18554                                     tag: 'span',
18555                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18556                                 }
18557                             ]
18558                         }
18559                     ]
18560                 },
18561                 {
18562                     tag: 'td',
18563                     cls: 'separator'
18564                 },
18565                 {
18566                     tag: 'td',
18567                     cn: [
18568                         {
18569                             tag: 'a',
18570                             href: '#',
18571                             cls: 'btn',
18572                             cn: [
18573                                 {
18574                                     tag: 'span',
18575                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18576                                 }
18577                             ]
18578                         }
18579                     ]
18580                 },
18581                 {
18582                     tag: 'td',
18583                     cls: 'separator'
18584                 }
18585             ]
18586         });
18587         
18588     },
18589     
18590     update: function()
18591     {
18592         
18593         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18594         
18595         this.fill();
18596     },
18597     
18598     fill: function() 
18599     {
18600         var hours = this.time.getHours();
18601         var minutes = this.time.getMinutes();
18602         var period = 'AM';
18603         
18604         if(hours > 11){
18605             period = 'PM';
18606         }
18607         
18608         if(hours == 0){
18609             hours = 12;
18610         }
18611         
18612         
18613         if(hours > 12){
18614             hours = hours - 12;
18615         }
18616         
18617         if(hours < 10){
18618             hours = '0' + hours;
18619         }
18620         
18621         if(minutes < 10){
18622             minutes = '0' + minutes;
18623         }
18624         
18625         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18626         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18627         this.pop.select('button', true).first().dom.innerHTML = period;
18628         
18629     },
18630     
18631     place: function()
18632     {   
18633         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18634         
18635         var cls = ['bottom'];
18636         
18637         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18638             cls.pop();
18639             cls.push('top');
18640         }
18641         
18642         cls.push('right');
18643         
18644         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18645             cls.pop();
18646             cls.push('left');
18647         }
18648         
18649         this.picker().addClass(cls.join('-'));
18650         
18651         var _this = this;
18652         
18653         Roo.each(cls, function(c){
18654             if(c == 'bottom'){
18655                 _this.picker().setTop(_this.inputEl().getHeight());
18656                 return;
18657             }
18658             if(c == 'top'){
18659                 _this.picker().setTop(0 - _this.picker().getHeight());
18660                 return;
18661             }
18662             
18663             if(c == 'left'){
18664                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18665                 return;
18666             }
18667             if(c == 'right'){
18668                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18669                 return;
18670             }
18671         });
18672         
18673     },
18674   
18675     onFocus : function()
18676     {
18677         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18678         this.show();
18679     },
18680     
18681     onBlur : function()
18682     {
18683         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18684         this.hide();
18685     },
18686     
18687     show : function()
18688     {
18689         this.picker().show();
18690         this.pop.show();
18691         this.update();
18692         this.place();
18693         
18694         this.fireEvent('show', this, this.date);
18695     },
18696     
18697     hide : function()
18698     {
18699         this.picker().hide();
18700         this.pop.hide();
18701         
18702         this.fireEvent('hide', this, this.date);
18703     },
18704     
18705     setTime : function()
18706     {
18707         this.hide();
18708         this.setValue(this.time.format(this.format));
18709         
18710         this.fireEvent('select', this, this.date);
18711         
18712         
18713     },
18714     
18715     onMousedown: function(e){
18716         e.stopPropagation();
18717         e.preventDefault();
18718     },
18719     
18720     onIncrementHours: function()
18721     {
18722         Roo.log('onIncrementHours');
18723         this.time = this.time.add(Date.HOUR, 1);
18724         this.update();
18725         
18726     },
18727     
18728     onDecrementHours: function()
18729     {
18730         Roo.log('onDecrementHours');
18731         this.time = this.time.add(Date.HOUR, -1);
18732         this.update();
18733     },
18734     
18735     onIncrementMinutes: function()
18736     {
18737         Roo.log('onIncrementMinutes');
18738         this.time = this.time.add(Date.MINUTE, 1);
18739         this.update();
18740     },
18741     
18742     onDecrementMinutes: function()
18743     {
18744         Roo.log('onDecrementMinutes');
18745         this.time = this.time.add(Date.MINUTE, -1);
18746         this.update();
18747     },
18748     
18749     onTogglePeriod: function()
18750     {
18751         Roo.log('onTogglePeriod');
18752         this.time = this.time.add(Date.HOUR, 12);
18753         this.update();
18754     }
18755     
18756    
18757 });
18758
18759 Roo.apply(Roo.bootstrap.TimeField,  {
18760     
18761     content : {
18762         tag: 'tbody',
18763         cn: [
18764             {
18765                 tag: 'tr',
18766                 cn: [
18767                 {
18768                     tag: 'td',
18769                     colspan: '7'
18770                 }
18771                 ]
18772             }
18773         ]
18774     },
18775     
18776     footer : {
18777         tag: 'tfoot',
18778         cn: [
18779             {
18780                 tag: 'tr',
18781                 cn: [
18782                 {
18783                     tag: 'th',
18784                     colspan: '7',
18785                     cls: '',
18786                     cn: [
18787                         {
18788                             tag: 'button',
18789                             cls: 'btn btn-info ok',
18790                             html: 'OK'
18791                         }
18792                     ]
18793                 }
18794
18795                 ]
18796             }
18797         ]
18798     }
18799 });
18800
18801 Roo.apply(Roo.bootstrap.TimeField,  {
18802   
18803     template : {
18804         tag: 'div',
18805         cls: 'datepicker dropdown-menu',
18806         cn: [
18807             {
18808                 tag: 'div',
18809                 cls: 'datepicker-time',
18810                 cn: [
18811                 {
18812                     tag: 'table',
18813                     cls: 'table-condensed',
18814                     cn:[
18815                     Roo.bootstrap.TimeField.content,
18816                     Roo.bootstrap.TimeField.footer
18817                     ]
18818                 }
18819                 ]
18820             }
18821         ]
18822     }
18823 });
18824
18825  
18826
18827  /*
18828  * - LGPL
18829  *
18830  * MonthField
18831  * 
18832  */
18833
18834 /**
18835  * @class Roo.bootstrap.MonthField
18836  * @extends Roo.bootstrap.Input
18837  * Bootstrap MonthField class
18838  * 
18839  * @cfg {String} language default en
18840  * 
18841  * @constructor
18842  * Create a new MonthField
18843  * @param {Object} config The config object
18844  */
18845
18846 Roo.bootstrap.MonthField = function(config){
18847     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18848     
18849     this.addEvents({
18850         /**
18851          * @event show
18852          * Fires when this field show.
18853          * @param {Roo.bootstrap.MonthField} this
18854          * @param {Mixed} date The date value
18855          */
18856         show : true,
18857         /**
18858          * @event show
18859          * Fires when this field hide.
18860          * @param {Roo.bootstrap.MonthField} this
18861          * @param {Mixed} date The date value
18862          */
18863         hide : true,
18864         /**
18865          * @event select
18866          * Fires when select a date.
18867          * @param {Roo.bootstrap.MonthField} this
18868          * @param {String} oldvalue The old value
18869          * @param {String} newvalue The new value
18870          */
18871         select : true
18872     });
18873 };
18874
18875 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18876     
18877     onRender: function(ct, position)
18878     {
18879         
18880         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18881         
18882         this.language = this.language || 'en';
18883         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18884         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18885         
18886         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18887         this.isInline = false;
18888         this.isInput = true;
18889         this.component = this.el.select('.add-on', true).first() || false;
18890         this.component = (this.component && this.component.length === 0) ? false : this.component;
18891         this.hasInput = this.component && this.inputEL().length;
18892         
18893         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18894         
18895         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18896         
18897         this.picker().on('mousedown', this.onMousedown, this);
18898         this.picker().on('click', this.onClick, this);
18899         
18900         this.picker().addClass('datepicker-dropdown');
18901         
18902         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18903             v.setStyle('width', '189px');
18904         });
18905         
18906         this.fillMonths();
18907         
18908         this.update();
18909         
18910         if(this.isInline) {
18911             this.show();
18912         }
18913         
18914     },
18915     
18916     setValue: function(v, suppressEvent)
18917     {   
18918         var o = this.getValue();
18919         
18920         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18921         
18922         this.update();
18923
18924         if(suppressEvent !== true){
18925             this.fireEvent('select', this, o, v);
18926         }
18927         
18928     },
18929     
18930     getValue: function()
18931     {
18932         return this.value;
18933     },
18934     
18935     onClick: function(e) 
18936     {
18937         e.stopPropagation();
18938         e.preventDefault();
18939         
18940         var target = e.getTarget();
18941         
18942         if(target.nodeName.toLowerCase() === 'i'){
18943             target = Roo.get(target).dom.parentNode;
18944         }
18945         
18946         var nodeName = target.nodeName;
18947         var className = target.className;
18948         var html = target.innerHTML;
18949         
18950         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18951             return;
18952         }
18953         
18954         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18955         
18956         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18957         
18958         this.hide();
18959                         
18960     },
18961     
18962     picker : function()
18963     {
18964         return this.pickerEl;
18965     },
18966     
18967     fillMonths: function()
18968     {    
18969         var i = 0;
18970         var months = this.picker().select('>.datepicker-months td', true).first();
18971         
18972         months.dom.innerHTML = '';
18973         
18974         while (i < 12) {
18975             var month = {
18976                 tag: 'span',
18977                 cls: 'month',
18978                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18979             };
18980             
18981             months.createChild(month);
18982         }
18983         
18984     },
18985     
18986     update: function()
18987     {
18988         var _this = this;
18989         
18990         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18991             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18992         }
18993         
18994         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18995             e.removeClass('active');
18996             
18997             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18998                 e.addClass('active');
18999             }
19000         })
19001     },
19002     
19003     place: function()
19004     {
19005         if(this.isInline) {
19006             return;
19007         }
19008         
19009         this.picker().removeClass(['bottom', 'top']);
19010         
19011         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19012             /*
19013              * place to the top of element!
19014              *
19015              */
19016             
19017             this.picker().addClass('top');
19018             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19019             
19020             return;
19021         }
19022         
19023         this.picker().addClass('bottom');
19024         
19025         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19026     },
19027     
19028     onFocus : function()
19029     {
19030         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19031         this.show();
19032     },
19033     
19034     onBlur : function()
19035     {
19036         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19037         
19038         var d = this.inputEl().getValue();
19039         
19040         this.setValue(d);
19041                 
19042         this.hide();
19043     },
19044     
19045     show : function()
19046     {
19047         this.picker().show();
19048         this.picker().select('>.datepicker-months', true).first().show();
19049         this.update();
19050         this.place();
19051         
19052         this.fireEvent('show', this, this.date);
19053     },
19054     
19055     hide : function()
19056     {
19057         if(this.isInline) {
19058             return;
19059         }
19060         this.picker().hide();
19061         this.fireEvent('hide', this, this.date);
19062         
19063     },
19064     
19065     onMousedown: function(e)
19066     {
19067         e.stopPropagation();
19068         e.preventDefault();
19069     },
19070     
19071     keyup: function(e)
19072     {
19073         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19074         this.update();
19075     },
19076
19077     fireKey: function(e)
19078     {
19079         if (!this.picker().isVisible()){
19080             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19081                 this.show();
19082             }
19083             return;
19084         }
19085         
19086         var dir;
19087         
19088         switch(e.keyCode){
19089             case 27: // escape
19090                 this.hide();
19091                 e.preventDefault();
19092                 break;
19093             case 37: // left
19094             case 39: // right
19095                 dir = e.keyCode == 37 ? -1 : 1;
19096                 
19097                 this.vIndex = this.vIndex + dir;
19098                 
19099                 if(this.vIndex < 0){
19100                     this.vIndex = 0;
19101                 }
19102                 
19103                 if(this.vIndex > 11){
19104                     this.vIndex = 11;
19105                 }
19106                 
19107                 if(isNaN(this.vIndex)){
19108                     this.vIndex = 0;
19109                 }
19110                 
19111                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19112                 
19113                 break;
19114             case 38: // up
19115             case 40: // down
19116                 
19117                 dir = e.keyCode == 38 ? -1 : 1;
19118                 
19119                 this.vIndex = this.vIndex + dir * 4;
19120                 
19121                 if(this.vIndex < 0){
19122                     this.vIndex = 0;
19123                 }
19124                 
19125                 if(this.vIndex > 11){
19126                     this.vIndex = 11;
19127                 }
19128                 
19129                 if(isNaN(this.vIndex)){
19130                     this.vIndex = 0;
19131                 }
19132                 
19133                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19134                 break;
19135                 
19136             case 13: // enter
19137                 
19138                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19139                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19140                 }
19141                 
19142                 this.hide();
19143                 e.preventDefault();
19144                 break;
19145             case 9: // tab
19146                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19147                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19148                 }
19149                 this.hide();
19150                 break;
19151             case 16: // shift
19152             case 17: // ctrl
19153             case 18: // alt
19154                 break;
19155             default :
19156                 this.hide();
19157                 
19158         }
19159     },
19160     
19161     remove: function() 
19162     {
19163         this.picker().remove();
19164     }
19165    
19166 });
19167
19168 Roo.apply(Roo.bootstrap.MonthField,  {
19169     
19170     content : {
19171         tag: 'tbody',
19172         cn: [
19173         {
19174             tag: 'tr',
19175             cn: [
19176             {
19177                 tag: 'td',
19178                 colspan: '7'
19179             }
19180             ]
19181         }
19182         ]
19183     },
19184     
19185     dates:{
19186         en: {
19187             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19188             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19189         }
19190     }
19191 });
19192
19193 Roo.apply(Roo.bootstrap.MonthField,  {
19194   
19195     template : {
19196         tag: 'div',
19197         cls: 'datepicker dropdown-menu roo-dynamic',
19198         cn: [
19199             {
19200                 tag: 'div',
19201                 cls: 'datepicker-months',
19202                 cn: [
19203                 {
19204                     tag: 'table',
19205                     cls: 'table-condensed',
19206                     cn:[
19207                         Roo.bootstrap.DateField.content
19208                     ]
19209                 }
19210                 ]
19211             }
19212         ]
19213     }
19214 });
19215
19216  
19217
19218  
19219  /*
19220  * - LGPL
19221  *
19222  * CheckBox
19223  * 
19224  */
19225
19226 /**
19227  * @class Roo.bootstrap.CheckBox
19228  * @extends Roo.bootstrap.Input
19229  * Bootstrap CheckBox class
19230  * 
19231  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19232  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19233  * @cfg {String} boxLabel The text that appears beside the checkbox
19234  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19235  * @cfg {Boolean} checked initnal the element
19236  * @cfg {Boolean} inline inline the element (default false)
19237  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19238  * 
19239  * @constructor
19240  * Create a new CheckBox
19241  * @param {Object} config The config object
19242  */
19243
19244 Roo.bootstrap.CheckBox = function(config){
19245     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19246    
19247     this.addEvents({
19248         /**
19249         * @event check
19250         * Fires when the element is checked or unchecked.
19251         * @param {Roo.bootstrap.CheckBox} this This input
19252         * @param {Boolean} checked The new checked value
19253         */
19254        check : true
19255     });
19256     
19257 };
19258
19259 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19260   
19261     inputType: 'checkbox',
19262     inputValue: 1,
19263     valueOff: 0,
19264     boxLabel: false,
19265     checked: false,
19266     weight : false,
19267     inline: false,
19268     
19269     getAutoCreate : function()
19270     {
19271         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19272         
19273         var id = Roo.id();
19274         
19275         var cfg = {};
19276         
19277         cfg.cls = 'form-group ' + this.inputType; //input-group
19278         
19279         if(this.inline){
19280             cfg.cls += ' ' + this.inputType + '-inline';
19281         }
19282         
19283         var input =  {
19284             tag: 'input',
19285             id : id,
19286             type : this.inputType,
19287             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19288             cls : 'roo-' + this.inputType, //'form-box',
19289             placeholder : this.placeholder || ''
19290             
19291         };
19292         
19293         if (this.weight) { // Validity check?
19294             cfg.cls += " " + this.inputType + "-" + this.weight;
19295         }
19296         
19297         if (this.disabled) {
19298             input.disabled=true;
19299         }
19300         
19301         if(this.checked){
19302             input.checked = this.checked;
19303         }
19304         
19305         if (this.name) {
19306             input.name = this.name;
19307         }
19308         
19309         if (this.size) {
19310             input.cls += ' input-' + this.size;
19311         }
19312         
19313         var settings=this;
19314         
19315         ['xs','sm','md','lg'].map(function(size){
19316             if (settings[size]) {
19317                 cfg.cls += ' col-' + size + '-' + settings[size];
19318             }
19319         });
19320         
19321         var inputblock = input;
19322          
19323         if (this.before || this.after) {
19324             
19325             inputblock = {
19326                 cls : 'input-group',
19327                 cn :  [] 
19328             };
19329             
19330             if (this.before) {
19331                 inputblock.cn.push({
19332                     tag :'span',
19333                     cls : 'input-group-addon',
19334                     html : this.before
19335                 });
19336             }
19337             
19338             inputblock.cn.push(input);
19339             
19340             if (this.after) {
19341                 inputblock.cn.push({
19342                     tag :'span',
19343                     cls : 'input-group-addon',
19344                     html : this.after
19345                 });
19346             }
19347             
19348         }
19349         
19350         if (align ==='left' && this.fieldLabel.length) {
19351 //                Roo.log("left and has label");
19352                 cfg.cn = [
19353                     
19354                     {
19355                         tag: 'label',
19356                         'for' :  id,
19357                         cls : 'control-label col-md-' + this.labelWidth,
19358                         html : this.fieldLabel
19359                         
19360                     },
19361                     {
19362                         cls : "col-md-" + (12 - this.labelWidth), 
19363                         cn: [
19364                             inputblock
19365                         ]
19366                     }
19367                     
19368                 ];
19369         } else if ( this.fieldLabel.length) {
19370 //                Roo.log(" label");
19371                 cfg.cn = [
19372                    
19373                     {
19374                         tag: this.boxLabel ? 'span' : 'label',
19375                         'for': id,
19376                         cls: 'control-label box-input-label',
19377                         //cls : 'input-group-addon',
19378                         html : this.fieldLabel
19379                         
19380                     },
19381                     
19382                     inputblock
19383                     
19384                 ];
19385
19386         } else {
19387             
19388 //                Roo.log(" no label && no align");
19389                 cfg.cn = [  inputblock ] ;
19390                 
19391                 
19392         }
19393         
19394         if(this.boxLabel){
19395              var boxLabelCfg = {
19396                 tag: 'label',
19397                 //'for': id, // box label is handled by onclick - so no for...
19398                 cls: 'box-label',
19399                 html: this.boxLabel
19400             };
19401             
19402             if(this.tooltip){
19403                 boxLabelCfg.tooltip = this.tooltip;
19404             }
19405              
19406             cfg.cn.push(boxLabelCfg);
19407         }
19408         
19409         
19410        
19411         return cfg;
19412         
19413     },
19414     
19415     /**
19416      * return the real input element.
19417      */
19418     inputEl: function ()
19419     {
19420         return this.el.select('input.roo-' + this.inputType,true).first();
19421     },
19422     
19423     labelEl: function()
19424     {
19425         return this.el.select('label.control-label',true).first();
19426     },
19427     /* depricated... */
19428     
19429     label: function()
19430     {
19431         return this.labelEl();
19432     },
19433     
19434     boxLabelEl: function()
19435     {
19436         return this.el.select('label.box-label',true).first();
19437     },
19438     
19439     initEvents : function()
19440     {
19441 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19442         
19443         this.inputEl().on('click', this.onClick,  this);
19444         
19445         if (this.boxLabel) { 
19446             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19447         }
19448         
19449         this.startValue = this.getValue();
19450         
19451         if(this.groupId){
19452             Roo.bootstrap.CheckBox.register(this);
19453         }
19454     },
19455     
19456     onClick : function()
19457     {   
19458         this.setChecked(!this.checked);
19459     },
19460     
19461     setChecked : function(state,suppressEvent)
19462     {
19463         this.startValue = this.getValue();
19464         
19465         if(this.inputType == 'radio'){
19466             
19467             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19468                 e.dom.checked = false;
19469             });
19470             
19471             this.inputEl().dom.checked = true;
19472             
19473             this.inputEl().dom.value = this.inputValue;
19474             
19475             if(suppressEvent !== true){
19476                 this.fireEvent('check', this, true);
19477             }
19478             
19479             this.validate();
19480             
19481             return;
19482         }
19483         
19484         this.checked = state;
19485         
19486         this.inputEl().dom.checked = state;
19487         
19488         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19489         
19490         if(suppressEvent !== true){
19491             this.fireEvent('check', this, state);
19492         }
19493         
19494         this.validate();
19495     },
19496     
19497     getValue : function()
19498     {
19499         if(this.inputType == 'radio'){
19500             return this.getGroupValue();
19501         }
19502         
19503         return this.inputEl().getValue();
19504         
19505     },
19506     
19507     getGroupValue : function()
19508     {
19509         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19510             return '';
19511         }
19512         
19513         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19514     },
19515     
19516     setValue : function(v,suppressEvent)
19517     {
19518         if(this.inputType == 'radio'){
19519             this.setGroupValue(v, suppressEvent);
19520             return;
19521         }
19522         
19523         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19524         
19525         this.validate();
19526     },
19527     
19528     setGroupValue : function(v, suppressEvent)
19529     {
19530         this.startValue = this.getValue();
19531         
19532         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19533             e.dom.checked = false;
19534             
19535             if(e.dom.value == v){
19536                 e.dom.checked = true;
19537             }
19538         });
19539         
19540         if(suppressEvent !== true){
19541             this.fireEvent('check', this, true);
19542         }
19543
19544         this.validate();
19545         
19546         return;
19547     },
19548     
19549     validate : function()
19550     {
19551         if(
19552                 this.disabled || 
19553                 (this.inputType == 'radio' && this.validateRadio()) ||
19554                 (this.inputType == 'checkbox' && this.validateCheckbox())
19555         ){
19556             this.markValid();
19557             return true;
19558         }
19559         
19560         this.markInvalid();
19561         return false;
19562     },
19563     
19564     validateRadio : function()
19565     {
19566         if(this.allowBlank){
19567             return true;
19568         }
19569         
19570         var valid = false;
19571         
19572         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19573             if(!e.dom.checked){
19574                 return;
19575             }
19576             
19577             valid = true;
19578             
19579             return false;
19580         });
19581         
19582         return valid;
19583     },
19584     
19585     validateCheckbox : function()
19586     {
19587         if(!this.groupId){
19588             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19589         }
19590         
19591         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19592         
19593         if(!group){
19594             return false;
19595         }
19596         
19597         var r = false;
19598         
19599         for(var i in group){
19600             if(r){
19601                 break;
19602             }
19603             
19604             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19605         }
19606         
19607         return r;
19608     },
19609     
19610     /**
19611      * Mark this field as valid
19612      */
19613     markValid : function()
19614     {
19615         if(this.allowBlank){
19616             return;
19617         }
19618         
19619         var _this = this;
19620         
19621         this.fireEvent('valid', this);
19622         
19623         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19624         
19625         if(this.groupId){
19626             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19627         }
19628         
19629         if(label){
19630             label.markValid();
19631         }
19632         
19633         if(this.inputType == 'radio'){
19634             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19635                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19636                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19637             });
19638             
19639             return;
19640         }
19641         
19642         if(!this.groupId){
19643             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19644             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19645             return;
19646         }
19647         
19648         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19649             
19650         if(!group){
19651             return;
19652         }
19653         
19654         for(var i in group){
19655             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19656             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19657         }
19658     },
19659     
19660      /**
19661      * Mark this field as invalid
19662      * @param {String} msg The validation message
19663      */
19664     markInvalid : function(msg)
19665     {
19666         if(this.allowBlank){
19667             return;
19668         }
19669         
19670         var _this = this;
19671         
19672         this.fireEvent('invalid', this, msg);
19673         
19674         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19675         
19676         if(this.groupId){
19677             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19678         }
19679         
19680         if(label){
19681             label.markInvalid();
19682         }
19683             
19684         if(this.inputType == 'radio'){
19685             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19686                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19687                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19688             });
19689             
19690             return;
19691         }
19692         
19693         if(!this.groupId){
19694             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19695             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19696             return;
19697         }
19698         
19699         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19700         
19701         if(!group){
19702             return;
19703         }
19704         
19705         for(var i in group){
19706             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19707             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19708         }
19709         
19710     }
19711     
19712 });
19713
19714 Roo.apply(Roo.bootstrap.CheckBox, {
19715     
19716     groups: {},
19717     
19718      /**
19719     * register a CheckBox Group
19720     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19721     */
19722     register : function(checkbox)
19723     {
19724         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19725             this.groups[checkbox.groupId] = {};
19726         }
19727         
19728         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19729             return;
19730         }
19731         
19732         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19733         
19734     },
19735     /**
19736     * fetch a CheckBox Group based on the group ID
19737     * @param {string} the group ID
19738     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19739     */
19740     get: function(groupId) {
19741         if (typeof(this.groups[groupId]) == 'undefined') {
19742             return false;
19743         }
19744         
19745         return this.groups[groupId] ;
19746     }
19747     
19748     
19749 });
19750 /*
19751  * - LGPL
19752  *
19753  * Radio
19754  *
19755  *
19756  * not inline
19757  *<div class="radio">
19758   <label>
19759     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19760     Option one is this and that&mdash;be sure to include why it's great
19761   </label>
19762 </div>
19763  *
19764  *
19765  *inline
19766  *<span>
19767  *<label class="radio-inline">fieldLabel</label>
19768  *<label class="radio-inline">
19769   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19770 </label>
19771 <span>
19772  * 
19773  * 
19774  */
19775
19776 /**
19777  * @class Roo.bootstrap.Radio
19778  * @extends Roo.bootstrap.CheckBox
19779  * Bootstrap Radio class
19780
19781  * @constructor
19782  * Create a new Radio
19783  * @param {Object} config The config object
19784  */
19785
19786 Roo.bootstrap.Radio = function(config){
19787     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19788    
19789 };
19790
19791 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19792     
19793     inputType: 'radio',
19794     inputValue: '',
19795     valueOff: '',
19796     
19797     getAutoCreate : function()
19798     {
19799         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19800         align = align || 'left'; // default...
19801         
19802         
19803         
19804         var id = Roo.id();
19805         
19806         var cfg = {
19807                 tag : this.inline ? 'span' : 'div',
19808                 cls : '',
19809                 cn : []
19810         };
19811         
19812         var inline = this.inline ? ' radio-inline' : '';
19813         
19814         var lbl = {
19815                 tag: 'label' ,
19816                 // does not need for, as we wrap the input with it..
19817                 'for' : id,
19818                 cls : 'control-label box-label' + inline,
19819                 cn : []
19820         };
19821         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19822         
19823         var fieldLabel = {
19824             tag: 'label' ,
19825             //cls : 'control-label' + inline,
19826             html : this.fieldLabel,
19827             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19828         };
19829         
19830  
19831         
19832         
19833         var input =  {
19834             tag: 'input',
19835             id : id,
19836             type : this.inputType,
19837             //value : (!this.checked) ? this.valueOff : this.inputValue,
19838             value : this.inputValue,
19839             cls : 'roo-radio',
19840             placeholder : this.placeholder || '' // ?? needed????
19841             
19842         };
19843         if (this.weight) { // Validity check?
19844             input.cls += " radio-" + this.weight;
19845         }
19846         if (this.disabled) {
19847             input.disabled=true;
19848         }
19849         
19850         if(this.checked){
19851             input.checked = this.checked;
19852         }
19853         
19854         if (this.name) {
19855             input.name = this.name;
19856         }
19857         
19858         if (this.size) {
19859             input.cls += ' input-' + this.size;
19860         }
19861         
19862         //?? can span's inline have a width??
19863         
19864         var settings=this;
19865         ['xs','sm','md','lg'].map(function(size){
19866             if (settings[size]) {
19867                 cfg.cls += ' col-' + size + '-' + settings[size];
19868             }
19869         });
19870         
19871         var inputblock = input;
19872         
19873         if (this.before || this.after) {
19874             
19875             inputblock = {
19876                 cls : 'input-group',
19877                 tag : 'span',
19878                 cn :  [] 
19879             };
19880             if (this.before) {
19881                 inputblock.cn.push({
19882                     tag :'span',
19883                     cls : 'input-group-addon',
19884                     html : this.before
19885                 });
19886             }
19887             inputblock.cn.push(input);
19888             if (this.after) {
19889                 inputblock.cn.push({
19890                     tag :'span',
19891                     cls : 'input-group-addon',
19892                     html : this.after
19893                 });
19894             }
19895             
19896         };
19897         
19898         
19899         if (this.fieldLabel && this.fieldLabel.length) {
19900             cfg.cn.push(fieldLabel);
19901         }
19902        
19903         // normal bootstrap puts the input inside the label.
19904         // however with our styled version - it has to go after the input.
19905        
19906         //lbl.cn.push(inputblock);
19907         
19908         var lblwrap =  {
19909             tag: 'span',
19910             cls: 'radio' + inline,
19911             cn: [
19912                 inputblock,
19913                 lbl
19914             ]
19915         };
19916         
19917         cfg.cn.push( lblwrap);
19918         
19919         if(this.boxLabel){
19920             lbl.cn.push({
19921                 tag: 'span',
19922                 html: this.boxLabel
19923             })
19924         }
19925          
19926         
19927         return cfg;
19928         
19929     },
19930     
19931     initEvents : function()
19932     {
19933 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19934         
19935         this.inputEl().on('click', this.onClick,  this);
19936         if (this.boxLabel) {
19937             //Roo.log('find label');
19938             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19939         }
19940         
19941     },
19942     
19943     inputEl: function ()
19944     {
19945         return this.el.select('input.roo-radio',true).first();
19946     },
19947     onClick : function()
19948     {   
19949         Roo.log("click");
19950         this.setChecked(true);
19951     },
19952     
19953     setChecked : function(state,suppressEvent)
19954     {
19955         if(state){
19956             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19957                 v.dom.checked = false;
19958             });
19959         }
19960         Roo.log(this.inputEl().dom);
19961         this.checked = state;
19962         this.inputEl().dom.checked = state;
19963         
19964         if(suppressEvent !== true){
19965             this.fireEvent('check', this, state);
19966         }
19967         
19968         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19969         
19970     },
19971     
19972     getGroupValue : function()
19973     {
19974         var value = '';
19975         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19976             if(v.dom.checked == true){
19977                 value = v.dom.value;
19978             }
19979         });
19980         
19981         return value;
19982     },
19983     
19984     /**
19985      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19986      * @return {Mixed} value The field value
19987      */
19988     getValue : function(){
19989         return this.getGroupValue();
19990     }
19991     
19992 });
19993
19994  
19995 //<script type="text/javascript">
19996
19997 /*
19998  * Based  Ext JS Library 1.1.1
19999  * Copyright(c) 2006-2007, Ext JS, LLC.
20000  * LGPL
20001  *
20002  */
20003  
20004 /**
20005  * @class Roo.HtmlEditorCore
20006  * @extends Roo.Component
20007  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20008  *
20009  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20010  */
20011
20012 Roo.HtmlEditorCore = function(config){
20013     
20014     
20015     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20016     
20017     
20018     this.addEvents({
20019         /**
20020          * @event initialize
20021          * Fires when the editor is fully initialized (including the iframe)
20022          * @param {Roo.HtmlEditorCore} this
20023          */
20024         initialize: true,
20025         /**
20026          * @event activate
20027          * Fires when the editor is first receives the focus. Any insertion must wait
20028          * until after this event.
20029          * @param {Roo.HtmlEditorCore} this
20030          */
20031         activate: true,
20032          /**
20033          * @event beforesync
20034          * Fires before the textarea is updated with content from the editor iframe. Return false
20035          * to cancel the sync.
20036          * @param {Roo.HtmlEditorCore} this
20037          * @param {String} html
20038          */
20039         beforesync: true,
20040          /**
20041          * @event beforepush
20042          * Fires before the iframe editor is updated with content from the textarea. Return false
20043          * to cancel the push.
20044          * @param {Roo.HtmlEditorCore} this
20045          * @param {String} html
20046          */
20047         beforepush: true,
20048          /**
20049          * @event sync
20050          * Fires when the textarea is updated with content from the editor iframe.
20051          * @param {Roo.HtmlEditorCore} this
20052          * @param {String} html
20053          */
20054         sync: true,
20055          /**
20056          * @event push
20057          * Fires when the iframe editor is updated with content from the textarea.
20058          * @param {Roo.HtmlEditorCore} this
20059          * @param {String} html
20060          */
20061         push: true,
20062         
20063         /**
20064          * @event editorevent
20065          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20066          * @param {Roo.HtmlEditorCore} this
20067          */
20068         editorevent: true
20069         
20070     });
20071     
20072     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20073     
20074     // defaults : white / black...
20075     this.applyBlacklists();
20076     
20077     
20078     
20079 };
20080
20081
20082 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20083
20084
20085      /**
20086      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20087      */
20088     
20089     owner : false,
20090     
20091      /**
20092      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20093      *                        Roo.resizable.
20094      */
20095     resizable : false,
20096      /**
20097      * @cfg {Number} height (in pixels)
20098      */   
20099     height: 300,
20100    /**
20101      * @cfg {Number} width (in pixels)
20102      */   
20103     width: 500,
20104     
20105     /**
20106      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20107      * 
20108      */
20109     stylesheets: false,
20110     
20111     // id of frame..
20112     frameId: false,
20113     
20114     // private properties
20115     validationEvent : false,
20116     deferHeight: true,
20117     initialized : false,
20118     activated : false,
20119     sourceEditMode : false,
20120     onFocus : Roo.emptyFn,
20121     iframePad:3,
20122     hideMode:'offsets',
20123     
20124     clearUp: true,
20125     
20126     // blacklist + whitelisted elements..
20127     black: false,
20128     white: false,
20129      
20130     
20131
20132     /**
20133      * Protected method that will not generally be called directly. It
20134      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20135      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20136      */
20137     getDocMarkup : function(){
20138         // body styles..
20139         var st = '';
20140         
20141         // inherit styels from page...?? 
20142         if (this.stylesheets === false) {
20143             
20144             Roo.get(document.head).select('style').each(function(node) {
20145                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20146             });
20147             
20148             Roo.get(document.head).select('link').each(function(node) { 
20149                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20150             });
20151             
20152         } else if (!this.stylesheets.length) {
20153                 // simple..
20154                 st = '<style type="text/css">' +
20155                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20156                    '</style>';
20157         } else { 
20158             
20159         }
20160         
20161         st +=  '<style type="text/css">' +
20162             'IMG { cursor: pointer } ' +
20163         '</style>';
20164
20165         
20166         return '<html><head>' + st  +
20167             //<style type="text/css">' +
20168             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20169             //'</style>' +
20170             ' </head><body class="roo-htmleditor-body"></body></html>';
20171     },
20172
20173     // private
20174     onRender : function(ct, position)
20175     {
20176         var _t = this;
20177         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20178         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20179         
20180         
20181         this.el.dom.style.border = '0 none';
20182         this.el.dom.setAttribute('tabIndex', -1);
20183         this.el.addClass('x-hidden hide');
20184         
20185         
20186         
20187         if(Roo.isIE){ // fix IE 1px bogus margin
20188             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20189         }
20190        
20191         
20192         this.frameId = Roo.id();
20193         
20194          
20195         
20196         var iframe = this.owner.wrap.createChild({
20197             tag: 'iframe',
20198             cls: 'form-control', // bootstrap..
20199             id: this.frameId,
20200             name: this.frameId,
20201             frameBorder : 'no',
20202             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20203         }, this.el
20204         );
20205         
20206         
20207         this.iframe = iframe.dom;
20208
20209          this.assignDocWin();
20210         
20211         this.doc.designMode = 'on';
20212        
20213         this.doc.open();
20214         this.doc.write(this.getDocMarkup());
20215         this.doc.close();
20216
20217         
20218         var task = { // must defer to wait for browser to be ready
20219             run : function(){
20220                 //console.log("run task?" + this.doc.readyState);
20221                 this.assignDocWin();
20222                 if(this.doc.body || this.doc.readyState == 'complete'){
20223                     try {
20224                         this.doc.designMode="on";
20225                     } catch (e) {
20226                         return;
20227                     }
20228                     Roo.TaskMgr.stop(task);
20229                     this.initEditor.defer(10, this);
20230                 }
20231             },
20232             interval : 10,
20233             duration: 10000,
20234             scope: this
20235         };
20236         Roo.TaskMgr.start(task);
20237
20238     },
20239
20240     // private
20241     onResize : function(w, h)
20242     {
20243          Roo.log('resize: ' +w + ',' + h );
20244         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20245         if(!this.iframe){
20246             return;
20247         }
20248         if(typeof w == 'number'){
20249             
20250             this.iframe.style.width = w + 'px';
20251         }
20252         if(typeof h == 'number'){
20253             
20254             this.iframe.style.height = h + 'px';
20255             if(this.doc){
20256                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20257             }
20258         }
20259         
20260     },
20261
20262     /**
20263      * Toggles the editor between standard and source edit mode.
20264      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20265      */
20266     toggleSourceEdit : function(sourceEditMode){
20267         
20268         this.sourceEditMode = sourceEditMode === true;
20269         
20270         if(this.sourceEditMode){
20271  
20272             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20273             
20274         }else{
20275             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20276             //this.iframe.className = '';
20277             this.deferFocus();
20278         }
20279         //this.setSize(this.owner.wrap.getSize());
20280         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20281     },
20282
20283     
20284   
20285
20286     /**
20287      * Protected method that will not generally be called directly. If you need/want
20288      * custom HTML cleanup, this is the method you should override.
20289      * @param {String} html The HTML to be cleaned
20290      * return {String} The cleaned HTML
20291      */
20292     cleanHtml : function(html){
20293         html = String(html);
20294         if(html.length > 5){
20295             if(Roo.isSafari){ // strip safari nonsense
20296                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20297             }
20298         }
20299         if(html == '&nbsp;'){
20300             html = '';
20301         }
20302         return html;
20303     },
20304
20305     /**
20306      * HTML Editor -> Textarea
20307      * Protected method that will not generally be called directly. Syncs the contents
20308      * of the editor iframe with the textarea.
20309      */
20310     syncValue : function(){
20311         if(this.initialized){
20312             var bd = (this.doc.body || this.doc.documentElement);
20313             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20314             var html = bd.innerHTML;
20315             if(Roo.isSafari){
20316                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20317                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20318                 if(m && m[1]){
20319                     html = '<div style="'+m[0]+'">' + html + '</div>';
20320                 }
20321             }
20322             html = this.cleanHtml(html);
20323             // fix up the special chars.. normaly like back quotes in word...
20324             // however we do not want to do this with chinese..
20325             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20326                 var cc = b.charCodeAt();
20327                 if (
20328                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20329                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20330                     (cc >= 0xf900 && cc < 0xfb00 )
20331                 ) {
20332                         return b;
20333                 }
20334                 return "&#"+cc+";" 
20335             });
20336             if(this.owner.fireEvent('beforesync', this, html) !== false){
20337                 this.el.dom.value = html;
20338                 this.owner.fireEvent('sync', this, html);
20339             }
20340         }
20341     },
20342
20343     /**
20344      * Protected method that will not generally be called directly. Pushes the value of the textarea
20345      * into the iframe editor.
20346      */
20347     pushValue : function(){
20348         if(this.initialized){
20349             var v = this.el.dom.value.trim();
20350             
20351 //            if(v.length < 1){
20352 //                v = '&#160;';
20353 //            }
20354             
20355             if(this.owner.fireEvent('beforepush', this, v) !== false){
20356                 var d = (this.doc.body || this.doc.documentElement);
20357                 d.innerHTML = v;
20358                 this.cleanUpPaste();
20359                 this.el.dom.value = d.innerHTML;
20360                 this.owner.fireEvent('push', this, v);
20361             }
20362         }
20363     },
20364
20365     // private
20366     deferFocus : function(){
20367         this.focus.defer(10, this);
20368     },
20369
20370     // doc'ed in Field
20371     focus : function(){
20372         if(this.win && !this.sourceEditMode){
20373             this.win.focus();
20374         }else{
20375             this.el.focus();
20376         }
20377     },
20378     
20379     assignDocWin: function()
20380     {
20381         var iframe = this.iframe;
20382         
20383          if(Roo.isIE){
20384             this.doc = iframe.contentWindow.document;
20385             this.win = iframe.contentWindow;
20386         } else {
20387 //            if (!Roo.get(this.frameId)) {
20388 //                return;
20389 //            }
20390 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20391 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20392             
20393             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20394                 return;
20395             }
20396             
20397             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20398             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20399         }
20400     },
20401     
20402     // private
20403     initEditor : function(){
20404         //console.log("INIT EDITOR");
20405         this.assignDocWin();
20406         
20407         
20408         
20409         this.doc.designMode="on";
20410         this.doc.open();
20411         this.doc.write(this.getDocMarkup());
20412         this.doc.close();
20413         
20414         var dbody = (this.doc.body || this.doc.documentElement);
20415         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20416         // this copies styles from the containing element into thsi one..
20417         // not sure why we need all of this..
20418         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20419         
20420         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20421         //ss['background-attachment'] = 'fixed'; // w3c
20422         dbody.bgProperties = 'fixed'; // ie
20423         //Roo.DomHelper.applyStyles(dbody, ss);
20424         Roo.EventManager.on(this.doc, {
20425             //'mousedown': this.onEditorEvent,
20426             'mouseup': this.onEditorEvent,
20427             'dblclick': this.onEditorEvent,
20428             'click': this.onEditorEvent,
20429             'keyup': this.onEditorEvent,
20430             buffer:100,
20431             scope: this
20432         });
20433         if(Roo.isGecko){
20434             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20435         }
20436         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20437             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20438         }
20439         this.initialized = true;
20440
20441         this.owner.fireEvent('initialize', this);
20442         this.pushValue();
20443     },
20444
20445     // private
20446     onDestroy : function(){
20447         
20448         
20449         
20450         if(this.rendered){
20451             
20452             //for (var i =0; i < this.toolbars.length;i++) {
20453             //    // fixme - ask toolbars for heights?
20454             //    this.toolbars[i].onDestroy();
20455            // }
20456             
20457             //this.wrap.dom.innerHTML = '';
20458             //this.wrap.remove();
20459         }
20460     },
20461
20462     // private
20463     onFirstFocus : function(){
20464         
20465         this.assignDocWin();
20466         
20467         
20468         this.activated = true;
20469          
20470     
20471         if(Roo.isGecko){ // prevent silly gecko errors
20472             this.win.focus();
20473             var s = this.win.getSelection();
20474             if(!s.focusNode || s.focusNode.nodeType != 3){
20475                 var r = s.getRangeAt(0);
20476                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20477                 r.collapse(true);
20478                 this.deferFocus();
20479             }
20480             try{
20481                 this.execCmd('useCSS', true);
20482                 this.execCmd('styleWithCSS', false);
20483             }catch(e){}
20484         }
20485         this.owner.fireEvent('activate', this);
20486     },
20487
20488     // private
20489     adjustFont: function(btn){
20490         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20491         //if(Roo.isSafari){ // safari
20492         //    adjust *= 2;
20493        // }
20494         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20495         if(Roo.isSafari){ // safari
20496             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20497             v =  (v < 10) ? 10 : v;
20498             v =  (v > 48) ? 48 : v;
20499             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20500             
20501         }
20502         
20503         
20504         v = Math.max(1, v+adjust);
20505         
20506         this.execCmd('FontSize', v  );
20507     },
20508
20509     onEditorEvent : function(e)
20510     {
20511         this.owner.fireEvent('editorevent', this, e);
20512       //  this.updateToolbar();
20513         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20514     },
20515
20516     insertTag : function(tg)
20517     {
20518         // could be a bit smarter... -> wrap the current selected tRoo..
20519         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20520             
20521             range = this.createRange(this.getSelection());
20522             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20523             wrappingNode.appendChild(range.extractContents());
20524             range.insertNode(wrappingNode);
20525
20526             return;
20527             
20528             
20529             
20530         }
20531         this.execCmd("formatblock",   tg);
20532         
20533     },
20534     
20535     insertText : function(txt)
20536     {
20537         
20538         
20539         var range = this.createRange();
20540         range.deleteContents();
20541                //alert(Sender.getAttribute('label'));
20542                
20543         range.insertNode(this.doc.createTextNode(txt));
20544     } ,
20545     
20546      
20547
20548     /**
20549      * Executes a Midas editor command on the editor document and performs necessary focus and
20550      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20551      * @param {String} cmd The Midas command
20552      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20553      */
20554     relayCmd : function(cmd, value){
20555         this.win.focus();
20556         this.execCmd(cmd, value);
20557         this.owner.fireEvent('editorevent', this);
20558         //this.updateToolbar();
20559         this.owner.deferFocus();
20560     },
20561
20562     /**
20563      * Executes a Midas editor command directly on the editor document.
20564      * For visual commands, you should use {@link #relayCmd} instead.
20565      * <b>This should only be called after the editor is initialized.</b>
20566      * @param {String} cmd The Midas command
20567      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20568      */
20569     execCmd : function(cmd, value){
20570         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20571         this.syncValue();
20572     },
20573  
20574  
20575    
20576     /**
20577      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20578      * to insert tRoo.
20579      * @param {String} text | dom node.. 
20580      */
20581     insertAtCursor : function(text)
20582     {
20583         
20584         
20585         
20586         if(!this.activated){
20587             return;
20588         }
20589         /*
20590         if(Roo.isIE){
20591             this.win.focus();
20592             var r = this.doc.selection.createRange();
20593             if(r){
20594                 r.collapse(true);
20595                 r.pasteHTML(text);
20596                 this.syncValue();
20597                 this.deferFocus();
20598             
20599             }
20600             return;
20601         }
20602         */
20603         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20604             this.win.focus();
20605             
20606             
20607             // from jquery ui (MIT licenced)
20608             var range, node;
20609             var win = this.win;
20610             
20611             if (win.getSelection && win.getSelection().getRangeAt) {
20612                 range = win.getSelection().getRangeAt(0);
20613                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20614                 range.insertNode(node);
20615             } else if (win.document.selection && win.document.selection.createRange) {
20616                 // no firefox support
20617                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20618                 win.document.selection.createRange().pasteHTML(txt);
20619             } else {
20620                 // no firefox support
20621                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20622                 this.execCmd('InsertHTML', txt);
20623             } 
20624             
20625             this.syncValue();
20626             
20627             this.deferFocus();
20628         }
20629     },
20630  // private
20631     mozKeyPress : function(e){
20632         if(e.ctrlKey){
20633             var c = e.getCharCode(), cmd;
20634           
20635             if(c > 0){
20636                 c = String.fromCharCode(c).toLowerCase();
20637                 switch(c){
20638                     case 'b':
20639                         cmd = 'bold';
20640                         break;
20641                     case 'i':
20642                         cmd = 'italic';
20643                         break;
20644                     
20645                     case 'u':
20646                         cmd = 'underline';
20647                         break;
20648                     
20649                     case 'v':
20650                         this.cleanUpPaste.defer(100, this);
20651                         return;
20652                         
20653                 }
20654                 if(cmd){
20655                     this.win.focus();
20656                     this.execCmd(cmd);
20657                     this.deferFocus();
20658                     e.preventDefault();
20659                 }
20660                 
20661             }
20662         }
20663     },
20664
20665     // private
20666     fixKeys : function(){ // load time branching for fastest keydown performance
20667         if(Roo.isIE){
20668             return function(e){
20669                 var k = e.getKey(), r;
20670                 if(k == e.TAB){
20671                     e.stopEvent();
20672                     r = this.doc.selection.createRange();
20673                     if(r){
20674                         r.collapse(true);
20675                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20676                         this.deferFocus();
20677                     }
20678                     return;
20679                 }
20680                 
20681                 if(k == e.ENTER){
20682                     r = this.doc.selection.createRange();
20683                     if(r){
20684                         var target = r.parentElement();
20685                         if(!target || target.tagName.toLowerCase() != 'li'){
20686                             e.stopEvent();
20687                             r.pasteHTML('<br />');
20688                             r.collapse(false);
20689                             r.select();
20690                         }
20691                     }
20692                 }
20693                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20694                     this.cleanUpPaste.defer(100, this);
20695                     return;
20696                 }
20697                 
20698                 
20699             };
20700         }else if(Roo.isOpera){
20701             return function(e){
20702                 var k = e.getKey();
20703                 if(k == e.TAB){
20704                     e.stopEvent();
20705                     this.win.focus();
20706                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20707                     this.deferFocus();
20708                 }
20709                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20710                     this.cleanUpPaste.defer(100, this);
20711                     return;
20712                 }
20713                 
20714             };
20715         }else if(Roo.isSafari){
20716             return function(e){
20717                 var k = e.getKey();
20718                 
20719                 if(k == e.TAB){
20720                     e.stopEvent();
20721                     this.execCmd('InsertText','\t');
20722                     this.deferFocus();
20723                     return;
20724                 }
20725                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20726                     this.cleanUpPaste.defer(100, this);
20727                     return;
20728                 }
20729                 
20730              };
20731         }
20732     }(),
20733     
20734     getAllAncestors: function()
20735     {
20736         var p = this.getSelectedNode();
20737         var a = [];
20738         if (!p) {
20739             a.push(p); // push blank onto stack..
20740             p = this.getParentElement();
20741         }
20742         
20743         
20744         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20745             a.push(p);
20746             p = p.parentNode;
20747         }
20748         a.push(this.doc.body);
20749         return a;
20750     },
20751     lastSel : false,
20752     lastSelNode : false,
20753     
20754     
20755     getSelection : function() 
20756     {
20757         this.assignDocWin();
20758         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20759     },
20760     
20761     getSelectedNode: function() 
20762     {
20763         // this may only work on Gecko!!!
20764         
20765         // should we cache this!!!!
20766         
20767         
20768         
20769          
20770         var range = this.createRange(this.getSelection()).cloneRange();
20771         
20772         if (Roo.isIE) {
20773             var parent = range.parentElement();
20774             while (true) {
20775                 var testRange = range.duplicate();
20776                 testRange.moveToElementText(parent);
20777                 if (testRange.inRange(range)) {
20778                     break;
20779                 }
20780                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20781                     break;
20782                 }
20783                 parent = parent.parentElement;
20784             }
20785             return parent;
20786         }
20787         
20788         // is ancestor a text element.
20789         var ac =  range.commonAncestorContainer;
20790         if (ac.nodeType == 3) {
20791             ac = ac.parentNode;
20792         }
20793         
20794         var ar = ac.childNodes;
20795          
20796         var nodes = [];
20797         var other_nodes = [];
20798         var has_other_nodes = false;
20799         for (var i=0;i<ar.length;i++) {
20800             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20801                 continue;
20802             }
20803             // fullly contained node.
20804             
20805             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20806                 nodes.push(ar[i]);
20807                 continue;
20808             }
20809             
20810             // probably selected..
20811             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20812                 other_nodes.push(ar[i]);
20813                 continue;
20814             }
20815             // outer..
20816             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20817                 continue;
20818             }
20819             
20820             
20821             has_other_nodes = true;
20822         }
20823         if (!nodes.length && other_nodes.length) {
20824             nodes= other_nodes;
20825         }
20826         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20827             return false;
20828         }
20829         
20830         return nodes[0];
20831     },
20832     createRange: function(sel)
20833     {
20834         // this has strange effects when using with 
20835         // top toolbar - not sure if it's a great idea.
20836         //this.editor.contentWindow.focus();
20837         if (typeof sel != "undefined") {
20838             try {
20839                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20840             } catch(e) {
20841                 return this.doc.createRange();
20842             }
20843         } else {
20844             return this.doc.createRange();
20845         }
20846     },
20847     getParentElement: function()
20848     {
20849         
20850         this.assignDocWin();
20851         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20852         
20853         var range = this.createRange(sel);
20854          
20855         try {
20856             var p = range.commonAncestorContainer;
20857             while (p.nodeType == 3) { // text node
20858                 p = p.parentNode;
20859             }
20860             return p;
20861         } catch (e) {
20862             return null;
20863         }
20864     
20865     },
20866     /***
20867      *
20868      * Range intersection.. the hard stuff...
20869      *  '-1' = before
20870      *  '0' = hits..
20871      *  '1' = after.
20872      *         [ -- selected range --- ]
20873      *   [fail]                        [fail]
20874      *
20875      *    basically..
20876      *      if end is before start or  hits it. fail.
20877      *      if start is after end or hits it fail.
20878      *
20879      *   if either hits (but other is outside. - then it's not 
20880      *   
20881      *    
20882      **/
20883     
20884     
20885     // @see http://www.thismuchiknow.co.uk/?p=64.
20886     rangeIntersectsNode : function(range, node)
20887     {
20888         var nodeRange = node.ownerDocument.createRange();
20889         try {
20890             nodeRange.selectNode(node);
20891         } catch (e) {
20892             nodeRange.selectNodeContents(node);
20893         }
20894     
20895         var rangeStartRange = range.cloneRange();
20896         rangeStartRange.collapse(true);
20897     
20898         var rangeEndRange = range.cloneRange();
20899         rangeEndRange.collapse(false);
20900     
20901         var nodeStartRange = nodeRange.cloneRange();
20902         nodeStartRange.collapse(true);
20903     
20904         var nodeEndRange = nodeRange.cloneRange();
20905         nodeEndRange.collapse(false);
20906     
20907         return rangeStartRange.compareBoundaryPoints(
20908                  Range.START_TO_START, nodeEndRange) == -1 &&
20909                rangeEndRange.compareBoundaryPoints(
20910                  Range.START_TO_START, nodeStartRange) == 1;
20911         
20912          
20913     },
20914     rangeCompareNode : function(range, node)
20915     {
20916         var nodeRange = node.ownerDocument.createRange();
20917         try {
20918             nodeRange.selectNode(node);
20919         } catch (e) {
20920             nodeRange.selectNodeContents(node);
20921         }
20922         
20923         
20924         range.collapse(true);
20925     
20926         nodeRange.collapse(true);
20927      
20928         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20929         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20930          
20931         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20932         
20933         var nodeIsBefore   =  ss == 1;
20934         var nodeIsAfter    = ee == -1;
20935         
20936         if (nodeIsBefore && nodeIsAfter) {
20937             return 0; // outer
20938         }
20939         if (!nodeIsBefore && nodeIsAfter) {
20940             return 1; //right trailed.
20941         }
20942         
20943         if (nodeIsBefore && !nodeIsAfter) {
20944             return 2;  // left trailed.
20945         }
20946         // fully contined.
20947         return 3;
20948     },
20949
20950     // private? - in a new class?
20951     cleanUpPaste :  function()
20952     {
20953         // cleans up the whole document..
20954         Roo.log('cleanuppaste');
20955         
20956         this.cleanUpChildren(this.doc.body);
20957         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20958         if (clean != this.doc.body.innerHTML) {
20959             this.doc.body.innerHTML = clean;
20960         }
20961         
20962     },
20963     
20964     cleanWordChars : function(input) {// change the chars to hex code
20965         var he = Roo.HtmlEditorCore;
20966         
20967         var output = input;
20968         Roo.each(he.swapCodes, function(sw) { 
20969             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20970             
20971             output = output.replace(swapper, sw[1]);
20972         });
20973         
20974         return output;
20975     },
20976     
20977     
20978     cleanUpChildren : function (n)
20979     {
20980         if (!n.childNodes.length) {
20981             return;
20982         }
20983         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20984            this.cleanUpChild(n.childNodes[i]);
20985         }
20986     },
20987     
20988     
20989         
20990     
20991     cleanUpChild : function (node)
20992     {
20993         var ed = this;
20994         //console.log(node);
20995         if (node.nodeName == "#text") {
20996             // clean up silly Windows -- stuff?
20997             return; 
20998         }
20999         if (node.nodeName == "#comment") {
21000             node.parentNode.removeChild(node);
21001             // clean up silly Windows -- stuff?
21002             return; 
21003         }
21004         var lcname = node.tagName.toLowerCase();
21005         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21006         // whitelist of tags..
21007         
21008         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21009             // remove node.
21010             node.parentNode.removeChild(node);
21011             return;
21012             
21013         }
21014         
21015         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21016         
21017         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21018         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21019         
21020         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21021         //    remove_keep_children = true;
21022         //}
21023         
21024         if (remove_keep_children) {
21025             this.cleanUpChildren(node);
21026             // inserts everything just before this node...
21027             while (node.childNodes.length) {
21028                 var cn = node.childNodes[0];
21029                 node.removeChild(cn);
21030                 node.parentNode.insertBefore(cn, node);
21031             }
21032             node.parentNode.removeChild(node);
21033             return;
21034         }
21035         
21036         if (!node.attributes || !node.attributes.length) {
21037             this.cleanUpChildren(node);
21038             return;
21039         }
21040         
21041         function cleanAttr(n,v)
21042         {
21043             
21044             if (v.match(/^\./) || v.match(/^\//)) {
21045                 return;
21046             }
21047             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21048                 return;
21049             }
21050             if (v.match(/^#/)) {
21051                 return;
21052             }
21053 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21054             node.removeAttribute(n);
21055             
21056         }
21057         
21058         var cwhite = this.cwhite;
21059         var cblack = this.cblack;
21060             
21061         function cleanStyle(n,v)
21062         {
21063             if (v.match(/expression/)) { //XSS?? should we even bother..
21064                 node.removeAttribute(n);
21065                 return;
21066             }
21067             
21068             var parts = v.split(/;/);
21069             var clean = [];
21070             
21071             Roo.each(parts, function(p) {
21072                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21073                 if (!p.length) {
21074                     return true;
21075                 }
21076                 var l = p.split(':').shift().replace(/\s+/g,'');
21077                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21078                 
21079                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21080 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21081                     //node.removeAttribute(n);
21082                     return true;
21083                 }
21084                 //Roo.log()
21085                 // only allow 'c whitelisted system attributes'
21086                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21087 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21088                     //node.removeAttribute(n);
21089                     return true;
21090                 }
21091                 
21092                 
21093                  
21094                 
21095                 clean.push(p);
21096                 return true;
21097             });
21098             if (clean.length) { 
21099                 node.setAttribute(n, clean.join(';'));
21100             } else {
21101                 node.removeAttribute(n);
21102             }
21103             
21104         }
21105         
21106         
21107         for (var i = node.attributes.length-1; i > -1 ; i--) {
21108             var a = node.attributes[i];
21109             //console.log(a);
21110             
21111             if (a.name.toLowerCase().substr(0,2)=='on')  {
21112                 node.removeAttribute(a.name);
21113                 continue;
21114             }
21115             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21116                 node.removeAttribute(a.name);
21117                 continue;
21118             }
21119             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21120                 cleanAttr(a.name,a.value); // fixme..
21121                 continue;
21122             }
21123             if (a.name == 'style') {
21124                 cleanStyle(a.name,a.value);
21125                 continue;
21126             }
21127             /// clean up MS crap..
21128             // tecnically this should be a list of valid class'es..
21129             
21130             
21131             if (a.name == 'class') {
21132                 if (a.value.match(/^Mso/)) {
21133                     node.className = '';
21134                 }
21135                 
21136                 if (a.value.match(/body/)) {
21137                     node.className = '';
21138                 }
21139                 continue;
21140             }
21141             
21142             // style cleanup!?
21143             // class cleanup?
21144             
21145         }
21146         
21147         
21148         this.cleanUpChildren(node);
21149         
21150         
21151     },
21152     
21153     /**
21154      * Clean up MS wordisms...
21155      */
21156     cleanWord : function(node)
21157     {
21158         
21159         
21160         if (!node) {
21161             this.cleanWord(this.doc.body);
21162             return;
21163         }
21164         if (node.nodeName == "#text") {
21165             // clean up silly Windows -- stuff?
21166             return; 
21167         }
21168         if (node.nodeName == "#comment") {
21169             node.parentNode.removeChild(node);
21170             // clean up silly Windows -- stuff?
21171             return; 
21172         }
21173         
21174         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21175             node.parentNode.removeChild(node);
21176             return;
21177         }
21178         
21179         // remove - but keep children..
21180         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21181             while (node.childNodes.length) {
21182                 var cn = node.childNodes[0];
21183                 node.removeChild(cn);
21184                 node.parentNode.insertBefore(cn, node);
21185             }
21186             node.parentNode.removeChild(node);
21187             this.iterateChildren(node, this.cleanWord);
21188             return;
21189         }
21190         // clean styles
21191         if (node.className.length) {
21192             
21193             var cn = node.className.split(/\W+/);
21194             var cna = [];
21195             Roo.each(cn, function(cls) {
21196                 if (cls.match(/Mso[a-zA-Z]+/)) {
21197                     return;
21198                 }
21199                 cna.push(cls);
21200             });
21201             node.className = cna.length ? cna.join(' ') : '';
21202             if (!cna.length) {
21203                 node.removeAttribute("class");
21204             }
21205         }
21206         
21207         if (node.hasAttribute("lang")) {
21208             node.removeAttribute("lang");
21209         }
21210         
21211         if (node.hasAttribute("style")) {
21212             
21213             var styles = node.getAttribute("style").split(";");
21214             var nstyle = [];
21215             Roo.each(styles, function(s) {
21216                 if (!s.match(/:/)) {
21217                     return;
21218                 }
21219                 var kv = s.split(":");
21220                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21221                     return;
21222                 }
21223                 // what ever is left... we allow.
21224                 nstyle.push(s);
21225             });
21226             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21227             if (!nstyle.length) {
21228                 node.removeAttribute('style');
21229             }
21230         }
21231         this.iterateChildren(node, this.cleanWord);
21232         
21233         
21234         
21235     },
21236     /**
21237      * iterateChildren of a Node, calling fn each time, using this as the scole..
21238      * @param {DomNode} node node to iterate children of.
21239      * @param {Function} fn method of this class to call on each item.
21240      */
21241     iterateChildren : function(node, fn)
21242     {
21243         if (!node.childNodes.length) {
21244                 return;
21245         }
21246         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21247            fn.call(this, node.childNodes[i])
21248         }
21249     },
21250     
21251     
21252     /**
21253      * cleanTableWidths.
21254      *
21255      * Quite often pasting from word etc.. results in tables with column and widths.
21256      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21257      *
21258      */
21259     cleanTableWidths : function(node)
21260     {
21261          
21262          
21263         if (!node) {
21264             this.cleanTableWidths(this.doc.body);
21265             return;
21266         }
21267         
21268         // ignore list...
21269         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21270             return; 
21271         }
21272         Roo.log(node.tagName);
21273         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21274             this.iterateChildren(node, this.cleanTableWidths);
21275             return;
21276         }
21277         if (node.hasAttribute('width')) {
21278             node.removeAttribute('width');
21279         }
21280         
21281          
21282         if (node.hasAttribute("style")) {
21283             // pretty basic...
21284             
21285             var styles = node.getAttribute("style").split(";");
21286             var nstyle = [];
21287             Roo.each(styles, function(s) {
21288                 if (!s.match(/:/)) {
21289                     return;
21290                 }
21291                 var kv = s.split(":");
21292                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21293                     return;
21294                 }
21295                 // what ever is left... we allow.
21296                 nstyle.push(s);
21297             });
21298             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21299             if (!nstyle.length) {
21300                 node.removeAttribute('style');
21301             }
21302         }
21303         
21304         this.iterateChildren(node, this.cleanTableWidths);
21305         
21306         
21307     },
21308     
21309     
21310     
21311     
21312     domToHTML : function(currentElement, depth, nopadtext) {
21313         
21314         depth = depth || 0;
21315         nopadtext = nopadtext || false;
21316     
21317         if (!currentElement) {
21318             return this.domToHTML(this.doc.body);
21319         }
21320         
21321         //Roo.log(currentElement);
21322         var j;
21323         var allText = false;
21324         var nodeName = currentElement.nodeName;
21325         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21326         
21327         if  (nodeName == '#text') {
21328             
21329             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21330         }
21331         
21332         
21333         var ret = '';
21334         if (nodeName != 'BODY') {
21335              
21336             var i = 0;
21337             // Prints the node tagName, such as <A>, <IMG>, etc
21338             if (tagName) {
21339                 var attr = [];
21340                 for(i = 0; i < currentElement.attributes.length;i++) {
21341                     // quoting?
21342                     var aname = currentElement.attributes.item(i).name;
21343                     if (!currentElement.attributes.item(i).value.length) {
21344                         continue;
21345                     }
21346                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21347                 }
21348                 
21349                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21350             } 
21351             else {
21352                 
21353                 // eack
21354             }
21355         } else {
21356             tagName = false;
21357         }
21358         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21359             return ret;
21360         }
21361         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21362             nopadtext = true;
21363         }
21364         
21365         
21366         // Traverse the tree
21367         i = 0;
21368         var currentElementChild = currentElement.childNodes.item(i);
21369         var allText = true;
21370         var innerHTML  = '';
21371         lastnode = '';
21372         while (currentElementChild) {
21373             // Formatting code (indent the tree so it looks nice on the screen)
21374             var nopad = nopadtext;
21375             if (lastnode == 'SPAN') {
21376                 nopad  = true;
21377             }
21378             // text
21379             if  (currentElementChild.nodeName == '#text') {
21380                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21381                 toadd = nopadtext ? toadd : toadd.trim();
21382                 if (!nopad && toadd.length > 80) {
21383                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21384                 }
21385                 innerHTML  += toadd;
21386                 
21387                 i++;
21388                 currentElementChild = currentElement.childNodes.item(i);
21389                 lastNode = '';
21390                 continue;
21391             }
21392             allText = false;
21393             
21394             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21395                 
21396             // Recursively traverse the tree structure of the child node
21397             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21398             lastnode = currentElementChild.nodeName;
21399             i++;
21400             currentElementChild=currentElement.childNodes.item(i);
21401         }
21402         
21403         ret += innerHTML;
21404         
21405         if (!allText) {
21406                 // The remaining code is mostly for formatting the tree
21407             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21408         }
21409         
21410         
21411         if (tagName) {
21412             ret+= "</"+tagName+">";
21413         }
21414         return ret;
21415         
21416     },
21417         
21418     applyBlacklists : function()
21419     {
21420         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21421         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21422         
21423         this.white = [];
21424         this.black = [];
21425         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21426             if (b.indexOf(tag) > -1) {
21427                 return;
21428             }
21429             this.white.push(tag);
21430             
21431         }, this);
21432         
21433         Roo.each(w, function(tag) {
21434             if (b.indexOf(tag) > -1) {
21435                 return;
21436             }
21437             if (this.white.indexOf(tag) > -1) {
21438                 return;
21439             }
21440             this.white.push(tag);
21441             
21442         }, this);
21443         
21444         
21445         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21446             if (w.indexOf(tag) > -1) {
21447                 return;
21448             }
21449             this.black.push(tag);
21450             
21451         }, this);
21452         
21453         Roo.each(b, function(tag) {
21454             if (w.indexOf(tag) > -1) {
21455                 return;
21456             }
21457             if (this.black.indexOf(tag) > -1) {
21458                 return;
21459             }
21460             this.black.push(tag);
21461             
21462         }, this);
21463         
21464         
21465         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21466         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21467         
21468         this.cwhite = [];
21469         this.cblack = [];
21470         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21471             if (b.indexOf(tag) > -1) {
21472                 return;
21473             }
21474             this.cwhite.push(tag);
21475             
21476         }, this);
21477         
21478         Roo.each(w, function(tag) {
21479             if (b.indexOf(tag) > -1) {
21480                 return;
21481             }
21482             if (this.cwhite.indexOf(tag) > -1) {
21483                 return;
21484             }
21485             this.cwhite.push(tag);
21486             
21487         }, this);
21488         
21489         
21490         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21491             if (w.indexOf(tag) > -1) {
21492                 return;
21493             }
21494             this.cblack.push(tag);
21495             
21496         }, this);
21497         
21498         Roo.each(b, function(tag) {
21499             if (w.indexOf(tag) > -1) {
21500                 return;
21501             }
21502             if (this.cblack.indexOf(tag) > -1) {
21503                 return;
21504             }
21505             this.cblack.push(tag);
21506             
21507         }, this);
21508     },
21509     
21510     setStylesheets : function(stylesheets)
21511     {
21512         if(typeof(stylesheets) == 'string'){
21513             Roo.get(this.iframe.contentDocument.head).createChild({
21514                 tag : 'link',
21515                 rel : 'stylesheet',
21516                 type : 'text/css',
21517                 href : stylesheets
21518             });
21519             
21520             return;
21521         }
21522         var _this = this;
21523      
21524         Roo.each(stylesheets, function(s) {
21525             if(!s.length){
21526                 return;
21527             }
21528             
21529             Roo.get(_this.iframe.contentDocument.head).createChild({
21530                 tag : 'link',
21531                 rel : 'stylesheet',
21532                 type : 'text/css',
21533                 href : s
21534             });
21535         });
21536
21537         
21538     },
21539     
21540     removeStylesheets : function()
21541     {
21542         var _this = this;
21543         
21544         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21545             s.remove();
21546         });
21547     }
21548     
21549     // hide stuff that is not compatible
21550     /**
21551      * @event blur
21552      * @hide
21553      */
21554     /**
21555      * @event change
21556      * @hide
21557      */
21558     /**
21559      * @event focus
21560      * @hide
21561      */
21562     /**
21563      * @event specialkey
21564      * @hide
21565      */
21566     /**
21567      * @cfg {String} fieldClass @hide
21568      */
21569     /**
21570      * @cfg {String} focusClass @hide
21571      */
21572     /**
21573      * @cfg {String} autoCreate @hide
21574      */
21575     /**
21576      * @cfg {String} inputType @hide
21577      */
21578     /**
21579      * @cfg {String} invalidClass @hide
21580      */
21581     /**
21582      * @cfg {String} invalidText @hide
21583      */
21584     /**
21585      * @cfg {String} msgFx @hide
21586      */
21587     /**
21588      * @cfg {String} validateOnBlur @hide
21589      */
21590 });
21591
21592 Roo.HtmlEditorCore.white = [
21593         'area', 'br', 'img', 'input', 'hr', 'wbr',
21594         
21595        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21596        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21597        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21598        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21599        'table',   'ul',         'xmp', 
21600        
21601        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21602       'thead',   'tr', 
21603      
21604       'dir', 'menu', 'ol', 'ul', 'dl',
21605        
21606       'embed',  'object'
21607 ];
21608
21609
21610 Roo.HtmlEditorCore.black = [
21611     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21612         'applet', // 
21613         'base',   'basefont', 'bgsound', 'blink',  'body', 
21614         'frame',  'frameset', 'head',    'html',   'ilayer', 
21615         'iframe', 'layer',  'link',     'meta',    'object',   
21616         'script', 'style' ,'title',  'xml' // clean later..
21617 ];
21618 Roo.HtmlEditorCore.clean = [
21619     'script', 'style', 'title', 'xml'
21620 ];
21621 Roo.HtmlEditorCore.remove = [
21622     'font'
21623 ];
21624 // attributes..
21625
21626 Roo.HtmlEditorCore.ablack = [
21627     'on'
21628 ];
21629     
21630 Roo.HtmlEditorCore.aclean = [ 
21631     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21632 ];
21633
21634 // protocols..
21635 Roo.HtmlEditorCore.pwhite= [
21636         'http',  'https',  'mailto'
21637 ];
21638
21639 // white listed style attributes.
21640 Roo.HtmlEditorCore.cwhite= [
21641       //  'text-align', /// default is to allow most things..
21642       
21643          
21644 //        'font-size'//??
21645 ];
21646
21647 // black listed style attributes.
21648 Roo.HtmlEditorCore.cblack= [
21649       //  'font-size' -- this can be set by the project 
21650 ];
21651
21652
21653 Roo.HtmlEditorCore.swapCodes   =[ 
21654     [    8211, "--" ], 
21655     [    8212, "--" ], 
21656     [    8216,  "'" ],  
21657     [    8217, "'" ],  
21658     [    8220, '"' ],  
21659     [    8221, '"' ],  
21660     [    8226, "*" ],  
21661     [    8230, "..." ]
21662 ]; 
21663
21664     /*
21665  * - LGPL
21666  *
21667  * HtmlEditor
21668  * 
21669  */
21670
21671 /**
21672  * @class Roo.bootstrap.HtmlEditor
21673  * @extends Roo.bootstrap.TextArea
21674  * Bootstrap HtmlEditor class
21675
21676  * @constructor
21677  * Create a new HtmlEditor
21678  * @param {Object} config The config object
21679  */
21680
21681 Roo.bootstrap.HtmlEditor = function(config){
21682     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21683     if (!this.toolbars) {
21684         this.toolbars = [];
21685     }
21686     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21687     this.addEvents({
21688             /**
21689              * @event initialize
21690              * Fires when the editor is fully initialized (including the iframe)
21691              * @param {HtmlEditor} this
21692              */
21693             initialize: true,
21694             /**
21695              * @event activate
21696              * Fires when the editor is first receives the focus. Any insertion must wait
21697              * until after this event.
21698              * @param {HtmlEditor} this
21699              */
21700             activate: true,
21701              /**
21702              * @event beforesync
21703              * Fires before the textarea is updated with content from the editor iframe. Return false
21704              * to cancel the sync.
21705              * @param {HtmlEditor} this
21706              * @param {String} html
21707              */
21708             beforesync: true,
21709              /**
21710              * @event beforepush
21711              * Fires before the iframe editor is updated with content from the textarea. Return false
21712              * to cancel the push.
21713              * @param {HtmlEditor} this
21714              * @param {String} html
21715              */
21716             beforepush: true,
21717              /**
21718              * @event sync
21719              * Fires when the textarea is updated with content from the editor iframe.
21720              * @param {HtmlEditor} this
21721              * @param {String} html
21722              */
21723             sync: true,
21724              /**
21725              * @event push
21726              * Fires when the iframe editor is updated with content from the textarea.
21727              * @param {HtmlEditor} this
21728              * @param {String} html
21729              */
21730             push: true,
21731              /**
21732              * @event editmodechange
21733              * Fires when the editor switches edit modes
21734              * @param {HtmlEditor} this
21735              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21736              */
21737             editmodechange: true,
21738             /**
21739              * @event editorevent
21740              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21741              * @param {HtmlEditor} this
21742              */
21743             editorevent: true,
21744             /**
21745              * @event firstfocus
21746              * Fires when on first focus - needed by toolbars..
21747              * @param {HtmlEditor} this
21748              */
21749             firstfocus: true,
21750             /**
21751              * @event autosave
21752              * Auto save the htmlEditor value as a file into Events
21753              * @param {HtmlEditor} this
21754              */
21755             autosave: true,
21756             /**
21757              * @event savedpreview
21758              * preview the saved version of htmlEditor
21759              * @param {HtmlEditor} this
21760              */
21761             savedpreview: true
21762         });
21763 };
21764
21765
21766 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21767     
21768     
21769       /**
21770      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21771      */
21772     toolbars : false,
21773    
21774      /**
21775      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21776      *                        Roo.resizable.
21777      */
21778     resizable : false,
21779      /**
21780      * @cfg {Number} height (in pixels)
21781      */   
21782     height: 300,
21783    /**
21784      * @cfg {Number} width (in pixels)
21785      */   
21786     width: false,
21787     
21788     /**
21789      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21790      * 
21791      */
21792     stylesheets: false,
21793     
21794     // id of frame..
21795     frameId: false,
21796     
21797     // private properties
21798     validationEvent : false,
21799     deferHeight: true,
21800     initialized : false,
21801     activated : false,
21802     
21803     onFocus : Roo.emptyFn,
21804     iframePad:3,
21805     hideMode:'offsets',
21806     
21807     
21808     tbContainer : false,
21809     
21810     toolbarContainer :function() {
21811         return this.wrap.select('.x-html-editor-tb',true).first();
21812     },
21813
21814     /**
21815      * Protected method that will not generally be called directly. It
21816      * is called when the editor creates its toolbar. Override this method if you need to
21817      * add custom toolbar buttons.
21818      * @param {HtmlEditor} editor
21819      */
21820     createToolbar : function(){
21821         
21822         Roo.log("create toolbars");
21823         
21824         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21825         this.toolbars[0].render(this.toolbarContainer());
21826         
21827         return;
21828         
21829 //        if (!editor.toolbars || !editor.toolbars.length) {
21830 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21831 //        }
21832 //        
21833 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21834 //            editor.toolbars[i] = Roo.factory(
21835 //                    typeof(editor.toolbars[i]) == 'string' ?
21836 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21837 //                Roo.bootstrap.HtmlEditor);
21838 //            editor.toolbars[i].init(editor);
21839 //        }
21840     },
21841
21842      
21843     // private
21844     onRender : function(ct, position)
21845     {
21846        // Roo.log("Call onRender: " + this.xtype);
21847         var _t = this;
21848         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21849       
21850         this.wrap = this.inputEl().wrap({
21851             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21852         });
21853         
21854         this.editorcore.onRender(ct, position);
21855          
21856         if (this.resizable) {
21857             this.resizeEl = new Roo.Resizable(this.wrap, {
21858                 pinned : true,
21859                 wrap: true,
21860                 dynamic : true,
21861                 minHeight : this.height,
21862                 height: this.height,
21863                 handles : this.resizable,
21864                 width: this.width,
21865                 listeners : {
21866                     resize : function(r, w, h) {
21867                         _t.onResize(w,h); // -something
21868                     }
21869                 }
21870             });
21871             
21872         }
21873         this.createToolbar(this);
21874        
21875         
21876         if(!this.width && this.resizable){
21877             this.setSize(this.wrap.getSize());
21878         }
21879         if (this.resizeEl) {
21880             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21881             // should trigger onReize..
21882         }
21883         
21884     },
21885
21886     // private
21887     onResize : function(w, h)
21888     {
21889         Roo.log('resize: ' +w + ',' + h );
21890         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21891         var ew = false;
21892         var eh = false;
21893         
21894         if(this.inputEl() ){
21895             if(typeof w == 'number'){
21896                 var aw = w - this.wrap.getFrameWidth('lr');
21897                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21898                 ew = aw;
21899             }
21900             if(typeof h == 'number'){
21901                  var tbh = -11;  // fixme it needs to tool bar size!
21902                 for (var i =0; i < this.toolbars.length;i++) {
21903                     // fixme - ask toolbars for heights?
21904                     tbh += this.toolbars[i].el.getHeight();
21905                     //if (this.toolbars[i].footer) {
21906                     //    tbh += this.toolbars[i].footer.el.getHeight();
21907                     //}
21908                 }
21909               
21910                 
21911                 
21912                 
21913                 
21914                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21915                 ah -= 5; // knock a few pixes off for look..
21916                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21917                 var eh = ah;
21918             }
21919         }
21920         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21921         this.editorcore.onResize(ew,eh);
21922         
21923     },
21924
21925     /**
21926      * Toggles the editor between standard and source edit mode.
21927      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21928      */
21929     toggleSourceEdit : function(sourceEditMode)
21930     {
21931         this.editorcore.toggleSourceEdit(sourceEditMode);
21932         
21933         if(this.editorcore.sourceEditMode){
21934             Roo.log('editor - showing textarea');
21935             
21936 //            Roo.log('in');
21937 //            Roo.log(this.syncValue());
21938             this.syncValue();
21939             this.inputEl().removeClass(['hide', 'x-hidden']);
21940             this.inputEl().dom.removeAttribute('tabIndex');
21941             this.inputEl().focus();
21942         }else{
21943             Roo.log('editor - hiding textarea');
21944 //            Roo.log('out')
21945 //            Roo.log(this.pushValue()); 
21946             this.pushValue();
21947             
21948             this.inputEl().addClass(['hide', 'x-hidden']);
21949             this.inputEl().dom.setAttribute('tabIndex', -1);
21950             //this.deferFocus();
21951         }
21952          
21953         if(this.resizable){
21954             this.setSize(this.wrap.getSize());
21955         }
21956         
21957         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21958     },
21959  
21960     // private (for BoxComponent)
21961     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21962
21963     // private (for BoxComponent)
21964     getResizeEl : function(){
21965         return this.wrap;
21966     },
21967
21968     // private (for BoxComponent)
21969     getPositionEl : function(){
21970         return this.wrap;
21971     },
21972
21973     // private
21974     initEvents : function(){
21975         this.originalValue = this.getValue();
21976     },
21977
21978 //    /**
21979 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21980 //     * @method
21981 //     */
21982 //    markInvalid : Roo.emptyFn,
21983 //    /**
21984 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21985 //     * @method
21986 //     */
21987 //    clearInvalid : Roo.emptyFn,
21988
21989     setValue : function(v){
21990         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21991         this.editorcore.pushValue();
21992     },
21993
21994      
21995     // private
21996     deferFocus : function(){
21997         this.focus.defer(10, this);
21998     },
21999
22000     // doc'ed in Field
22001     focus : function(){
22002         this.editorcore.focus();
22003         
22004     },
22005       
22006
22007     // private
22008     onDestroy : function(){
22009         
22010         
22011         
22012         if(this.rendered){
22013             
22014             for (var i =0; i < this.toolbars.length;i++) {
22015                 // fixme - ask toolbars for heights?
22016                 this.toolbars[i].onDestroy();
22017             }
22018             
22019             this.wrap.dom.innerHTML = '';
22020             this.wrap.remove();
22021         }
22022     },
22023
22024     // private
22025     onFirstFocus : function(){
22026         //Roo.log("onFirstFocus");
22027         this.editorcore.onFirstFocus();
22028          for (var i =0; i < this.toolbars.length;i++) {
22029             this.toolbars[i].onFirstFocus();
22030         }
22031         
22032     },
22033     
22034     // private
22035     syncValue : function()
22036     {   
22037         this.editorcore.syncValue();
22038     },
22039     
22040     pushValue : function()
22041     {   
22042         this.editorcore.pushValue();
22043     }
22044      
22045     
22046     // hide stuff that is not compatible
22047     /**
22048      * @event blur
22049      * @hide
22050      */
22051     /**
22052      * @event change
22053      * @hide
22054      */
22055     /**
22056      * @event focus
22057      * @hide
22058      */
22059     /**
22060      * @event specialkey
22061      * @hide
22062      */
22063     /**
22064      * @cfg {String} fieldClass @hide
22065      */
22066     /**
22067      * @cfg {String} focusClass @hide
22068      */
22069     /**
22070      * @cfg {String} autoCreate @hide
22071      */
22072     /**
22073      * @cfg {String} inputType @hide
22074      */
22075     /**
22076      * @cfg {String} invalidClass @hide
22077      */
22078     /**
22079      * @cfg {String} invalidText @hide
22080      */
22081     /**
22082      * @cfg {String} msgFx @hide
22083      */
22084     /**
22085      * @cfg {String} validateOnBlur @hide
22086      */
22087 });
22088  
22089     
22090    
22091    
22092    
22093       
22094 Roo.namespace('Roo.bootstrap.htmleditor');
22095 /**
22096  * @class Roo.bootstrap.HtmlEditorToolbar1
22097  * Basic Toolbar
22098  * 
22099  * Usage:
22100  *
22101  new Roo.bootstrap.HtmlEditor({
22102     ....
22103     toolbars : [
22104         new Roo.bootstrap.HtmlEditorToolbar1({
22105             disable : { fonts: 1 , format: 1, ..., ... , ...],
22106             btns : [ .... ]
22107         })
22108     }
22109      
22110  * 
22111  * @cfg {Object} disable List of elements to disable..
22112  * @cfg {Array} btns List of additional buttons.
22113  * 
22114  * 
22115  * NEEDS Extra CSS? 
22116  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22117  */
22118  
22119 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22120 {
22121     
22122     Roo.apply(this, config);
22123     
22124     // default disabled, based on 'good practice'..
22125     this.disable = this.disable || {};
22126     Roo.applyIf(this.disable, {
22127         fontSize : true,
22128         colors : true,
22129         specialElements : true
22130     });
22131     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22132     
22133     this.editor = config.editor;
22134     this.editorcore = config.editor.editorcore;
22135     
22136     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22137     
22138     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22139     // dont call parent... till later.
22140 }
22141 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22142      
22143     bar : true,
22144     
22145     editor : false,
22146     editorcore : false,
22147     
22148     
22149     formats : [
22150         "p" ,  
22151         "h1","h2","h3","h4","h5","h6", 
22152         "pre", "code", 
22153         "abbr", "acronym", "address", "cite", "samp", "var",
22154         'div','span'
22155     ],
22156     
22157     onRender : function(ct, position)
22158     {
22159        // Roo.log("Call onRender: " + this.xtype);
22160         
22161        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22162        Roo.log(this.el);
22163        this.el.dom.style.marginBottom = '0';
22164        var _this = this;
22165        var editorcore = this.editorcore;
22166        var editor= this.editor;
22167        
22168        var children = [];
22169        var btn = function(id,cmd , toggle, handler){
22170        
22171             var  event = toggle ? 'toggle' : 'click';
22172        
22173             var a = {
22174                 size : 'sm',
22175                 xtype: 'Button',
22176                 xns: Roo.bootstrap,
22177                 glyphicon : id,
22178                 cmd : id || cmd,
22179                 enableToggle:toggle !== false,
22180                 //html : 'submit'
22181                 pressed : toggle ? false : null,
22182                 listeners : {}
22183             };
22184             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22185                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22186             };
22187             children.push(a);
22188             return a;
22189        }
22190         
22191         var style = {
22192                 xtype: 'Button',
22193                 size : 'sm',
22194                 xns: Roo.bootstrap,
22195                 glyphicon : 'font',
22196                 //html : 'submit'
22197                 menu : {
22198                     xtype: 'Menu',
22199                     xns: Roo.bootstrap,
22200                     items:  []
22201                 }
22202         };
22203         Roo.each(this.formats, function(f) {
22204             style.menu.items.push({
22205                 xtype :'MenuItem',
22206                 xns: Roo.bootstrap,
22207                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22208                 tagname : f,
22209                 listeners : {
22210                     click : function()
22211                     {
22212                         editorcore.insertTag(this.tagname);
22213                         editor.focus();
22214                     }
22215                 }
22216                 
22217             });
22218         });
22219          children.push(style);   
22220             
22221             
22222         btn('bold',false,true);
22223         btn('italic',false,true);
22224         btn('align-left', 'justifyleft',true);
22225         btn('align-center', 'justifycenter',true);
22226         btn('align-right' , 'justifyright',true);
22227         btn('link', false, false, function(btn) {
22228             //Roo.log("create link?");
22229             var url = prompt(this.createLinkText, this.defaultLinkValue);
22230             if(url && url != 'http:/'+'/'){
22231                 this.editorcore.relayCmd('createlink', url);
22232             }
22233         }),
22234         btn('list','insertunorderedlist',true);
22235         btn('pencil', false,true, function(btn){
22236                 Roo.log(this);
22237                 
22238                 this.toggleSourceEdit(btn.pressed);
22239         });
22240         /*
22241         var cog = {
22242                 xtype: 'Button',
22243                 size : 'sm',
22244                 xns: Roo.bootstrap,
22245                 glyphicon : 'cog',
22246                 //html : 'submit'
22247                 menu : {
22248                     xtype: 'Menu',
22249                     xns: Roo.bootstrap,
22250                     items:  []
22251                 }
22252         };
22253         
22254         cog.menu.items.push({
22255             xtype :'MenuItem',
22256             xns: Roo.bootstrap,
22257             html : Clean styles,
22258             tagname : f,
22259             listeners : {
22260                 click : function()
22261                 {
22262                     editorcore.insertTag(this.tagname);
22263                     editor.focus();
22264                 }
22265             }
22266             
22267         });
22268        */
22269         
22270          
22271        this.xtype = 'NavSimplebar';
22272         
22273         for(var i=0;i< children.length;i++) {
22274             
22275             this.buttons.add(this.addxtypeChild(children[i]));
22276             
22277         }
22278         
22279         editor.on('editorevent', this.updateToolbar, this);
22280     },
22281     onBtnClick : function(id)
22282     {
22283        this.editorcore.relayCmd(id);
22284        this.editorcore.focus();
22285     },
22286     
22287     /**
22288      * Protected method that will not generally be called directly. It triggers
22289      * a toolbar update by reading the markup state of the current selection in the editor.
22290      */
22291     updateToolbar: function(){
22292
22293         if(!this.editorcore.activated){
22294             this.editor.onFirstFocus(); // is this neeed?
22295             return;
22296         }
22297
22298         var btns = this.buttons; 
22299         var doc = this.editorcore.doc;
22300         btns.get('bold').setActive(doc.queryCommandState('bold'));
22301         btns.get('italic').setActive(doc.queryCommandState('italic'));
22302         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22303         
22304         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22305         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22306         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22307         
22308         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22309         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22310          /*
22311         
22312         var ans = this.editorcore.getAllAncestors();
22313         if (this.formatCombo) {
22314             
22315             
22316             var store = this.formatCombo.store;
22317             this.formatCombo.setValue("");
22318             for (var i =0; i < ans.length;i++) {
22319                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22320                     // select it..
22321                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22322                     break;
22323                 }
22324             }
22325         }
22326         
22327         
22328         
22329         // hides menus... - so this cant be on a menu...
22330         Roo.bootstrap.MenuMgr.hideAll();
22331         */
22332         Roo.bootstrap.MenuMgr.hideAll();
22333         //this.editorsyncValue();
22334     },
22335     onFirstFocus: function() {
22336         this.buttons.each(function(item){
22337            item.enable();
22338         });
22339     },
22340     toggleSourceEdit : function(sourceEditMode){
22341         
22342           
22343         if(sourceEditMode){
22344             Roo.log("disabling buttons");
22345            this.buttons.each( function(item){
22346                 if(item.cmd != 'pencil'){
22347                     item.disable();
22348                 }
22349             });
22350           
22351         }else{
22352             Roo.log("enabling buttons");
22353             if(this.editorcore.initialized){
22354                 this.buttons.each( function(item){
22355                     item.enable();
22356                 });
22357             }
22358             
22359         }
22360         Roo.log("calling toggole on editor");
22361         // tell the editor that it's been pressed..
22362         this.editor.toggleSourceEdit(sourceEditMode);
22363        
22364     }
22365 });
22366
22367
22368
22369
22370
22371 /**
22372  * @class Roo.bootstrap.Table.AbstractSelectionModel
22373  * @extends Roo.util.Observable
22374  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22375  * implemented by descendant classes.  This class should not be directly instantiated.
22376  * @constructor
22377  */
22378 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22379     this.locked = false;
22380     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22381 };
22382
22383
22384 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22385     /** @ignore Called by the grid automatically. Do not call directly. */
22386     init : function(grid){
22387         this.grid = grid;
22388         this.initEvents();
22389     },
22390
22391     /**
22392      * Locks the selections.
22393      */
22394     lock : function(){
22395         this.locked = true;
22396     },
22397
22398     /**
22399      * Unlocks the selections.
22400      */
22401     unlock : function(){
22402         this.locked = false;
22403     },
22404
22405     /**
22406      * Returns true if the selections are locked.
22407      * @return {Boolean}
22408      */
22409     isLocked : function(){
22410         return this.locked;
22411     }
22412 });
22413 /**
22414  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22415  * @class Roo.bootstrap.Table.RowSelectionModel
22416  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22417  * It supports multiple selections and keyboard selection/navigation. 
22418  * @constructor
22419  * @param {Object} config
22420  */
22421
22422 Roo.bootstrap.Table.RowSelectionModel = function(config){
22423     Roo.apply(this, config);
22424     this.selections = new Roo.util.MixedCollection(false, function(o){
22425         return o.id;
22426     });
22427
22428     this.last = false;
22429     this.lastActive = false;
22430
22431     this.addEvents({
22432         /**
22433              * @event selectionchange
22434              * Fires when the selection changes
22435              * @param {SelectionModel} this
22436              */
22437             "selectionchange" : true,
22438         /**
22439              * @event afterselectionchange
22440              * Fires after the selection changes (eg. by key press or clicking)
22441              * @param {SelectionModel} this
22442              */
22443             "afterselectionchange" : true,
22444         /**
22445              * @event beforerowselect
22446              * Fires when a row is selected being selected, return false to cancel.
22447              * @param {SelectionModel} this
22448              * @param {Number} rowIndex The selected index
22449              * @param {Boolean} keepExisting False if other selections will be cleared
22450              */
22451             "beforerowselect" : true,
22452         /**
22453              * @event rowselect
22454              * Fires when a row is selected.
22455              * @param {SelectionModel} this
22456              * @param {Number} rowIndex The selected index
22457              * @param {Roo.data.Record} r The record
22458              */
22459             "rowselect" : true,
22460         /**
22461              * @event rowdeselect
22462              * Fires when a row is deselected.
22463              * @param {SelectionModel} this
22464              * @param {Number} rowIndex The selected index
22465              */
22466         "rowdeselect" : true
22467     });
22468     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22469     this.locked = false;
22470  };
22471
22472 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22473     /**
22474      * @cfg {Boolean} singleSelect
22475      * True to allow selection of only one row at a time (defaults to false)
22476      */
22477     singleSelect : false,
22478
22479     // private
22480     initEvents : function()
22481     {
22482
22483         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22484         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22485         //}else{ // allow click to work like normal
22486          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22487         //}
22488         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22489         this.grid.on("rowclick", this.handleMouseDown, this);
22490         
22491         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22492             "up" : function(e){
22493                 if(!e.shiftKey){
22494                     this.selectPrevious(e.shiftKey);
22495                 }else if(this.last !== false && this.lastActive !== false){
22496                     var last = this.last;
22497                     this.selectRange(this.last,  this.lastActive-1);
22498                     this.grid.getView().focusRow(this.lastActive);
22499                     if(last !== false){
22500                         this.last = last;
22501                     }
22502                 }else{
22503                     this.selectFirstRow();
22504                 }
22505                 this.fireEvent("afterselectionchange", this);
22506             },
22507             "down" : function(e){
22508                 if(!e.shiftKey){
22509                     this.selectNext(e.shiftKey);
22510                 }else if(this.last !== false && this.lastActive !== false){
22511                     var last = this.last;
22512                     this.selectRange(this.last,  this.lastActive+1);
22513                     this.grid.getView().focusRow(this.lastActive);
22514                     if(last !== false){
22515                         this.last = last;
22516                     }
22517                 }else{
22518                     this.selectFirstRow();
22519                 }
22520                 this.fireEvent("afterselectionchange", this);
22521             },
22522             scope: this
22523         });
22524         this.grid.store.on('load', function(){
22525             this.selections.clear();
22526         },this);
22527         /*
22528         var view = this.grid.view;
22529         view.on("refresh", this.onRefresh, this);
22530         view.on("rowupdated", this.onRowUpdated, this);
22531         view.on("rowremoved", this.onRemove, this);
22532         */
22533     },
22534
22535     // private
22536     onRefresh : function()
22537     {
22538         var ds = this.grid.store, i, v = this.grid.view;
22539         var s = this.selections;
22540         s.each(function(r){
22541             if((i = ds.indexOfId(r.id)) != -1){
22542                 v.onRowSelect(i);
22543             }else{
22544                 s.remove(r);
22545             }
22546         });
22547     },
22548
22549     // private
22550     onRemove : function(v, index, r){
22551         this.selections.remove(r);
22552     },
22553
22554     // private
22555     onRowUpdated : function(v, index, r){
22556         if(this.isSelected(r)){
22557             v.onRowSelect(index);
22558         }
22559     },
22560
22561     /**
22562      * Select records.
22563      * @param {Array} records The records to select
22564      * @param {Boolean} keepExisting (optional) True to keep existing selections
22565      */
22566     selectRecords : function(records, keepExisting)
22567     {
22568         if(!keepExisting){
22569             this.clearSelections();
22570         }
22571             var ds = this.grid.store;
22572         for(var i = 0, len = records.length; i < len; i++){
22573             this.selectRow(ds.indexOf(records[i]), true);
22574         }
22575     },
22576
22577     /**
22578      * Gets the number of selected rows.
22579      * @return {Number}
22580      */
22581     getCount : function(){
22582         return this.selections.length;
22583     },
22584
22585     /**
22586      * Selects the first row in the grid.
22587      */
22588     selectFirstRow : function(){
22589         this.selectRow(0);
22590     },
22591
22592     /**
22593      * Select the last row.
22594      * @param {Boolean} keepExisting (optional) True to keep existing selections
22595      */
22596     selectLastRow : function(keepExisting){
22597         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22598         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22599     },
22600
22601     /**
22602      * Selects the row immediately following the last selected row.
22603      * @param {Boolean} keepExisting (optional) True to keep existing selections
22604      */
22605     selectNext : function(keepExisting)
22606     {
22607             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22608             this.selectRow(this.last+1, keepExisting);
22609             this.grid.getView().focusRow(this.last);
22610         }
22611     },
22612
22613     /**
22614      * Selects the row that precedes the last selected row.
22615      * @param {Boolean} keepExisting (optional) True to keep existing selections
22616      */
22617     selectPrevious : function(keepExisting){
22618         if(this.last){
22619             this.selectRow(this.last-1, keepExisting);
22620             this.grid.getView().focusRow(this.last);
22621         }
22622     },
22623
22624     /**
22625      * Returns the selected records
22626      * @return {Array} Array of selected records
22627      */
22628     getSelections : function(){
22629         return [].concat(this.selections.items);
22630     },
22631
22632     /**
22633      * Returns the first selected record.
22634      * @return {Record}
22635      */
22636     getSelected : function(){
22637         return this.selections.itemAt(0);
22638     },
22639
22640
22641     /**
22642      * Clears all selections.
22643      */
22644     clearSelections : function(fast)
22645     {
22646         if(this.locked) {
22647             return;
22648         }
22649         if(fast !== true){
22650                 var ds = this.grid.store;
22651             var s = this.selections;
22652             s.each(function(r){
22653                 this.deselectRow(ds.indexOfId(r.id));
22654             }, this);
22655             s.clear();
22656         }else{
22657             this.selections.clear();
22658         }
22659         this.last = false;
22660     },
22661
22662
22663     /**
22664      * Selects all rows.
22665      */
22666     selectAll : function(){
22667         if(this.locked) {
22668             return;
22669         }
22670         this.selections.clear();
22671         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22672             this.selectRow(i, true);
22673         }
22674     },
22675
22676     /**
22677      * Returns True if there is a selection.
22678      * @return {Boolean}
22679      */
22680     hasSelection : function(){
22681         return this.selections.length > 0;
22682     },
22683
22684     /**
22685      * Returns True if the specified row is selected.
22686      * @param {Number/Record} record The record or index of the record to check
22687      * @return {Boolean}
22688      */
22689     isSelected : function(index){
22690             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22691         return (r && this.selections.key(r.id) ? true : false);
22692     },
22693
22694     /**
22695      * Returns True if the specified record id is selected.
22696      * @param {String} id The id of record to check
22697      * @return {Boolean}
22698      */
22699     isIdSelected : function(id){
22700         return (this.selections.key(id) ? true : false);
22701     },
22702
22703
22704     // private
22705     handleMouseDBClick : function(e, t){
22706         
22707     },
22708     // private
22709     handleMouseDown : function(e, t)
22710     {
22711             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
22712         if(this.isLocked() || rowIndex < 0 ){
22713             return;
22714         };
22715         if(e.shiftKey && this.last !== false){
22716             var last = this.last;
22717             this.selectRange(last, rowIndex, e.ctrlKey);
22718             this.last = last; // reset the last
22719             t.focus();
22720     
22721         }else{
22722             var isSelected = this.isSelected(rowIndex);
22723             //Roo.log("select row:" + rowIndex);
22724             if(isSelected){
22725                 this.deselectRow(rowIndex);
22726             } else {
22727                         this.selectRow(rowIndex, true);
22728             }
22729     
22730             /*
22731                 if(e.button !== 0 && isSelected){
22732                 alert('rowIndex 2: ' + rowIndex);
22733                     view.focusRow(rowIndex);
22734                 }else if(e.ctrlKey && isSelected){
22735                     this.deselectRow(rowIndex);
22736                 }else if(!isSelected){
22737                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22738                     view.focusRow(rowIndex);
22739                 }
22740             */
22741         }
22742         this.fireEvent("afterselectionchange", this);
22743     },
22744     // private
22745     handleDragableRowClick :  function(grid, rowIndex, e) 
22746     {
22747         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22748             this.selectRow(rowIndex, false);
22749             grid.view.focusRow(rowIndex);
22750              this.fireEvent("afterselectionchange", this);
22751         }
22752     },
22753     
22754     /**
22755      * Selects multiple rows.
22756      * @param {Array} rows Array of the indexes of the row to select
22757      * @param {Boolean} keepExisting (optional) True to keep existing selections
22758      */
22759     selectRows : function(rows, keepExisting){
22760         if(!keepExisting){
22761             this.clearSelections();
22762         }
22763         for(var i = 0, len = rows.length; i < len; i++){
22764             this.selectRow(rows[i], true);
22765         }
22766     },
22767
22768     /**
22769      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22770      * @param {Number} startRow The index of the first row in the range
22771      * @param {Number} endRow The index of the last row in the range
22772      * @param {Boolean} keepExisting (optional) True to retain existing selections
22773      */
22774     selectRange : function(startRow, endRow, keepExisting){
22775         if(this.locked) {
22776             return;
22777         }
22778         if(!keepExisting){
22779             this.clearSelections();
22780         }
22781         if(startRow <= endRow){
22782             for(var i = startRow; i <= endRow; i++){
22783                 this.selectRow(i, true);
22784             }
22785         }else{
22786             for(var i = startRow; i >= endRow; i--){
22787                 this.selectRow(i, true);
22788             }
22789         }
22790     },
22791
22792     /**
22793      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22794      * @param {Number} startRow The index of the first row in the range
22795      * @param {Number} endRow The index of the last row in the range
22796      */
22797     deselectRange : function(startRow, endRow, preventViewNotify){
22798         if(this.locked) {
22799             return;
22800         }
22801         for(var i = startRow; i <= endRow; i++){
22802             this.deselectRow(i, preventViewNotify);
22803         }
22804     },
22805
22806     /**
22807      * Selects a row.
22808      * @param {Number} row The index of the row to select
22809      * @param {Boolean} keepExisting (optional) True to keep existing selections
22810      */
22811     selectRow : function(index, keepExisting, preventViewNotify)
22812     {
22813             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
22814             return;
22815         }
22816         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22817             if(!keepExisting || this.singleSelect){
22818                 this.clearSelections();
22819             }
22820             
22821             var r = this.grid.store.getAt(index);
22822             //console.log('selectRow - record id :' + r.id);
22823             
22824             this.selections.add(r);
22825             this.last = this.lastActive = index;
22826             if(!preventViewNotify){
22827                 var proxy = new Roo.Element(
22828                                 this.grid.getRowDom(index)
22829                 );
22830                 proxy.addClass('bg-info info');
22831             }
22832             this.fireEvent("rowselect", this, index, r);
22833             this.fireEvent("selectionchange", this);
22834         }
22835     },
22836
22837     /**
22838      * Deselects a row.
22839      * @param {Number} row The index of the row to deselect
22840      */
22841     deselectRow : function(index, preventViewNotify)
22842     {
22843         if(this.locked) {
22844             return;
22845         }
22846         if(this.last == index){
22847             this.last = false;
22848         }
22849         if(this.lastActive == index){
22850             this.lastActive = false;
22851         }
22852         
22853         var r = this.grid.store.getAt(index);
22854         if (!r) {
22855             return;
22856         }
22857         
22858         this.selections.remove(r);
22859         //.console.log('deselectRow - record id :' + r.id);
22860         if(!preventViewNotify){
22861         
22862             var proxy = new Roo.Element(
22863                 this.grid.getRowDom(index)
22864             );
22865             proxy.removeClass('bg-info info');
22866         }
22867         this.fireEvent("rowdeselect", this, index);
22868         this.fireEvent("selectionchange", this);
22869     },
22870
22871     // private
22872     restoreLast : function(){
22873         if(this._last){
22874             this.last = this._last;
22875         }
22876     },
22877
22878     // private
22879     acceptsNav : function(row, col, cm){
22880         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22881     },
22882
22883     // private
22884     onEditorKey : function(field, e){
22885         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22886         if(k == e.TAB){
22887             e.stopEvent();
22888             ed.completeEdit();
22889             if(e.shiftKey){
22890                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22891             }else{
22892                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22893             }
22894         }else if(k == e.ENTER && !e.ctrlKey){
22895             e.stopEvent();
22896             ed.completeEdit();
22897             if(e.shiftKey){
22898                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22899             }else{
22900                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22901             }
22902         }else if(k == e.ESC){
22903             ed.cancelEdit();
22904         }
22905         if(newCell){
22906             g.startEditing(newCell[0], newCell[1]);
22907         }
22908     }
22909 });
22910 /*
22911  * Based on:
22912  * Ext JS Library 1.1.1
22913  * Copyright(c) 2006-2007, Ext JS, LLC.
22914  *
22915  * Originally Released Under LGPL - original licence link has changed is not relivant.
22916  *
22917  * Fork - LGPL
22918  * <script type="text/javascript">
22919  */
22920  
22921 /**
22922  * @class Roo.bootstrap.PagingToolbar
22923  * @extends Roo.bootstrap.NavSimplebar
22924  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22925  * @constructor
22926  * Create a new PagingToolbar
22927  * @param {Object} config The config object
22928  * @param {Roo.data.Store} store
22929  */
22930 Roo.bootstrap.PagingToolbar = function(config)
22931 {
22932     // old args format still supported... - xtype is prefered..
22933         // created from xtype...
22934     
22935     this.ds = config.dataSource;
22936     
22937     if (config.store && !this.ds) {
22938         this.store= Roo.factory(config.store, Roo.data);
22939         this.ds = this.store;
22940         this.ds.xmodule = this.xmodule || false;
22941     }
22942     
22943     this.toolbarItems = [];
22944     if (config.items) {
22945         this.toolbarItems = config.items;
22946     }
22947     
22948     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22949     
22950     this.cursor = 0;
22951     
22952     if (this.ds) { 
22953         this.bind(this.ds);
22954     }
22955     
22956     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22957     
22958 };
22959
22960 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22961     /**
22962      * @cfg {Roo.data.Store} dataSource
22963      * The underlying data store providing the paged data
22964      */
22965     /**
22966      * @cfg {String/HTMLElement/Element} container
22967      * container The id or element that will contain the toolbar
22968      */
22969     /**
22970      * @cfg {Boolean} displayInfo
22971      * True to display the displayMsg (defaults to false)
22972      */
22973     /**
22974      * @cfg {Number} pageSize
22975      * The number of records to display per page (defaults to 20)
22976      */
22977     pageSize: 20,
22978     /**
22979      * @cfg {String} displayMsg
22980      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22981      */
22982     displayMsg : 'Displaying {0} - {1} of {2}',
22983     /**
22984      * @cfg {String} emptyMsg
22985      * The message to display when no records are found (defaults to "No data to display")
22986      */
22987     emptyMsg : 'No data to display',
22988     /**
22989      * Customizable piece of the default paging text (defaults to "Page")
22990      * @type String
22991      */
22992     beforePageText : "Page",
22993     /**
22994      * Customizable piece of the default paging text (defaults to "of %0")
22995      * @type String
22996      */
22997     afterPageText : "of {0}",
22998     /**
22999      * Customizable piece of the default paging text (defaults to "First Page")
23000      * @type String
23001      */
23002     firstText : "First Page",
23003     /**
23004      * Customizable piece of the default paging text (defaults to "Previous Page")
23005      * @type String
23006      */
23007     prevText : "Previous Page",
23008     /**
23009      * Customizable piece of the default paging text (defaults to "Next Page")
23010      * @type String
23011      */
23012     nextText : "Next Page",
23013     /**
23014      * Customizable piece of the default paging text (defaults to "Last Page")
23015      * @type String
23016      */
23017     lastText : "Last Page",
23018     /**
23019      * Customizable piece of the default paging text (defaults to "Refresh")
23020      * @type String
23021      */
23022     refreshText : "Refresh",
23023
23024     buttons : false,
23025     // private
23026     onRender : function(ct, position) 
23027     {
23028         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23029         this.navgroup.parentId = this.id;
23030         this.navgroup.onRender(this.el, null);
23031         // add the buttons to the navgroup
23032         
23033         if(this.displayInfo){
23034             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23035             this.displayEl = this.el.select('.x-paging-info', true).first();
23036 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23037 //            this.displayEl = navel.el.select('span',true).first();
23038         }
23039         
23040         var _this = this;
23041         
23042         if(this.buttons){
23043             Roo.each(_this.buttons, function(e){ // this might need to use render????
23044                Roo.factory(e).onRender(_this.el, null);
23045             });
23046         }
23047             
23048         Roo.each(_this.toolbarItems, function(e) {
23049             _this.navgroup.addItem(e);
23050         });
23051         
23052         
23053         this.first = this.navgroup.addItem({
23054             tooltip: this.firstText,
23055             cls: "prev",
23056             icon : 'fa fa-backward',
23057             disabled: true,
23058             preventDefault: true,
23059             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23060         });
23061         
23062         this.prev =  this.navgroup.addItem({
23063             tooltip: this.prevText,
23064             cls: "prev",
23065             icon : 'fa fa-step-backward',
23066             disabled: true,
23067             preventDefault: true,
23068             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23069         });
23070     //this.addSeparator();
23071         
23072         
23073         var field = this.navgroup.addItem( {
23074             tagtype : 'span',
23075             cls : 'x-paging-position',
23076             
23077             html : this.beforePageText  +
23078                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23079                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23080          } ); //?? escaped?
23081         
23082         this.field = field.el.select('input', true).first();
23083         this.field.on("keydown", this.onPagingKeydown, this);
23084         this.field.on("focus", function(){this.dom.select();});
23085     
23086     
23087         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23088         //this.field.setHeight(18);
23089         //this.addSeparator();
23090         this.next = this.navgroup.addItem({
23091             tooltip: this.nextText,
23092             cls: "next",
23093             html : ' <i class="fa fa-step-forward">',
23094             disabled: true,
23095             preventDefault: true,
23096             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23097         });
23098         this.last = this.navgroup.addItem({
23099             tooltip: this.lastText,
23100             icon : 'fa fa-forward',
23101             cls: "next",
23102             disabled: true,
23103             preventDefault: true,
23104             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23105         });
23106     //this.addSeparator();
23107         this.loading = this.navgroup.addItem({
23108             tooltip: this.refreshText,
23109             icon: 'fa fa-refresh',
23110             preventDefault: true,
23111             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23112         });
23113         
23114     },
23115
23116     // private
23117     updateInfo : function(){
23118         if(this.displayEl){
23119             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23120             var msg = count == 0 ?
23121                 this.emptyMsg :
23122                 String.format(
23123                     this.displayMsg,
23124                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23125                 );
23126             this.displayEl.update(msg);
23127         }
23128     },
23129
23130     // private
23131     onLoad : function(ds, r, o){
23132        this.cursor = o.params ? o.params.start : 0;
23133        var d = this.getPageData(),
23134             ap = d.activePage,
23135             ps = d.pages;
23136         
23137        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23138        this.field.dom.value = ap;
23139        this.first.setDisabled(ap == 1);
23140        this.prev.setDisabled(ap == 1);
23141        this.next.setDisabled(ap == ps);
23142        this.last.setDisabled(ap == ps);
23143        this.loading.enable();
23144        this.updateInfo();
23145     },
23146
23147     // private
23148     getPageData : function(){
23149         var total = this.ds.getTotalCount();
23150         return {
23151             total : total,
23152             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23153             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23154         };
23155     },
23156
23157     // private
23158     onLoadError : function(){
23159         this.loading.enable();
23160     },
23161
23162     // private
23163     onPagingKeydown : function(e){
23164         var k = e.getKey();
23165         var d = this.getPageData();
23166         if(k == e.RETURN){
23167             var v = this.field.dom.value, pageNum;
23168             if(!v || isNaN(pageNum = parseInt(v, 10))){
23169                 this.field.dom.value = d.activePage;
23170                 return;
23171             }
23172             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23173             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23174             e.stopEvent();
23175         }
23176         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))
23177         {
23178           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23179           this.field.dom.value = pageNum;
23180           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23181           e.stopEvent();
23182         }
23183         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23184         {
23185           var v = this.field.dom.value, pageNum; 
23186           var increment = (e.shiftKey) ? 10 : 1;
23187           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23188                 increment *= -1;
23189           }
23190           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23191             this.field.dom.value = d.activePage;
23192             return;
23193           }
23194           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23195           {
23196             this.field.dom.value = parseInt(v, 10) + increment;
23197             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23198             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23199           }
23200           e.stopEvent();
23201         }
23202     },
23203
23204     // private
23205     beforeLoad : function(){
23206         if(this.loading){
23207             this.loading.disable();
23208         }
23209     },
23210
23211     // private
23212     onClick : function(which){
23213         
23214         var ds = this.ds;
23215         if (!ds) {
23216             return;
23217         }
23218         
23219         switch(which){
23220             case "first":
23221                 ds.load({params:{start: 0, limit: this.pageSize}});
23222             break;
23223             case "prev":
23224                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23225             break;
23226             case "next":
23227                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23228             break;
23229             case "last":
23230                 var total = ds.getTotalCount();
23231                 var extra = total % this.pageSize;
23232                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23233                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23234             break;
23235             case "refresh":
23236                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23237             break;
23238         }
23239     },
23240
23241     /**
23242      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23243      * @param {Roo.data.Store} store The data store to unbind
23244      */
23245     unbind : function(ds){
23246         ds.un("beforeload", this.beforeLoad, this);
23247         ds.un("load", this.onLoad, this);
23248         ds.un("loadexception", this.onLoadError, this);
23249         ds.un("remove", this.updateInfo, this);
23250         ds.un("add", this.updateInfo, this);
23251         this.ds = undefined;
23252     },
23253
23254     /**
23255      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23256      * @param {Roo.data.Store} store The data store to bind
23257      */
23258     bind : function(ds){
23259         ds.on("beforeload", this.beforeLoad, this);
23260         ds.on("load", this.onLoad, this);
23261         ds.on("loadexception", this.onLoadError, this);
23262         ds.on("remove", this.updateInfo, this);
23263         ds.on("add", this.updateInfo, this);
23264         this.ds = ds;
23265     }
23266 });/*
23267  * - LGPL
23268  *
23269  * element
23270  * 
23271  */
23272
23273 /**
23274  * @class Roo.bootstrap.MessageBar
23275  * @extends Roo.bootstrap.Component
23276  * Bootstrap MessageBar class
23277  * @cfg {String} html contents of the MessageBar
23278  * @cfg {String} weight (info | success | warning | danger) default info
23279  * @cfg {String} beforeClass insert the bar before the given class
23280  * @cfg {Boolean} closable (true | false) default false
23281  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23282  * 
23283  * @constructor
23284  * Create a new Element
23285  * @param {Object} config The config object
23286  */
23287
23288 Roo.bootstrap.MessageBar = function(config){
23289     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23290 };
23291
23292 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23293     
23294     html: '',
23295     weight: 'info',
23296     closable: false,
23297     fixed: false,
23298     beforeClass: 'bootstrap-sticky-wrap',
23299     
23300     getAutoCreate : function(){
23301         
23302         var cfg = {
23303             tag: 'div',
23304             cls: 'alert alert-dismissable alert-' + this.weight,
23305             cn: [
23306                 {
23307                     tag: 'span',
23308                     cls: 'message',
23309                     html: this.html || ''
23310                 }
23311             ]
23312         };
23313         
23314         if(this.fixed){
23315             cfg.cls += ' alert-messages-fixed';
23316         }
23317         
23318         if(this.closable){
23319             cfg.cn.push({
23320                 tag: 'button',
23321                 cls: 'close',
23322                 html: 'x'
23323             });
23324         }
23325         
23326         return cfg;
23327     },
23328     
23329     onRender : function(ct, position)
23330     {
23331         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23332         
23333         if(!this.el){
23334             var cfg = Roo.apply({},  this.getAutoCreate());
23335             cfg.id = Roo.id();
23336             
23337             if (this.cls) {
23338                 cfg.cls += ' ' + this.cls;
23339             }
23340             if (this.style) {
23341                 cfg.style = this.style;
23342             }
23343             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23344             
23345             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23346         }
23347         
23348         this.el.select('>button.close').on('click', this.hide, this);
23349         
23350     },
23351     
23352     show : function()
23353     {
23354         if (!this.rendered) {
23355             this.render();
23356         }
23357         
23358         this.el.show();
23359         
23360         this.fireEvent('show', this);
23361         
23362     },
23363     
23364     hide : function()
23365     {
23366         if (!this.rendered) {
23367             this.render();
23368         }
23369         
23370         this.el.hide();
23371         
23372         this.fireEvent('hide', this);
23373     },
23374     
23375     update : function()
23376     {
23377 //        var e = this.el.dom.firstChild;
23378 //        
23379 //        if(this.closable){
23380 //            e = e.nextSibling;
23381 //        }
23382 //        
23383 //        e.data = this.html || '';
23384
23385         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23386     }
23387    
23388 });
23389
23390  
23391
23392      /*
23393  * - LGPL
23394  *
23395  * Graph
23396  * 
23397  */
23398
23399
23400 /**
23401  * @class Roo.bootstrap.Graph
23402  * @extends Roo.bootstrap.Component
23403  * Bootstrap Graph class
23404 > Prameters
23405  -sm {number} sm 4
23406  -md {number} md 5
23407  @cfg {String} graphtype  bar | vbar | pie
23408  @cfg {number} g_x coodinator | centre x (pie)
23409  @cfg {number} g_y coodinator | centre y (pie)
23410  @cfg {number} g_r radius (pie)
23411  @cfg {number} g_height height of the chart (respected by all elements in the set)
23412  @cfg {number} g_width width of the chart (respected by all elements in the set)
23413  @cfg {Object} title The title of the chart
23414     
23415  -{Array}  values
23416  -opts (object) options for the chart 
23417      o {
23418      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23419      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23420      o vgutter (number)
23421      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.
23422      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23423      o to
23424      o stretch (boolean)
23425      o }
23426  -opts (object) options for the pie
23427      o{
23428      o cut
23429      o startAngle (number)
23430      o endAngle (number)
23431      } 
23432  *
23433  * @constructor
23434  * Create a new Input
23435  * @param {Object} config The config object
23436  */
23437
23438 Roo.bootstrap.Graph = function(config){
23439     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23440     
23441     this.addEvents({
23442         // img events
23443         /**
23444          * @event click
23445          * The img click event for the img.
23446          * @param {Roo.EventObject} e
23447          */
23448         "click" : true
23449     });
23450 };
23451
23452 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23453     
23454     sm: 4,
23455     md: 5,
23456     graphtype: 'bar',
23457     g_height: 250,
23458     g_width: 400,
23459     g_x: 50,
23460     g_y: 50,
23461     g_r: 30,
23462     opts:{
23463         //g_colors: this.colors,
23464         g_type: 'soft',
23465         g_gutter: '20%'
23466
23467     },
23468     title : false,
23469
23470     getAutoCreate : function(){
23471         
23472         var cfg = {
23473             tag: 'div',
23474             html : null
23475         };
23476         
23477         
23478         return  cfg;
23479     },
23480
23481     onRender : function(ct,position){
23482         
23483         
23484         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23485         
23486         if (typeof(Raphael) == 'undefined') {
23487             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23488             return;
23489         }
23490         
23491         this.raphael = Raphael(this.el.dom);
23492         
23493                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23494                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23495                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23496                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23497                 /*
23498                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23499                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23500                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23501                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23502                 
23503                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23504                 r.barchart(330, 10, 300, 220, data1);
23505                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23506                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23507                 */
23508                 
23509                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23510                 // r.barchart(30, 30, 560, 250,  xdata, {
23511                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23512                 //     axis : "0 0 1 1",
23513                 //     axisxlabels :  xdata
23514                 //     //yvalues : cols,
23515                    
23516                 // });
23517 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23518 //        
23519 //        this.load(null,xdata,{
23520 //                axis : "0 0 1 1",
23521 //                axisxlabels :  xdata
23522 //                });
23523
23524     },
23525
23526     load : function(graphtype,xdata,opts)
23527     {
23528         this.raphael.clear();
23529         if(!graphtype) {
23530             graphtype = this.graphtype;
23531         }
23532         if(!opts){
23533             opts = this.opts;
23534         }
23535         var r = this.raphael,
23536             fin = function () {
23537                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23538             },
23539             fout = function () {
23540                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23541             },
23542             pfin = function() {
23543                 this.sector.stop();
23544                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23545
23546                 if (this.label) {
23547                     this.label[0].stop();
23548                     this.label[0].attr({ r: 7.5 });
23549                     this.label[1].attr({ "font-weight": 800 });
23550                 }
23551             },
23552             pfout = function() {
23553                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23554
23555                 if (this.label) {
23556                     this.label[0].animate({ r: 5 }, 500, "bounce");
23557                     this.label[1].attr({ "font-weight": 400 });
23558                 }
23559             };
23560
23561         switch(graphtype){
23562             case 'bar':
23563                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23564                 break;
23565             case 'hbar':
23566                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23567                 break;
23568             case 'pie':
23569 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23570 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23571 //            
23572                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23573                 
23574                 break;
23575
23576         }
23577         
23578         if(this.title){
23579             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23580         }
23581         
23582     },
23583     
23584     setTitle: function(o)
23585     {
23586         this.title = o;
23587     },
23588     
23589     initEvents: function() {
23590         
23591         if(!this.href){
23592             this.el.on('click', this.onClick, this);
23593         }
23594     },
23595     
23596     onClick : function(e)
23597     {
23598         Roo.log('img onclick');
23599         this.fireEvent('click', this, e);
23600     }
23601    
23602 });
23603
23604  
23605 /*
23606  * - LGPL
23607  *
23608  * numberBox
23609  * 
23610  */
23611 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23612
23613 /**
23614  * @class Roo.bootstrap.dash.NumberBox
23615  * @extends Roo.bootstrap.Component
23616  * Bootstrap NumberBox class
23617  * @cfg {String} headline Box headline
23618  * @cfg {String} content Box content
23619  * @cfg {String} icon Box icon
23620  * @cfg {String} footer Footer text
23621  * @cfg {String} fhref Footer href
23622  * 
23623  * @constructor
23624  * Create a new NumberBox
23625  * @param {Object} config The config object
23626  */
23627
23628
23629 Roo.bootstrap.dash.NumberBox = function(config){
23630     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23631     
23632 };
23633
23634 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23635     
23636     headline : '',
23637     content : '',
23638     icon : '',
23639     footer : '',
23640     fhref : '',
23641     ficon : '',
23642     
23643     getAutoCreate : function(){
23644         
23645         var cfg = {
23646             tag : 'div',
23647             cls : 'small-box ',
23648             cn : [
23649                 {
23650                     tag : 'div',
23651                     cls : 'inner',
23652                     cn :[
23653                         {
23654                             tag : 'h3',
23655                             cls : 'roo-headline',
23656                             html : this.headline
23657                         },
23658                         {
23659                             tag : 'p',
23660                             cls : 'roo-content',
23661                             html : this.content
23662                         }
23663                     ]
23664                 }
23665             ]
23666         };
23667         
23668         if(this.icon){
23669             cfg.cn.push({
23670                 tag : 'div',
23671                 cls : 'icon',
23672                 cn :[
23673                     {
23674                         tag : 'i',
23675                         cls : 'ion ' + this.icon
23676                     }
23677                 ]
23678             });
23679         }
23680         
23681         if(this.footer){
23682             var footer = {
23683                 tag : 'a',
23684                 cls : 'small-box-footer',
23685                 href : this.fhref || '#',
23686                 html : this.footer
23687             };
23688             
23689             cfg.cn.push(footer);
23690             
23691         }
23692         
23693         return  cfg;
23694     },
23695
23696     onRender : function(ct,position){
23697         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23698
23699
23700        
23701                 
23702     },
23703
23704     setHeadline: function (value)
23705     {
23706         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23707     },
23708     
23709     setFooter: function (value, href)
23710     {
23711         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23712         
23713         if(href){
23714             this.el.select('a.small-box-footer',true).first().attr('href', href);
23715         }
23716         
23717     },
23718
23719     setContent: function (value)
23720     {
23721         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23722     },
23723
23724     initEvents: function() 
23725     {   
23726         
23727     }
23728     
23729 });
23730
23731  
23732 /*
23733  * - LGPL
23734  *
23735  * TabBox
23736  * 
23737  */
23738 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23739
23740 /**
23741  * @class Roo.bootstrap.dash.TabBox
23742  * @extends Roo.bootstrap.Component
23743  * Bootstrap TabBox class
23744  * @cfg {String} title Title of the TabBox
23745  * @cfg {String} icon Icon of the TabBox
23746  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23747  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23748  * 
23749  * @constructor
23750  * Create a new TabBox
23751  * @param {Object} config The config object
23752  */
23753
23754
23755 Roo.bootstrap.dash.TabBox = function(config){
23756     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23757     this.addEvents({
23758         // raw events
23759         /**
23760          * @event addpane
23761          * When a pane is added
23762          * @param {Roo.bootstrap.dash.TabPane} pane
23763          */
23764         "addpane" : true,
23765         /**
23766          * @event activatepane
23767          * When a pane is activated
23768          * @param {Roo.bootstrap.dash.TabPane} pane
23769          */
23770         "activatepane" : true
23771         
23772          
23773     });
23774     
23775     this.panes = [];
23776 };
23777
23778 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23779
23780     title : '',
23781     icon : false,
23782     showtabs : true,
23783     tabScrollable : false,
23784     
23785     getChildContainer : function()
23786     {
23787         return this.el.select('.tab-content', true).first();
23788     },
23789     
23790     getAutoCreate : function(){
23791         
23792         var header = {
23793             tag: 'li',
23794             cls: 'pull-left header',
23795             html: this.title,
23796             cn : []
23797         };
23798         
23799         if(this.icon){
23800             header.cn.push({
23801                 tag: 'i',
23802                 cls: 'fa ' + this.icon
23803             });
23804         }
23805         
23806         var h = {
23807             tag: 'ul',
23808             cls: 'nav nav-tabs pull-right',
23809             cn: [
23810                 header
23811             ]
23812         };
23813         
23814         if(this.tabScrollable){
23815             h = {
23816                 tag: 'div',
23817                 cls: 'tab-header',
23818                 cn: [
23819                     {
23820                         tag: 'ul',
23821                         cls: 'nav nav-tabs pull-right',
23822                         cn: [
23823                             header
23824                         ]
23825                     }
23826                 ]
23827             };
23828         }
23829         
23830         var cfg = {
23831             tag: 'div',
23832             cls: 'nav-tabs-custom',
23833             cn: [
23834                 h,
23835                 {
23836                     tag: 'div',
23837                     cls: 'tab-content no-padding',
23838                     cn: []
23839                 }
23840             ]
23841         };
23842
23843         return  cfg;
23844     },
23845     initEvents : function()
23846     {
23847         //Roo.log('add add pane handler');
23848         this.on('addpane', this.onAddPane, this);
23849     },
23850      /**
23851      * Updates the box title
23852      * @param {String} html to set the title to.
23853      */
23854     setTitle : function(value)
23855     {
23856         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23857     },
23858     onAddPane : function(pane)
23859     {
23860         this.panes.push(pane);
23861         //Roo.log('addpane');
23862         //Roo.log(pane);
23863         // tabs are rendere left to right..
23864         if(!this.showtabs){
23865             return;
23866         }
23867         
23868         var ctr = this.el.select('.nav-tabs', true).first();
23869          
23870          
23871         var existing = ctr.select('.nav-tab',true);
23872         var qty = existing.getCount();;
23873         
23874         
23875         var tab = ctr.createChild({
23876             tag : 'li',
23877             cls : 'nav-tab' + (qty ? '' : ' active'),
23878             cn : [
23879                 {
23880                     tag : 'a',
23881                     href:'#',
23882                     html : pane.title
23883                 }
23884             ]
23885         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23886         pane.tab = tab;
23887         
23888         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23889         if (!qty) {
23890             pane.el.addClass('active');
23891         }
23892         
23893                 
23894     },
23895     onTabClick : function(ev,un,ob,pane)
23896     {
23897         //Roo.log('tab - prev default');
23898         ev.preventDefault();
23899         
23900         
23901         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23902         pane.tab.addClass('active');
23903         //Roo.log(pane.title);
23904         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23905         // technically we should have a deactivate event.. but maybe add later.
23906         // and it should not de-activate the selected tab...
23907         this.fireEvent('activatepane', pane);
23908         pane.el.addClass('active');
23909         pane.fireEvent('activate');
23910         
23911         
23912     },
23913     
23914     getActivePane : function()
23915     {
23916         var r = false;
23917         Roo.each(this.panes, function(p) {
23918             if(p.el.hasClass('active')){
23919                 r = p;
23920                 return false;
23921             }
23922             
23923             return;
23924         });
23925         
23926         return r;
23927     }
23928     
23929     
23930 });
23931
23932  
23933 /*
23934  * - LGPL
23935  *
23936  * Tab pane
23937  * 
23938  */
23939 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23940 /**
23941  * @class Roo.bootstrap.TabPane
23942  * @extends Roo.bootstrap.Component
23943  * Bootstrap TabPane class
23944  * @cfg {Boolean} active (false | true) Default false
23945  * @cfg {String} title title of panel
23946
23947  * 
23948  * @constructor
23949  * Create a new TabPane
23950  * @param {Object} config The config object
23951  */
23952
23953 Roo.bootstrap.dash.TabPane = function(config){
23954     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23955     
23956     this.addEvents({
23957         // raw events
23958         /**
23959          * @event activate
23960          * When a pane is activated
23961          * @param {Roo.bootstrap.dash.TabPane} pane
23962          */
23963         "activate" : true
23964          
23965     });
23966 };
23967
23968 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23969     
23970     active : false,
23971     title : '',
23972     
23973     // the tabBox that this is attached to.
23974     tab : false,
23975      
23976     getAutoCreate : function() 
23977     {
23978         var cfg = {
23979             tag: 'div',
23980             cls: 'tab-pane'
23981         };
23982         
23983         if(this.active){
23984             cfg.cls += ' active';
23985         }
23986         
23987         return cfg;
23988     },
23989     initEvents  : function()
23990     {
23991         //Roo.log('trigger add pane handler');
23992         this.parent().fireEvent('addpane', this)
23993     },
23994     
23995      /**
23996      * Updates the tab title 
23997      * @param {String} html to set the title to.
23998      */
23999     setTitle: function(str)
24000     {
24001         if (!this.tab) {
24002             return;
24003         }
24004         this.title = str;
24005         this.tab.select('a', true).first().dom.innerHTML = str;
24006         
24007     }
24008     
24009     
24010     
24011 });
24012
24013  
24014
24015
24016  /*
24017  * - LGPL
24018  *
24019  * menu
24020  * 
24021  */
24022 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24023
24024 /**
24025  * @class Roo.bootstrap.menu.Menu
24026  * @extends Roo.bootstrap.Component
24027  * Bootstrap Menu class - container for Menu
24028  * @cfg {String} html Text of the menu
24029  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24030  * @cfg {String} icon Font awesome icon
24031  * @cfg {String} pos Menu align to (top | bottom) default bottom
24032  * 
24033  * 
24034  * @constructor
24035  * Create a new Menu
24036  * @param {Object} config The config object
24037  */
24038
24039
24040 Roo.bootstrap.menu.Menu = function(config){
24041     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24042     
24043     this.addEvents({
24044         /**
24045          * @event beforeshow
24046          * Fires before this menu is displayed
24047          * @param {Roo.bootstrap.menu.Menu} this
24048          */
24049         beforeshow : true,
24050         /**
24051          * @event beforehide
24052          * Fires before this menu is hidden
24053          * @param {Roo.bootstrap.menu.Menu} this
24054          */
24055         beforehide : true,
24056         /**
24057          * @event show
24058          * Fires after this menu is displayed
24059          * @param {Roo.bootstrap.menu.Menu} this
24060          */
24061         show : true,
24062         /**
24063          * @event hide
24064          * Fires after this menu is hidden
24065          * @param {Roo.bootstrap.menu.Menu} this
24066          */
24067         hide : true,
24068         /**
24069          * @event click
24070          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24071          * @param {Roo.bootstrap.menu.Menu} this
24072          * @param {Roo.EventObject} e
24073          */
24074         click : true
24075     });
24076     
24077 };
24078
24079 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24080     
24081     submenu : false,
24082     html : '',
24083     weight : 'default',
24084     icon : false,
24085     pos : 'bottom',
24086     
24087     
24088     getChildContainer : function() {
24089         if(this.isSubMenu){
24090             return this.el;
24091         }
24092         
24093         return this.el.select('ul.dropdown-menu', true).first();  
24094     },
24095     
24096     getAutoCreate : function()
24097     {
24098         var text = [
24099             {
24100                 tag : 'span',
24101                 cls : 'roo-menu-text',
24102                 html : this.html
24103             }
24104         ];
24105         
24106         if(this.icon){
24107             text.unshift({
24108                 tag : 'i',
24109                 cls : 'fa ' + this.icon
24110             })
24111         }
24112         
24113         
24114         var cfg = {
24115             tag : 'div',
24116             cls : 'btn-group',
24117             cn : [
24118                 {
24119                     tag : 'button',
24120                     cls : 'dropdown-button btn btn-' + this.weight,
24121                     cn : text
24122                 },
24123                 {
24124                     tag : 'button',
24125                     cls : 'dropdown-toggle btn btn-' + this.weight,
24126                     cn : [
24127                         {
24128                             tag : 'span',
24129                             cls : 'caret'
24130                         }
24131                     ]
24132                 },
24133                 {
24134                     tag : 'ul',
24135                     cls : 'dropdown-menu'
24136                 }
24137             ]
24138             
24139         };
24140         
24141         if(this.pos == 'top'){
24142             cfg.cls += ' dropup';
24143         }
24144         
24145         if(this.isSubMenu){
24146             cfg = {
24147                 tag : 'ul',
24148                 cls : 'dropdown-menu'
24149             }
24150         }
24151         
24152         return cfg;
24153     },
24154     
24155     onRender : function(ct, position)
24156     {
24157         this.isSubMenu = ct.hasClass('dropdown-submenu');
24158         
24159         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24160     },
24161     
24162     initEvents : function() 
24163     {
24164         if(this.isSubMenu){
24165             return;
24166         }
24167         
24168         this.hidden = true;
24169         
24170         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24171         this.triggerEl.on('click', this.onTriggerPress, this);
24172         
24173         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24174         this.buttonEl.on('click', this.onClick, this);
24175         
24176     },
24177     
24178     list : function()
24179     {
24180         if(this.isSubMenu){
24181             return this.el;
24182         }
24183         
24184         return this.el.select('ul.dropdown-menu', true).first();
24185     },
24186     
24187     onClick : function(e)
24188     {
24189         this.fireEvent("click", this, e);
24190     },
24191     
24192     onTriggerPress  : function(e)
24193     {   
24194         if (this.isVisible()) {
24195             this.hide();
24196         } else {
24197             this.show();
24198         }
24199     },
24200     
24201     isVisible : function(){
24202         return !this.hidden;
24203     },
24204     
24205     show : function()
24206     {
24207         this.fireEvent("beforeshow", this);
24208         
24209         this.hidden = false;
24210         this.el.addClass('open');
24211         
24212         Roo.get(document).on("mouseup", this.onMouseUp, this);
24213         
24214         this.fireEvent("show", this);
24215         
24216         
24217     },
24218     
24219     hide : function()
24220     {
24221         this.fireEvent("beforehide", this);
24222         
24223         this.hidden = true;
24224         this.el.removeClass('open');
24225         
24226         Roo.get(document).un("mouseup", this.onMouseUp);
24227         
24228         this.fireEvent("hide", this);
24229     },
24230     
24231     onMouseUp : function()
24232     {
24233         this.hide();
24234     }
24235     
24236 });
24237
24238  
24239  /*
24240  * - LGPL
24241  *
24242  * menu item
24243  * 
24244  */
24245 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24246
24247 /**
24248  * @class Roo.bootstrap.menu.Item
24249  * @extends Roo.bootstrap.Component
24250  * Bootstrap MenuItem class
24251  * @cfg {Boolean} submenu (true | false) default false
24252  * @cfg {String} html text of the item
24253  * @cfg {String} href the link
24254  * @cfg {Boolean} disable (true | false) default false
24255  * @cfg {Boolean} preventDefault (true | false) default true
24256  * @cfg {String} icon Font awesome icon
24257  * @cfg {String} pos Submenu align to (left | right) default right 
24258  * 
24259  * 
24260  * @constructor
24261  * Create a new Item
24262  * @param {Object} config The config object
24263  */
24264
24265
24266 Roo.bootstrap.menu.Item = function(config){
24267     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24268     this.addEvents({
24269         /**
24270          * @event mouseover
24271          * Fires when the mouse is hovering over this menu
24272          * @param {Roo.bootstrap.menu.Item} this
24273          * @param {Roo.EventObject} e
24274          */
24275         mouseover : true,
24276         /**
24277          * @event mouseout
24278          * Fires when the mouse exits this menu
24279          * @param {Roo.bootstrap.menu.Item} this
24280          * @param {Roo.EventObject} e
24281          */
24282         mouseout : true,
24283         // raw events
24284         /**
24285          * @event click
24286          * The raw click event for the entire grid.
24287          * @param {Roo.EventObject} e
24288          */
24289         click : true
24290     });
24291 };
24292
24293 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24294     
24295     submenu : false,
24296     href : '',
24297     html : '',
24298     preventDefault: true,
24299     disable : false,
24300     icon : false,
24301     pos : 'right',
24302     
24303     getAutoCreate : function()
24304     {
24305         var text = [
24306             {
24307                 tag : 'span',
24308                 cls : 'roo-menu-item-text',
24309                 html : this.html
24310             }
24311         ];
24312         
24313         if(this.icon){
24314             text.unshift({
24315                 tag : 'i',
24316                 cls : 'fa ' + this.icon
24317             })
24318         }
24319         
24320         var cfg = {
24321             tag : 'li',
24322             cn : [
24323                 {
24324                     tag : 'a',
24325                     href : this.href || '#',
24326                     cn : text
24327                 }
24328             ]
24329         };
24330         
24331         if(this.disable){
24332             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24333         }
24334         
24335         if(this.submenu){
24336             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24337             
24338             if(this.pos == 'left'){
24339                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24340             }
24341         }
24342         
24343         return cfg;
24344     },
24345     
24346     initEvents : function() 
24347     {
24348         this.el.on('mouseover', this.onMouseOver, this);
24349         this.el.on('mouseout', this.onMouseOut, this);
24350         
24351         this.el.select('a', true).first().on('click', this.onClick, this);
24352         
24353     },
24354     
24355     onClick : function(e)
24356     {
24357         if(this.preventDefault){
24358             e.preventDefault();
24359         }
24360         
24361         this.fireEvent("click", this, e);
24362     },
24363     
24364     onMouseOver : function(e)
24365     {
24366         if(this.submenu && this.pos == 'left'){
24367             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24368         }
24369         
24370         this.fireEvent("mouseover", this, e);
24371     },
24372     
24373     onMouseOut : function(e)
24374     {
24375         this.fireEvent("mouseout", this, e);
24376     }
24377 });
24378
24379  
24380
24381  /*
24382  * - LGPL
24383  *
24384  * menu separator
24385  * 
24386  */
24387 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24388
24389 /**
24390  * @class Roo.bootstrap.menu.Separator
24391  * @extends Roo.bootstrap.Component
24392  * Bootstrap Separator class
24393  * 
24394  * @constructor
24395  * Create a new Separator
24396  * @param {Object} config The config object
24397  */
24398
24399
24400 Roo.bootstrap.menu.Separator = function(config){
24401     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24402 };
24403
24404 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24405     
24406     getAutoCreate : function(){
24407         var cfg = {
24408             tag : 'li',
24409             cls: 'divider'
24410         };
24411         
24412         return cfg;
24413     }
24414    
24415 });
24416
24417  
24418
24419  /*
24420  * - LGPL
24421  *
24422  * Tooltip
24423  * 
24424  */
24425
24426 /**
24427  * @class Roo.bootstrap.Tooltip
24428  * Bootstrap Tooltip class
24429  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24430  * to determine which dom element triggers the tooltip.
24431  * 
24432  * It needs to add support for additional attributes like tooltip-position
24433  * 
24434  * @constructor
24435  * Create a new Toolti
24436  * @param {Object} config The config object
24437  */
24438
24439 Roo.bootstrap.Tooltip = function(config){
24440     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24441 };
24442
24443 Roo.apply(Roo.bootstrap.Tooltip, {
24444     /**
24445      * @function init initialize tooltip monitoring.
24446      * @static
24447      */
24448     currentEl : false,
24449     currentTip : false,
24450     currentRegion : false,
24451     
24452     //  init : delay?
24453     
24454     init : function()
24455     {
24456         Roo.get(document).on('mouseover', this.enter ,this);
24457         Roo.get(document).on('mouseout', this.leave, this);
24458          
24459         
24460         this.currentTip = new Roo.bootstrap.Tooltip();
24461     },
24462     
24463     enter : function(ev)
24464     {
24465         var dom = ev.getTarget();
24466         
24467         //Roo.log(['enter',dom]);
24468         var el = Roo.fly(dom);
24469         if (this.currentEl) {
24470             //Roo.log(dom);
24471             //Roo.log(this.currentEl);
24472             //Roo.log(this.currentEl.contains(dom));
24473             if (this.currentEl == el) {
24474                 return;
24475             }
24476             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24477                 return;
24478             }
24479
24480         }
24481         
24482         if (this.currentTip.el) {
24483             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24484         }    
24485         //Roo.log(ev);
24486         
24487         if(!el || el.dom == document){
24488             return;
24489         }
24490         
24491         var bindEl = el;
24492         
24493         // you can not look for children, as if el is the body.. then everythign is the child..
24494         if (!el.attr('tooltip')) { //
24495             if (!el.select("[tooltip]").elements.length) {
24496                 return;
24497             }
24498             // is the mouse over this child...?
24499             bindEl = el.select("[tooltip]").first();
24500             var xy = ev.getXY();
24501             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24502                 //Roo.log("not in region.");
24503                 return;
24504             }
24505             //Roo.log("child element over..");
24506             
24507         }
24508         this.currentEl = bindEl;
24509         this.currentTip.bind(bindEl);
24510         this.currentRegion = Roo.lib.Region.getRegion(dom);
24511         this.currentTip.enter();
24512         
24513     },
24514     leave : function(ev)
24515     {
24516         var dom = ev.getTarget();
24517         //Roo.log(['leave',dom]);
24518         if (!this.currentEl) {
24519             return;
24520         }
24521         
24522         
24523         if (dom != this.currentEl.dom) {
24524             return;
24525         }
24526         var xy = ev.getXY();
24527         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24528             return;
24529         }
24530         // only activate leave if mouse cursor is outside... bounding box..
24531         
24532         
24533         
24534         
24535         if (this.currentTip) {
24536             this.currentTip.leave();
24537         }
24538         //Roo.log('clear currentEl');
24539         this.currentEl = false;
24540         
24541         
24542     },
24543     alignment : {
24544         'left' : ['r-l', [-2,0], 'right'],
24545         'right' : ['l-r', [2,0], 'left'],
24546         'bottom' : ['t-b', [0,2], 'top'],
24547         'top' : [ 'b-t', [0,-2], 'bottom']
24548     }
24549     
24550 });
24551
24552
24553 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24554     
24555     
24556     bindEl : false,
24557     
24558     delay : null, // can be { show : 300 , hide: 500}
24559     
24560     timeout : null,
24561     
24562     hoverState : null, //???
24563     
24564     placement : 'bottom', 
24565     
24566     getAutoCreate : function(){
24567     
24568         var cfg = {
24569            cls : 'tooltip',
24570            role : 'tooltip',
24571            cn : [
24572                 {
24573                     cls : 'tooltip-arrow'
24574                 },
24575                 {
24576                     cls : 'tooltip-inner'
24577                 }
24578            ]
24579         };
24580         
24581         return cfg;
24582     },
24583     bind : function(el)
24584     {
24585         this.bindEl = el;
24586     },
24587       
24588     
24589     enter : function () {
24590        
24591         if (this.timeout != null) {
24592             clearTimeout(this.timeout);
24593         }
24594         
24595         this.hoverState = 'in';
24596          //Roo.log("enter - show");
24597         if (!this.delay || !this.delay.show) {
24598             this.show();
24599             return;
24600         }
24601         var _t = this;
24602         this.timeout = setTimeout(function () {
24603             if (_t.hoverState == 'in') {
24604                 _t.show();
24605             }
24606         }, this.delay.show);
24607     },
24608     leave : function()
24609     {
24610         clearTimeout(this.timeout);
24611     
24612         this.hoverState = 'out';
24613          if (!this.delay || !this.delay.hide) {
24614             this.hide();
24615             return;
24616         }
24617        
24618         var _t = this;
24619         this.timeout = setTimeout(function () {
24620             //Roo.log("leave - timeout");
24621             
24622             if (_t.hoverState == 'out') {
24623                 _t.hide();
24624                 Roo.bootstrap.Tooltip.currentEl = false;
24625             }
24626         }, delay);
24627     },
24628     
24629     show : function ()
24630     {
24631         if (!this.el) {
24632             this.render(document.body);
24633         }
24634         // set content.
24635         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24636         
24637         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24638         
24639         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24640         
24641         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24642         
24643         var placement = typeof this.placement == 'function' ?
24644             this.placement.call(this, this.el, on_el) :
24645             this.placement;
24646             
24647         var autoToken = /\s?auto?\s?/i;
24648         var autoPlace = autoToken.test(placement);
24649         if (autoPlace) {
24650             placement = placement.replace(autoToken, '') || 'top';
24651         }
24652         
24653         //this.el.detach()
24654         //this.el.setXY([0,0]);
24655         this.el.show();
24656         //this.el.dom.style.display='block';
24657         
24658         //this.el.appendTo(on_el);
24659         
24660         var p = this.getPosition();
24661         var box = this.el.getBox();
24662         
24663         if (autoPlace) {
24664             // fixme..
24665         }
24666         
24667         var align = Roo.bootstrap.Tooltip.alignment[placement];
24668         
24669         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24670         
24671         if(placement == 'top' || placement == 'bottom'){
24672             if(xy[0] < 0){
24673                 placement = 'right';
24674             }
24675             
24676             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24677                 placement = 'left';
24678             }
24679             
24680             var scroll = Roo.select('body', true).first().getScroll();
24681             
24682             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24683                 placement = 'top';
24684             }
24685             
24686         }
24687         
24688         align = Roo.bootstrap.Tooltip.alignment[placement];
24689         
24690         this.el.alignTo(this.bindEl, align[0],align[1]);
24691         //var arrow = this.el.select('.arrow',true).first();
24692         //arrow.set(align[2], 
24693         
24694         this.el.addClass(placement);
24695         
24696         this.el.addClass('in fade');
24697         
24698         this.hoverState = null;
24699         
24700         if (this.el.hasClass('fade')) {
24701             // fade it?
24702         }
24703         
24704     },
24705     hide : function()
24706     {
24707          
24708         if (!this.el) {
24709             return;
24710         }
24711         //this.el.setXY([0,0]);
24712         this.el.removeClass('in');
24713         //this.el.hide();
24714         
24715     }
24716     
24717 });
24718  
24719
24720  /*
24721  * - LGPL
24722  *
24723  * Location Picker
24724  * 
24725  */
24726
24727 /**
24728  * @class Roo.bootstrap.LocationPicker
24729  * @extends Roo.bootstrap.Component
24730  * Bootstrap LocationPicker class
24731  * @cfg {Number} latitude Position when init default 0
24732  * @cfg {Number} longitude Position when init default 0
24733  * @cfg {Number} zoom default 15
24734  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24735  * @cfg {Boolean} mapTypeControl default false
24736  * @cfg {Boolean} disableDoubleClickZoom default false
24737  * @cfg {Boolean} scrollwheel default true
24738  * @cfg {Boolean} streetViewControl default false
24739  * @cfg {Number} radius default 0
24740  * @cfg {String} locationName
24741  * @cfg {Boolean} draggable default true
24742  * @cfg {Boolean} enableAutocomplete default false
24743  * @cfg {Boolean} enableReverseGeocode default true
24744  * @cfg {String} markerTitle
24745  * 
24746  * @constructor
24747  * Create a new LocationPicker
24748  * @param {Object} config The config object
24749  */
24750
24751
24752 Roo.bootstrap.LocationPicker = function(config){
24753     
24754     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24755     
24756     this.addEvents({
24757         /**
24758          * @event initial
24759          * Fires when the picker initialized.
24760          * @param {Roo.bootstrap.LocationPicker} this
24761          * @param {Google Location} location
24762          */
24763         initial : true,
24764         /**
24765          * @event positionchanged
24766          * Fires when the picker position changed.
24767          * @param {Roo.bootstrap.LocationPicker} this
24768          * @param {Google Location} location
24769          */
24770         positionchanged : true,
24771         /**
24772          * @event resize
24773          * Fires when the map resize.
24774          * @param {Roo.bootstrap.LocationPicker} this
24775          */
24776         resize : true,
24777         /**
24778          * @event show
24779          * Fires when the map show.
24780          * @param {Roo.bootstrap.LocationPicker} this
24781          */
24782         show : true,
24783         /**
24784          * @event hide
24785          * Fires when the map hide.
24786          * @param {Roo.bootstrap.LocationPicker} this
24787          */
24788         hide : true,
24789         /**
24790          * @event mapClick
24791          * Fires when click the map.
24792          * @param {Roo.bootstrap.LocationPicker} this
24793          * @param {Map event} e
24794          */
24795         mapClick : true,
24796         /**
24797          * @event mapRightClick
24798          * Fires when right click the map.
24799          * @param {Roo.bootstrap.LocationPicker} this
24800          * @param {Map event} e
24801          */
24802         mapRightClick : true,
24803         /**
24804          * @event markerClick
24805          * Fires when click the marker.
24806          * @param {Roo.bootstrap.LocationPicker} this
24807          * @param {Map event} e
24808          */
24809         markerClick : true,
24810         /**
24811          * @event markerRightClick
24812          * Fires when right click the marker.
24813          * @param {Roo.bootstrap.LocationPicker} this
24814          * @param {Map event} e
24815          */
24816         markerRightClick : true,
24817         /**
24818          * @event OverlayViewDraw
24819          * Fires when OverlayView Draw
24820          * @param {Roo.bootstrap.LocationPicker} this
24821          */
24822         OverlayViewDraw : true,
24823         /**
24824          * @event OverlayViewOnAdd
24825          * Fires when OverlayView Draw
24826          * @param {Roo.bootstrap.LocationPicker} this
24827          */
24828         OverlayViewOnAdd : true,
24829         /**
24830          * @event OverlayViewOnRemove
24831          * Fires when OverlayView Draw
24832          * @param {Roo.bootstrap.LocationPicker} this
24833          */
24834         OverlayViewOnRemove : true,
24835         /**
24836          * @event OverlayViewShow
24837          * Fires when OverlayView Draw
24838          * @param {Roo.bootstrap.LocationPicker} this
24839          * @param {Pixel} cpx
24840          */
24841         OverlayViewShow : true,
24842         /**
24843          * @event OverlayViewHide
24844          * Fires when OverlayView Draw
24845          * @param {Roo.bootstrap.LocationPicker} this
24846          */
24847         OverlayViewHide : true,
24848         /**
24849          * @event loadexception
24850          * Fires when load google lib failed.
24851          * @param {Roo.bootstrap.LocationPicker} this
24852          */
24853         loadexception : true
24854     });
24855         
24856 };
24857
24858 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24859     
24860     gMapContext: false,
24861     
24862     latitude: 0,
24863     longitude: 0,
24864     zoom: 15,
24865     mapTypeId: false,
24866     mapTypeControl: false,
24867     disableDoubleClickZoom: false,
24868     scrollwheel: true,
24869     streetViewControl: false,
24870     radius: 0,
24871     locationName: '',
24872     draggable: true,
24873     enableAutocomplete: false,
24874     enableReverseGeocode: true,
24875     markerTitle: '',
24876     
24877     getAutoCreate: function()
24878     {
24879
24880         var cfg = {
24881             tag: 'div',
24882             cls: 'roo-location-picker'
24883         };
24884         
24885         return cfg
24886     },
24887     
24888     initEvents: function(ct, position)
24889     {       
24890         if(!this.el.getWidth() || this.isApplied()){
24891             return;
24892         }
24893         
24894         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24895         
24896         this.initial();
24897     },
24898     
24899     initial: function()
24900     {
24901         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24902             this.fireEvent('loadexception', this);
24903             return;
24904         }
24905         
24906         if(!this.mapTypeId){
24907             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24908         }
24909         
24910         this.gMapContext = this.GMapContext();
24911         
24912         this.initOverlayView();
24913         
24914         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24915         
24916         var _this = this;
24917                 
24918         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24919             _this.setPosition(_this.gMapContext.marker.position);
24920         });
24921         
24922         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24923             _this.fireEvent('mapClick', this, event);
24924             
24925         });
24926
24927         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24928             _this.fireEvent('mapRightClick', this, event);
24929             
24930         });
24931         
24932         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24933             _this.fireEvent('markerClick', this, event);
24934             
24935         });
24936
24937         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24938             _this.fireEvent('markerRightClick', this, event);
24939             
24940         });
24941         
24942         this.setPosition(this.gMapContext.location);
24943         
24944         this.fireEvent('initial', this, this.gMapContext.location);
24945     },
24946     
24947     initOverlayView: function()
24948     {
24949         var _this = this;
24950         
24951         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24952             
24953             draw: function()
24954             {
24955                 _this.fireEvent('OverlayViewDraw', _this);
24956             },
24957             
24958             onAdd: function()
24959             {
24960                 _this.fireEvent('OverlayViewOnAdd', _this);
24961             },
24962             
24963             onRemove: function()
24964             {
24965                 _this.fireEvent('OverlayViewOnRemove', _this);
24966             },
24967             
24968             show: function(cpx)
24969             {
24970                 _this.fireEvent('OverlayViewShow', _this, cpx);
24971             },
24972             
24973             hide: function()
24974             {
24975                 _this.fireEvent('OverlayViewHide', _this);
24976             }
24977             
24978         });
24979     },
24980     
24981     fromLatLngToContainerPixel: function(event)
24982     {
24983         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24984     },
24985     
24986     isApplied: function() 
24987     {
24988         return this.getGmapContext() == false ? false : true;
24989     },
24990     
24991     getGmapContext: function() 
24992     {
24993         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24994     },
24995     
24996     GMapContext: function() 
24997     {
24998         var position = new google.maps.LatLng(this.latitude, this.longitude);
24999         
25000         var _map = new google.maps.Map(this.el.dom, {
25001             center: position,
25002             zoom: this.zoom,
25003             mapTypeId: this.mapTypeId,
25004             mapTypeControl: this.mapTypeControl,
25005             disableDoubleClickZoom: this.disableDoubleClickZoom,
25006             scrollwheel: this.scrollwheel,
25007             streetViewControl: this.streetViewControl,
25008             locationName: this.locationName,
25009             draggable: this.draggable,
25010             enableAutocomplete: this.enableAutocomplete,
25011             enableReverseGeocode: this.enableReverseGeocode
25012         });
25013         
25014         var _marker = new google.maps.Marker({
25015             position: position,
25016             map: _map,
25017             title: this.markerTitle,
25018             draggable: this.draggable
25019         });
25020         
25021         return {
25022             map: _map,
25023             marker: _marker,
25024             circle: null,
25025             location: position,
25026             radius: this.radius,
25027             locationName: this.locationName,
25028             addressComponents: {
25029                 formatted_address: null,
25030                 addressLine1: null,
25031                 addressLine2: null,
25032                 streetName: null,
25033                 streetNumber: null,
25034                 city: null,
25035                 district: null,
25036                 state: null,
25037                 stateOrProvince: null
25038             },
25039             settings: this,
25040             domContainer: this.el.dom,
25041             geodecoder: new google.maps.Geocoder()
25042         };
25043     },
25044     
25045     drawCircle: function(center, radius, options) 
25046     {
25047         if (this.gMapContext.circle != null) {
25048             this.gMapContext.circle.setMap(null);
25049         }
25050         if (radius > 0) {
25051             radius *= 1;
25052             options = Roo.apply({}, options, {
25053                 strokeColor: "#0000FF",
25054                 strokeOpacity: .35,
25055                 strokeWeight: 2,
25056                 fillColor: "#0000FF",
25057                 fillOpacity: .2
25058             });
25059             
25060             options.map = this.gMapContext.map;
25061             options.radius = radius;
25062             options.center = center;
25063             this.gMapContext.circle = new google.maps.Circle(options);
25064             return this.gMapContext.circle;
25065         }
25066         
25067         return null;
25068     },
25069     
25070     setPosition: function(location) 
25071     {
25072         this.gMapContext.location = location;
25073         this.gMapContext.marker.setPosition(location);
25074         this.gMapContext.map.panTo(location);
25075         this.drawCircle(location, this.gMapContext.radius, {});
25076         
25077         var _this = this;
25078         
25079         if (this.gMapContext.settings.enableReverseGeocode) {
25080             this.gMapContext.geodecoder.geocode({
25081                 latLng: this.gMapContext.location
25082             }, function(results, status) {
25083                 
25084                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25085                     _this.gMapContext.locationName = results[0].formatted_address;
25086                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25087                     
25088                     _this.fireEvent('positionchanged', this, location);
25089                 }
25090             });
25091             
25092             return;
25093         }
25094         
25095         this.fireEvent('positionchanged', this, location);
25096     },
25097     
25098     resize: function()
25099     {
25100         google.maps.event.trigger(this.gMapContext.map, "resize");
25101         
25102         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25103         
25104         this.fireEvent('resize', this);
25105     },
25106     
25107     setPositionByLatLng: function(latitude, longitude)
25108     {
25109         this.setPosition(new google.maps.LatLng(latitude, longitude));
25110     },
25111     
25112     getCurrentPosition: function() 
25113     {
25114         return {
25115             latitude: this.gMapContext.location.lat(),
25116             longitude: this.gMapContext.location.lng()
25117         };
25118     },
25119     
25120     getAddressName: function() 
25121     {
25122         return this.gMapContext.locationName;
25123     },
25124     
25125     getAddressComponents: function() 
25126     {
25127         return this.gMapContext.addressComponents;
25128     },
25129     
25130     address_component_from_google_geocode: function(address_components) 
25131     {
25132         var result = {};
25133         
25134         for (var i = 0; i < address_components.length; i++) {
25135             var component = address_components[i];
25136             if (component.types.indexOf("postal_code") >= 0) {
25137                 result.postalCode = component.short_name;
25138             } else if (component.types.indexOf("street_number") >= 0) {
25139                 result.streetNumber = component.short_name;
25140             } else if (component.types.indexOf("route") >= 0) {
25141                 result.streetName = component.short_name;
25142             } else if (component.types.indexOf("neighborhood") >= 0) {
25143                 result.city = component.short_name;
25144             } else if (component.types.indexOf("locality") >= 0) {
25145                 result.city = component.short_name;
25146             } else if (component.types.indexOf("sublocality") >= 0) {
25147                 result.district = component.short_name;
25148             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25149                 result.stateOrProvince = component.short_name;
25150             } else if (component.types.indexOf("country") >= 0) {
25151                 result.country = component.short_name;
25152             }
25153         }
25154         
25155         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25156         result.addressLine2 = "";
25157         return result;
25158     },
25159     
25160     setZoomLevel: function(zoom)
25161     {
25162         this.gMapContext.map.setZoom(zoom);
25163     },
25164     
25165     show: function()
25166     {
25167         if(!this.el){
25168             return;
25169         }
25170         
25171         this.el.show();
25172         
25173         this.resize();
25174         
25175         this.fireEvent('show', this);
25176     },
25177     
25178     hide: function()
25179     {
25180         if(!this.el){
25181             return;
25182         }
25183         
25184         this.el.hide();
25185         
25186         this.fireEvent('hide', this);
25187     }
25188     
25189 });
25190
25191 Roo.apply(Roo.bootstrap.LocationPicker, {
25192     
25193     OverlayView : function(map, options)
25194     {
25195         options = options || {};
25196         
25197         this.setMap(map);
25198     }
25199     
25200     
25201 });/*
25202  * - LGPL
25203  *
25204  * Alert
25205  * 
25206  */
25207
25208 /**
25209  * @class Roo.bootstrap.Alert
25210  * @extends Roo.bootstrap.Component
25211  * Bootstrap Alert class
25212  * @cfg {String} title The title of alert
25213  * @cfg {String} html The content of alert
25214  * @cfg {String} weight (  success | info | warning | danger )
25215  * @cfg {String} faicon font-awesomeicon
25216  * 
25217  * @constructor
25218  * Create a new alert
25219  * @param {Object} config The config object
25220  */
25221
25222
25223 Roo.bootstrap.Alert = function(config){
25224     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25225     
25226 };
25227
25228 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25229     
25230     title: '',
25231     html: '',
25232     weight: false,
25233     faicon: false,
25234     
25235     getAutoCreate : function()
25236     {
25237         
25238         var cfg = {
25239             tag : 'div',
25240             cls : 'alert',
25241             cn : [
25242                 {
25243                     tag : 'i',
25244                     cls : 'roo-alert-icon'
25245                     
25246                 },
25247                 {
25248                     tag : 'b',
25249                     cls : 'roo-alert-title',
25250                     html : this.title
25251                 },
25252                 {
25253                     tag : 'span',
25254                     cls : 'roo-alert-text',
25255                     html : this.html
25256                 }
25257             ]
25258         };
25259         
25260         if(this.faicon){
25261             cfg.cn[0].cls += ' fa ' + this.faicon;
25262         }
25263         
25264         if(this.weight){
25265             cfg.cls += ' alert-' + this.weight;
25266         }
25267         
25268         return cfg;
25269     },
25270     
25271     initEvents: function() 
25272     {
25273         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25274     },
25275     
25276     setTitle : function(str)
25277     {
25278         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25279     },
25280     
25281     setText : function(str)
25282     {
25283         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25284     },
25285     
25286     setWeight : function(weight)
25287     {
25288         if(this.weight){
25289             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25290         }
25291         
25292         this.weight = weight;
25293         
25294         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25295     },
25296     
25297     setIcon : function(icon)
25298     {
25299         if(this.faicon){
25300             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25301         }
25302         
25303         this.faicon = icon;
25304         
25305         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25306     },
25307     
25308     hide: function() 
25309     {
25310         this.el.hide();   
25311     },
25312     
25313     show: function() 
25314     {  
25315         this.el.show();   
25316     }
25317     
25318 });
25319
25320  
25321 /*
25322 * Licence: LGPL
25323 */
25324
25325 /**
25326  * @class Roo.bootstrap.UploadCropbox
25327  * @extends Roo.bootstrap.Component
25328  * Bootstrap UploadCropbox class
25329  * @cfg {String} emptyText show when image has been loaded
25330  * @cfg {String} rotateNotify show when image too small to rotate
25331  * @cfg {Number} errorTimeout default 3000
25332  * @cfg {Number} minWidth default 300
25333  * @cfg {Number} minHeight default 300
25334  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25335  * @cfg {Boolean} isDocument (true|false) default false
25336  * @cfg {String} url action url
25337  * @cfg {String} paramName default 'imageUpload'
25338  * @cfg {String} method default POST
25339  * @cfg {Boolean} loadMask (true|false) default true
25340  * @cfg {Boolean} loadingText default 'Loading...'
25341  * 
25342  * @constructor
25343  * Create a new UploadCropbox
25344  * @param {Object} config The config object
25345  */
25346
25347 Roo.bootstrap.UploadCropbox = function(config){
25348     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25349     
25350     this.addEvents({
25351         /**
25352          * @event beforeselectfile
25353          * Fire before select file
25354          * @param {Roo.bootstrap.UploadCropbox} this
25355          */
25356         "beforeselectfile" : true,
25357         /**
25358          * @event initial
25359          * Fire after initEvent
25360          * @param {Roo.bootstrap.UploadCropbox} this
25361          */
25362         "initial" : true,
25363         /**
25364          * @event crop
25365          * Fire after initEvent
25366          * @param {Roo.bootstrap.UploadCropbox} this
25367          * @param {String} data
25368          */
25369         "crop" : true,
25370         /**
25371          * @event prepare
25372          * Fire when preparing the file data
25373          * @param {Roo.bootstrap.UploadCropbox} this
25374          * @param {Object} file
25375          */
25376         "prepare" : true,
25377         /**
25378          * @event exception
25379          * Fire when get exception
25380          * @param {Roo.bootstrap.UploadCropbox} this
25381          * @param {XMLHttpRequest} xhr
25382          */
25383         "exception" : true,
25384         /**
25385          * @event beforeloadcanvas
25386          * Fire before load the canvas
25387          * @param {Roo.bootstrap.UploadCropbox} this
25388          * @param {String} src
25389          */
25390         "beforeloadcanvas" : true,
25391         /**
25392          * @event trash
25393          * Fire when trash image
25394          * @param {Roo.bootstrap.UploadCropbox} this
25395          */
25396         "trash" : true,
25397         /**
25398          * @event download
25399          * Fire when download the image
25400          * @param {Roo.bootstrap.UploadCropbox} this
25401          */
25402         "download" : true,
25403         /**
25404          * @event footerbuttonclick
25405          * Fire when footerbuttonclick
25406          * @param {Roo.bootstrap.UploadCropbox} this
25407          * @param {String} type
25408          */
25409         "footerbuttonclick" : true,
25410         /**
25411          * @event resize
25412          * Fire when resize
25413          * @param {Roo.bootstrap.UploadCropbox} this
25414          */
25415         "resize" : true,
25416         /**
25417          * @event rotate
25418          * Fire when rotate the image
25419          * @param {Roo.bootstrap.UploadCropbox} this
25420          * @param {String} pos
25421          */
25422         "rotate" : true,
25423         /**
25424          * @event inspect
25425          * Fire when inspect the file
25426          * @param {Roo.bootstrap.UploadCropbox} this
25427          * @param {Object} file
25428          */
25429         "inspect" : true,
25430         /**
25431          * @event upload
25432          * Fire when xhr upload the file
25433          * @param {Roo.bootstrap.UploadCropbox} this
25434          * @param {Object} data
25435          */
25436         "upload" : true,
25437         /**
25438          * @event arrange
25439          * Fire when arrange the file data
25440          * @param {Roo.bootstrap.UploadCropbox} this
25441          * @param {Object} formData
25442          */
25443         "arrange" : true
25444     });
25445     
25446     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25447 };
25448
25449 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25450     
25451     emptyText : 'Click to upload image',
25452     rotateNotify : 'Image is too small to rotate',
25453     errorTimeout : 3000,
25454     scale : 0,
25455     baseScale : 1,
25456     rotate : 0,
25457     dragable : false,
25458     pinching : false,
25459     mouseX : 0,
25460     mouseY : 0,
25461     cropData : false,
25462     minWidth : 300,
25463     minHeight : 300,
25464     file : false,
25465     exif : {},
25466     baseRotate : 1,
25467     cropType : 'image/jpeg',
25468     buttons : false,
25469     canvasLoaded : false,
25470     isDocument : false,
25471     method : 'POST',
25472     paramName : 'imageUpload',
25473     loadMask : true,
25474     loadingText : 'Loading...',
25475     maskEl : false,
25476     
25477     getAutoCreate : function()
25478     {
25479         var cfg = {
25480             tag : 'div',
25481             cls : 'roo-upload-cropbox',
25482             cn : [
25483                 {
25484                     tag : 'input',
25485                     cls : 'roo-upload-cropbox-selector',
25486                     type : 'file'
25487                 },
25488                 {
25489                     tag : 'div',
25490                     cls : 'roo-upload-cropbox-body',
25491                     style : 'cursor:pointer',
25492                     cn : [
25493                         {
25494                             tag : 'div',
25495                             cls : 'roo-upload-cropbox-preview'
25496                         },
25497                         {
25498                             tag : 'div',
25499                             cls : 'roo-upload-cropbox-thumb'
25500                         },
25501                         {
25502                             tag : 'div',
25503                             cls : 'roo-upload-cropbox-empty-notify',
25504                             html : this.emptyText
25505                         },
25506                         {
25507                             tag : 'div',
25508                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25509                             html : this.rotateNotify
25510                         }
25511                     ]
25512                 },
25513                 {
25514                     tag : 'div',
25515                     cls : 'roo-upload-cropbox-footer',
25516                     cn : {
25517                         tag : 'div',
25518                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25519                         cn : []
25520                     }
25521                 }
25522             ]
25523         };
25524         
25525         return cfg;
25526     },
25527     
25528     onRender : function(ct, position)
25529     {
25530         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25531         
25532         if (this.buttons.length) {
25533             
25534             Roo.each(this.buttons, function(bb) {
25535                 
25536                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25537                 
25538                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25539                 
25540             }, this);
25541         }
25542         
25543         if(this.loadMask){
25544             this.maskEl = this.el;
25545         }
25546     },
25547     
25548     initEvents : function()
25549     {
25550         this.urlAPI = (window.createObjectURL && window) || 
25551                                 (window.URL && URL.revokeObjectURL && URL) || 
25552                                 (window.webkitURL && webkitURL);
25553                         
25554         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25555         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25556         
25557         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25558         this.selectorEl.hide();
25559         
25560         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25561         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25562         
25563         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25564         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25565         this.thumbEl.hide();
25566         
25567         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25568         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25569         
25570         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25571         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25572         this.errorEl.hide();
25573         
25574         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25575         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25576         this.footerEl.hide();
25577         
25578         this.setThumbBoxSize();
25579         
25580         this.bind();
25581         
25582         this.resize();
25583         
25584         this.fireEvent('initial', this);
25585     },
25586
25587     bind : function()
25588     {
25589         var _this = this;
25590         
25591         window.addEventListener("resize", function() { _this.resize(); } );
25592         
25593         this.bodyEl.on('click', this.beforeSelectFile, this);
25594         
25595         if(Roo.isTouch){
25596             this.bodyEl.on('touchstart', this.onTouchStart, this);
25597             this.bodyEl.on('touchmove', this.onTouchMove, this);
25598             this.bodyEl.on('touchend', this.onTouchEnd, this);
25599         }
25600         
25601         if(!Roo.isTouch){
25602             this.bodyEl.on('mousedown', this.onMouseDown, this);
25603             this.bodyEl.on('mousemove', this.onMouseMove, this);
25604             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25605             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25606             Roo.get(document).on('mouseup', this.onMouseUp, this);
25607         }
25608         
25609         this.selectorEl.on('change', this.onFileSelected, this);
25610     },
25611     
25612     reset : function()
25613     {    
25614         this.scale = 0;
25615         this.baseScale = 1;
25616         this.rotate = 0;
25617         this.baseRotate = 1;
25618         this.dragable = false;
25619         this.pinching = false;
25620         this.mouseX = 0;
25621         this.mouseY = 0;
25622         this.cropData = false;
25623         this.notifyEl.dom.innerHTML = this.emptyText;
25624         
25625         this.selectorEl.dom.value = '';
25626         
25627     },
25628     
25629     resize : function()
25630     {
25631         if(this.fireEvent('resize', this) != false){
25632             this.setThumbBoxPosition();
25633             this.setCanvasPosition();
25634         }
25635     },
25636     
25637     onFooterButtonClick : function(e, el, o, type)
25638     {
25639         switch (type) {
25640             case 'rotate-left' :
25641                 this.onRotateLeft(e);
25642                 break;
25643             case 'rotate-right' :
25644                 this.onRotateRight(e);
25645                 break;
25646             case 'picture' :
25647                 this.beforeSelectFile(e);
25648                 break;
25649             case 'trash' :
25650                 this.trash(e);
25651                 break;
25652             case 'crop' :
25653                 this.crop(e);
25654                 break;
25655             case 'download' :
25656                 this.download(e);
25657                 break;
25658             default :
25659                 break;
25660         }
25661         
25662         this.fireEvent('footerbuttonclick', this, type);
25663     },
25664     
25665     beforeSelectFile : function(e)
25666     {
25667         e.preventDefault();
25668         
25669         if(this.fireEvent('beforeselectfile', this) != false){
25670             this.selectorEl.dom.click();
25671         }
25672     },
25673     
25674     onFileSelected : function(e)
25675     {
25676         e.preventDefault();
25677         
25678         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25679             return;
25680         }
25681         
25682         var file = this.selectorEl.dom.files[0];
25683         
25684         if(this.fireEvent('inspect', this, file) != false){
25685             this.prepare(file);
25686         }
25687         
25688     },
25689     
25690     trash : function(e)
25691     {
25692         this.fireEvent('trash', this);
25693     },
25694     
25695     download : function(e)
25696     {
25697         this.fireEvent('download', this);
25698     },
25699     
25700     loadCanvas : function(src)
25701     {   
25702         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25703             
25704             this.reset();
25705             
25706             this.imageEl = document.createElement('img');
25707             
25708             var _this = this;
25709             
25710             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25711             
25712             this.imageEl.src = src;
25713         }
25714     },
25715     
25716     onLoadCanvas : function()
25717     {   
25718         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25719         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25720         
25721         this.bodyEl.un('click', this.beforeSelectFile, this);
25722         
25723         this.notifyEl.hide();
25724         this.thumbEl.show();
25725         this.footerEl.show();
25726         
25727         this.baseRotateLevel();
25728         
25729         if(this.isDocument){
25730             this.setThumbBoxSize();
25731         }
25732         
25733         this.setThumbBoxPosition();
25734         
25735         this.baseScaleLevel();
25736         
25737         this.draw();
25738         
25739         this.resize();
25740         
25741         this.canvasLoaded = true;
25742         
25743         if(this.loadMask){
25744             this.maskEl.unmask();
25745         }
25746         
25747     },
25748     
25749     setCanvasPosition : function()
25750     {   
25751         if(!this.canvasEl){
25752             return;
25753         }
25754         
25755         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25756         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25757         
25758         this.previewEl.setLeft(pw);
25759         this.previewEl.setTop(ph);
25760         
25761     },
25762     
25763     onMouseDown : function(e)
25764     {   
25765         e.stopEvent();
25766         
25767         this.dragable = true;
25768         this.pinching = false;
25769         
25770         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25771             this.dragable = false;
25772             return;
25773         }
25774         
25775         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25776         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25777         
25778     },
25779     
25780     onMouseMove : function(e)
25781     {   
25782         e.stopEvent();
25783         
25784         if(!this.canvasLoaded){
25785             return;
25786         }
25787         
25788         if (!this.dragable){
25789             return;
25790         }
25791         
25792         var minX = Math.ceil(this.thumbEl.getLeft(true));
25793         var minY = Math.ceil(this.thumbEl.getTop(true));
25794         
25795         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25796         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25797         
25798         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25799         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25800         
25801         x = x - this.mouseX;
25802         y = y - this.mouseY;
25803         
25804         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25805         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25806         
25807         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25808         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25809         
25810         this.previewEl.setLeft(bgX);
25811         this.previewEl.setTop(bgY);
25812         
25813         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25814         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25815     },
25816     
25817     onMouseUp : function(e)
25818     {   
25819         e.stopEvent();
25820         
25821         this.dragable = false;
25822     },
25823     
25824     onMouseWheel : function(e)
25825     {   
25826         e.stopEvent();
25827         
25828         this.startScale = this.scale;
25829         
25830         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25831         
25832         if(!this.zoomable()){
25833             this.scale = this.startScale;
25834             return;
25835         }
25836         
25837         this.draw();
25838         
25839         return;
25840     },
25841     
25842     zoomable : function()
25843     {
25844         var minScale = this.thumbEl.getWidth() / this.minWidth;
25845         
25846         if(this.minWidth < this.minHeight){
25847             minScale = this.thumbEl.getHeight() / this.minHeight;
25848         }
25849         
25850         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25851         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25852         
25853         if(
25854                 this.isDocument &&
25855                 (this.rotate == 0 || this.rotate == 180) && 
25856                 (
25857                     width > this.imageEl.OriginWidth || 
25858                     height > this.imageEl.OriginHeight ||
25859                     (width < this.minWidth && height < this.minHeight)
25860                 )
25861         ){
25862             return false;
25863         }
25864         
25865         if(
25866                 this.isDocument &&
25867                 (this.rotate == 90 || this.rotate == 270) && 
25868                 (
25869                     width > this.imageEl.OriginWidth || 
25870                     height > this.imageEl.OriginHeight ||
25871                     (width < this.minHeight && height < this.minWidth)
25872                 )
25873         ){
25874             return false;
25875         }
25876         
25877         if(
25878                 !this.isDocument &&
25879                 (this.rotate == 0 || this.rotate == 180) && 
25880                 (
25881                     width < this.minWidth || 
25882                     width > this.imageEl.OriginWidth || 
25883                     height < this.minHeight || 
25884                     height > this.imageEl.OriginHeight
25885                 )
25886         ){
25887             return false;
25888         }
25889         
25890         if(
25891                 !this.isDocument &&
25892                 (this.rotate == 90 || this.rotate == 270) && 
25893                 (
25894                     width < this.minHeight || 
25895                     width > this.imageEl.OriginWidth || 
25896                     height < this.minWidth || 
25897                     height > this.imageEl.OriginHeight
25898                 )
25899         ){
25900             return false;
25901         }
25902         
25903         return true;
25904         
25905     },
25906     
25907     onRotateLeft : function(e)
25908     {   
25909         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25910             
25911             var minScale = this.thumbEl.getWidth() / this.minWidth;
25912             
25913             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25914             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25915             
25916             this.startScale = this.scale;
25917             
25918             while (this.getScaleLevel() < minScale){
25919             
25920                 this.scale = this.scale + 1;
25921                 
25922                 if(!this.zoomable()){
25923                     break;
25924                 }
25925                 
25926                 if(
25927                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25928                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25929                 ){
25930                     continue;
25931                 }
25932                 
25933                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25934
25935                 this.draw();
25936                 
25937                 return;
25938             }
25939             
25940             this.scale = this.startScale;
25941             
25942             this.onRotateFail();
25943             
25944             return false;
25945         }
25946         
25947         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25948
25949         if(this.isDocument){
25950             this.setThumbBoxSize();
25951             this.setThumbBoxPosition();
25952             this.setCanvasPosition();
25953         }
25954         
25955         this.draw();
25956         
25957         this.fireEvent('rotate', this, 'left');
25958         
25959     },
25960     
25961     onRotateRight : function(e)
25962     {
25963         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25964             
25965             var minScale = this.thumbEl.getWidth() / this.minWidth;
25966         
25967             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25968             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25969             
25970             this.startScale = this.scale;
25971             
25972             while (this.getScaleLevel() < minScale){
25973             
25974                 this.scale = this.scale + 1;
25975                 
25976                 if(!this.zoomable()){
25977                     break;
25978                 }
25979                 
25980                 if(
25981                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25982                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25983                 ){
25984                     continue;
25985                 }
25986                 
25987                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25988
25989                 this.draw();
25990                 
25991                 return;
25992             }
25993             
25994             this.scale = this.startScale;
25995             
25996             this.onRotateFail();
25997             
25998             return false;
25999         }
26000         
26001         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26002
26003         if(this.isDocument){
26004             this.setThumbBoxSize();
26005             this.setThumbBoxPosition();
26006             this.setCanvasPosition();
26007         }
26008         
26009         this.draw();
26010         
26011         this.fireEvent('rotate', this, 'right');
26012     },
26013     
26014     onRotateFail : function()
26015     {
26016         this.errorEl.show(true);
26017         
26018         var _this = this;
26019         
26020         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26021     },
26022     
26023     draw : function()
26024     {
26025         this.previewEl.dom.innerHTML = '';
26026         
26027         var canvasEl = document.createElement("canvas");
26028         
26029         var contextEl = canvasEl.getContext("2d");
26030         
26031         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26032         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26033         var center = this.imageEl.OriginWidth / 2;
26034         
26035         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26036             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26037             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26038             center = this.imageEl.OriginHeight / 2;
26039         }
26040         
26041         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26042         
26043         contextEl.translate(center, center);
26044         contextEl.rotate(this.rotate * Math.PI / 180);
26045
26046         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26047         
26048         this.canvasEl = document.createElement("canvas");
26049         
26050         this.contextEl = this.canvasEl.getContext("2d");
26051         
26052         switch (this.rotate) {
26053             case 0 :
26054                 
26055                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26056                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26057                 
26058                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26059                 
26060                 break;
26061             case 90 : 
26062                 
26063                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26064                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26065                 
26066                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26067                     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);
26068                     break;
26069                 }
26070                 
26071                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26072                 
26073                 break;
26074             case 180 :
26075                 
26076                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26077                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26078                 
26079                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26080                     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);
26081                     break;
26082                 }
26083                 
26084                 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);
26085                 
26086                 break;
26087             case 270 :
26088                 
26089                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26090                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26091         
26092                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26093                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26094                     break;
26095                 }
26096                 
26097                 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);
26098                 
26099                 break;
26100             default : 
26101                 break;
26102         }
26103         
26104         this.previewEl.appendChild(this.canvasEl);
26105         
26106         this.setCanvasPosition();
26107     },
26108     
26109     crop : function()
26110     {
26111         if(!this.canvasLoaded){
26112             return;
26113         }
26114         
26115         var imageCanvas = document.createElement("canvas");
26116         
26117         var imageContext = imageCanvas.getContext("2d");
26118         
26119         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26120         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26121         
26122         var center = imageCanvas.width / 2;
26123         
26124         imageContext.translate(center, center);
26125         
26126         imageContext.rotate(this.rotate * Math.PI / 180);
26127         
26128         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26129         
26130         var canvas = document.createElement("canvas");
26131         
26132         var context = canvas.getContext("2d");
26133                 
26134         canvas.width = this.minWidth;
26135         canvas.height = this.minHeight;
26136
26137         switch (this.rotate) {
26138             case 0 :
26139                 
26140                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26141                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26142                 
26143                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26144                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26145                 
26146                 var targetWidth = this.minWidth - 2 * x;
26147                 var targetHeight = this.minHeight - 2 * y;
26148                 
26149                 var scale = 1;
26150                 
26151                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26152                     scale = targetWidth / width;
26153                 }
26154                 
26155                 if(x > 0 && y == 0){
26156                     scale = targetHeight / height;
26157                 }
26158                 
26159                 if(x > 0 && y > 0){
26160                     scale = targetWidth / width;
26161                     
26162                     if(width < height){
26163                         scale = targetHeight / height;
26164                     }
26165                 }
26166                 
26167                 context.scale(scale, scale);
26168                 
26169                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26170                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26171
26172                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26173                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26174
26175                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26176                 
26177                 break;
26178             case 90 : 
26179                 
26180                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26181                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26182                 
26183                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26184                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26185                 
26186                 var targetWidth = this.minWidth - 2 * x;
26187                 var targetHeight = this.minHeight - 2 * y;
26188                 
26189                 var scale = 1;
26190                 
26191                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26192                     scale = targetWidth / width;
26193                 }
26194                 
26195                 if(x > 0 && y == 0){
26196                     scale = targetHeight / height;
26197                 }
26198                 
26199                 if(x > 0 && y > 0){
26200                     scale = targetWidth / width;
26201                     
26202                     if(width < height){
26203                         scale = targetHeight / height;
26204                     }
26205                 }
26206                 
26207                 context.scale(scale, scale);
26208                 
26209                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26210                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26211
26212                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26213                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26214                 
26215                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26216                 
26217                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26218                 
26219                 break;
26220             case 180 :
26221                 
26222                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26223                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26224                 
26225                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26226                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26227                 
26228                 var targetWidth = this.minWidth - 2 * x;
26229                 var targetHeight = this.minHeight - 2 * y;
26230                 
26231                 var scale = 1;
26232                 
26233                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26234                     scale = targetWidth / width;
26235                 }
26236                 
26237                 if(x > 0 && y == 0){
26238                     scale = targetHeight / height;
26239                 }
26240                 
26241                 if(x > 0 && y > 0){
26242                     scale = targetWidth / width;
26243                     
26244                     if(width < height){
26245                         scale = targetHeight / height;
26246                     }
26247                 }
26248                 
26249                 context.scale(scale, scale);
26250                 
26251                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26252                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26253
26254                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26255                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26256
26257                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26258                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26259                 
26260                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26261                 
26262                 break;
26263             case 270 :
26264                 
26265                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26266                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26267                 
26268                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26269                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26270                 
26271                 var targetWidth = this.minWidth - 2 * x;
26272                 var targetHeight = this.minHeight - 2 * y;
26273                 
26274                 var scale = 1;
26275                 
26276                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26277                     scale = targetWidth / width;
26278                 }
26279                 
26280                 if(x > 0 && y == 0){
26281                     scale = targetHeight / height;
26282                 }
26283                 
26284                 if(x > 0 && y > 0){
26285                     scale = targetWidth / width;
26286                     
26287                     if(width < height){
26288                         scale = targetHeight / height;
26289                     }
26290                 }
26291                 
26292                 context.scale(scale, scale);
26293                 
26294                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26295                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26296
26297                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26298                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26299                 
26300                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26301                 
26302                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26303                 
26304                 break;
26305             default : 
26306                 break;
26307         }
26308         
26309         this.cropData = canvas.toDataURL(this.cropType);
26310         
26311         if(this.fireEvent('crop', this, this.cropData) !== false){
26312             this.process(this.file, this.cropData);
26313         }
26314         
26315         return;
26316         
26317     },
26318     
26319     setThumbBoxSize : function()
26320     {
26321         var width, height;
26322         
26323         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26324             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26325             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26326             
26327             this.minWidth = width;
26328             this.minHeight = height;
26329             
26330             if(this.rotate == 90 || this.rotate == 270){
26331                 this.minWidth = height;
26332                 this.minHeight = width;
26333             }
26334         }
26335         
26336         height = 300;
26337         width = Math.ceil(this.minWidth * height / this.minHeight);
26338         
26339         if(this.minWidth > this.minHeight){
26340             width = 300;
26341             height = Math.ceil(this.minHeight * width / this.minWidth);
26342         }
26343         
26344         this.thumbEl.setStyle({
26345             width : width + 'px',
26346             height : height + 'px'
26347         });
26348
26349         return;
26350             
26351     },
26352     
26353     setThumbBoxPosition : function()
26354     {
26355         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26356         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26357         
26358         this.thumbEl.setLeft(x);
26359         this.thumbEl.setTop(y);
26360         
26361     },
26362     
26363     baseRotateLevel : function()
26364     {
26365         this.baseRotate = 1;
26366         
26367         if(
26368                 typeof(this.exif) != 'undefined' &&
26369                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26370                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26371         ){
26372             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26373         }
26374         
26375         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26376         
26377     },
26378     
26379     baseScaleLevel : function()
26380     {
26381         var width, height;
26382         
26383         if(this.isDocument){
26384             
26385             if(this.baseRotate == 6 || this.baseRotate == 8){
26386             
26387                 height = this.thumbEl.getHeight();
26388                 this.baseScale = height / this.imageEl.OriginWidth;
26389
26390                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26391                     width = this.thumbEl.getWidth();
26392                     this.baseScale = width / this.imageEl.OriginHeight;
26393                 }
26394
26395                 return;
26396             }
26397
26398             height = this.thumbEl.getHeight();
26399             this.baseScale = height / this.imageEl.OriginHeight;
26400
26401             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26402                 width = this.thumbEl.getWidth();
26403                 this.baseScale = width / this.imageEl.OriginWidth;
26404             }
26405
26406             return;
26407         }
26408         
26409         if(this.baseRotate == 6 || this.baseRotate == 8){
26410             
26411             width = this.thumbEl.getHeight();
26412             this.baseScale = width / this.imageEl.OriginHeight;
26413             
26414             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26415                 height = this.thumbEl.getWidth();
26416                 this.baseScale = height / this.imageEl.OriginHeight;
26417             }
26418             
26419             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26420                 height = this.thumbEl.getWidth();
26421                 this.baseScale = height / this.imageEl.OriginHeight;
26422                 
26423                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26424                     width = this.thumbEl.getHeight();
26425                     this.baseScale = width / this.imageEl.OriginWidth;
26426                 }
26427             }
26428             
26429             return;
26430         }
26431         
26432         width = this.thumbEl.getWidth();
26433         this.baseScale = width / this.imageEl.OriginWidth;
26434         
26435         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26436             height = this.thumbEl.getHeight();
26437             this.baseScale = height / this.imageEl.OriginHeight;
26438         }
26439         
26440         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26441             
26442             height = this.thumbEl.getHeight();
26443             this.baseScale = height / this.imageEl.OriginHeight;
26444             
26445             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26446                 width = this.thumbEl.getWidth();
26447                 this.baseScale = width / this.imageEl.OriginWidth;
26448             }
26449             
26450         }
26451         
26452         return;
26453     },
26454     
26455     getScaleLevel : function()
26456     {
26457         return this.baseScale * Math.pow(1.1, this.scale);
26458     },
26459     
26460     onTouchStart : function(e)
26461     {
26462         if(!this.canvasLoaded){
26463             this.beforeSelectFile(e);
26464             return;
26465         }
26466         
26467         var touches = e.browserEvent.touches;
26468         
26469         if(!touches){
26470             return;
26471         }
26472         
26473         if(touches.length == 1){
26474             this.onMouseDown(e);
26475             return;
26476         }
26477         
26478         if(touches.length != 2){
26479             return;
26480         }
26481         
26482         var coords = [];
26483         
26484         for(var i = 0, finger; finger = touches[i]; i++){
26485             coords.push(finger.pageX, finger.pageY);
26486         }
26487         
26488         var x = Math.pow(coords[0] - coords[2], 2);
26489         var y = Math.pow(coords[1] - coords[3], 2);
26490         
26491         this.startDistance = Math.sqrt(x + y);
26492         
26493         this.startScale = this.scale;
26494         
26495         this.pinching = true;
26496         this.dragable = false;
26497         
26498     },
26499     
26500     onTouchMove : function(e)
26501     {
26502         if(!this.pinching && !this.dragable){
26503             return;
26504         }
26505         
26506         var touches = e.browserEvent.touches;
26507         
26508         if(!touches){
26509             return;
26510         }
26511         
26512         if(this.dragable){
26513             this.onMouseMove(e);
26514             return;
26515         }
26516         
26517         var coords = [];
26518         
26519         for(var i = 0, finger; finger = touches[i]; i++){
26520             coords.push(finger.pageX, finger.pageY);
26521         }
26522         
26523         var x = Math.pow(coords[0] - coords[2], 2);
26524         var y = Math.pow(coords[1] - coords[3], 2);
26525         
26526         this.endDistance = Math.sqrt(x + y);
26527         
26528         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26529         
26530         if(!this.zoomable()){
26531             this.scale = this.startScale;
26532             return;
26533         }
26534         
26535         this.draw();
26536         
26537     },
26538     
26539     onTouchEnd : function(e)
26540     {
26541         this.pinching = false;
26542         this.dragable = false;
26543         
26544     },
26545     
26546     process : function(file, crop)
26547     {
26548         if(this.loadMask){
26549             this.maskEl.mask(this.loadingText);
26550         }
26551         
26552         this.xhr = new XMLHttpRequest();
26553         
26554         file.xhr = this.xhr;
26555
26556         this.xhr.open(this.method, this.url, true);
26557         
26558         var headers = {
26559             "Accept": "application/json",
26560             "Cache-Control": "no-cache",
26561             "X-Requested-With": "XMLHttpRequest"
26562         };
26563         
26564         for (var headerName in headers) {
26565             var headerValue = headers[headerName];
26566             if (headerValue) {
26567                 this.xhr.setRequestHeader(headerName, headerValue);
26568             }
26569         }
26570         
26571         var _this = this;
26572         
26573         this.xhr.onload = function()
26574         {
26575             _this.xhrOnLoad(_this.xhr);
26576         }
26577         
26578         this.xhr.onerror = function()
26579         {
26580             _this.xhrOnError(_this.xhr);
26581         }
26582         
26583         var formData = new FormData();
26584
26585         formData.append('returnHTML', 'NO');
26586         
26587         if(crop){
26588             formData.append('crop', crop);
26589         }
26590         
26591         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26592             formData.append(this.paramName, file, file.name);
26593         }
26594         
26595         if(typeof(file.filename) != 'undefined'){
26596             formData.append('filename', file.filename);
26597         }
26598         
26599         if(typeof(file.mimetype) != 'undefined'){
26600             formData.append('mimetype', file.mimetype);
26601         }
26602         
26603         if(this.fireEvent('arrange', this, formData) != false){
26604             this.xhr.send(formData);
26605         };
26606     },
26607     
26608     xhrOnLoad : function(xhr)
26609     {
26610         if(this.loadMask){
26611             this.maskEl.unmask();
26612         }
26613         
26614         if (xhr.readyState !== 4) {
26615             this.fireEvent('exception', this, xhr);
26616             return;
26617         }
26618
26619         var response = Roo.decode(xhr.responseText);
26620         
26621         if(!response.success){
26622             this.fireEvent('exception', this, xhr);
26623             return;
26624         }
26625         
26626         var response = Roo.decode(xhr.responseText);
26627         
26628         this.fireEvent('upload', this, response);
26629         
26630     },
26631     
26632     xhrOnError : function()
26633     {
26634         if(this.loadMask){
26635             this.maskEl.unmask();
26636         }
26637         
26638         Roo.log('xhr on error');
26639         
26640         var response = Roo.decode(xhr.responseText);
26641           
26642         Roo.log(response);
26643         
26644     },
26645     
26646     prepare : function(file)
26647     {   
26648         if(this.loadMask){
26649             this.maskEl.mask(this.loadingText);
26650         }
26651         
26652         this.file = false;
26653         this.exif = {};
26654         
26655         if(typeof(file) === 'string'){
26656             this.loadCanvas(file);
26657             return;
26658         }
26659         
26660         if(!file || !this.urlAPI){
26661             return;
26662         }
26663         
26664         this.file = file;
26665         this.cropType = file.type;
26666         
26667         var _this = this;
26668         
26669         if(this.fireEvent('prepare', this, this.file) != false){
26670             
26671             var reader = new FileReader();
26672             
26673             reader.onload = function (e) {
26674                 if (e.target.error) {
26675                     Roo.log(e.target.error);
26676                     return;
26677                 }
26678                 
26679                 var buffer = e.target.result,
26680                     dataView = new DataView(buffer),
26681                     offset = 2,
26682                     maxOffset = dataView.byteLength - 4,
26683                     markerBytes,
26684                     markerLength;
26685                 
26686                 if (dataView.getUint16(0) === 0xffd8) {
26687                     while (offset < maxOffset) {
26688                         markerBytes = dataView.getUint16(offset);
26689                         
26690                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26691                             markerLength = dataView.getUint16(offset + 2) + 2;
26692                             if (offset + markerLength > dataView.byteLength) {
26693                                 Roo.log('Invalid meta data: Invalid segment size.');
26694                                 break;
26695                             }
26696                             
26697                             if(markerBytes == 0xffe1){
26698                                 _this.parseExifData(
26699                                     dataView,
26700                                     offset,
26701                                     markerLength
26702                                 );
26703                             }
26704                             
26705                             offset += markerLength;
26706                             
26707                             continue;
26708                         }
26709                         
26710                         break;
26711                     }
26712                     
26713                 }
26714                 
26715                 var url = _this.urlAPI.createObjectURL(_this.file);
26716                 
26717                 _this.loadCanvas(url);
26718                 
26719                 return;
26720             }
26721             
26722             reader.readAsArrayBuffer(this.file);
26723             
26724         }
26725         
26726     },
26727     
26728     parseExifData : function(dataView, offset, length)
26729     {
26730         var tiffOffset = offset + 10,
26731             littleEndian,
26732             dirOffset;
26733     
26734         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26735             // No Exif data, might be XMP data instead
26736             return;
26737         }
26738         
26739         // Check for the ASCII code for "Exif" (0x45786966):
26740         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26741             // No Exif data, might be XMP data instead
26742             return;
26743         }
26744         if (tiffOffset + 8 > dataView.byteLength) {
26745             Roo.log('Invalid Exif data: Invalid segment size.');
26746             return;
26747         }
26748         // Check for the two null bytes:
26749         if (dataView.getUint16(offset + 8) !== 0x0000) {
26750             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26751             return;
26752         }
26753         // Check the byte alignment:
26754         switch (dataView.getUint16(tiffOffset)) {
26755         case 0x4949:
26756             littleEndian = true;
26757             break;
26758         case 0x4D4D:
26759             littleEndian = false;
26760             break;
26761         default:
26762             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26763             return;
26764         }
26765         // Check for the TIFF tag marker (0x002A):
26766         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26767             Roo.log('Invalid Exif data: Missing TIFF marker.');
26768             return;
26769         }
26770         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26771         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26772         
26773         this.parseExifTags(
26774             dataView,
26775             tiffOffset,
26776             tiffOffset + dirOffset,
26777             littleEndian
26778         );
26779     },
26780     
26781     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26782     {
26783         var tagsNumber,
26784             dirEndOffset,
26785             i;
26786         if (dirOffset + 6 > dataView.byteLength) {
26787             Roo.log('Invalid Exif data: Invalid directory offset.');
26788             return;
26789         }
26790         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26791         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26792         if (dirEndOffset + 4 > dataView.byteLength) {
26793             Roo.log('Invalid Exif data: Invalid directory size.');
26794             return;
26795         }
26796         for (i = 0; i < tagsNumber; i += 1) {
26797             this.parseExifTag(
26798                 dataView,
26799                 tiffOffset,
26800                 dirOffset + 2 + 12 * i, // tag offset
26801                 littleEndian
26802             );
26803         }
26804         // Return the offset to the next directory:
26805         return dataView.getUint32(dirEndOffset, littleEndian);
26806     },
26807     
26808     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26809     {
26810         var tag = dataView.getUint16(offset, littleEndian);
26811         
26812         this.exif[tag] = this.getExifValue(
26813             dataView,
26814             tiffOffset,
26815             offset,
26816             dataView.getUint16(offset + 2, littleEndian), // tag type
26817             dataView.getUint32(offset + 4, littleEndian), // tag length
26818             littleEndian
26819         );
26820     },
26821     
26822     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26823     {
26824         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26825             tagSize,
26826             dataOffset,
26827             values,
26828             i,
26829             str,
26830             c;
26831     
26832         if (!tagType) {
26833             Roo.log('Invalid Exif data: Invalid tag type.');
26834             return;
26835         }
26836         
26837         tagSize = tagType.size * length;
26838         // Determine if the value is contained in the dataOffset bytes,
26839         // or if the value at the dataOffset is a pointer to the actual data:
26840         dataOffset = tagSize > 4 ?
26841                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26842         if (dataOffset + tagSize > dataView.byteLength) {
26843             Roo.log('Invalid Exif data: Invalid data offset.');
26844             return;
26845         }
26846         if (length === 1) {
26847             return tagType.getValue(dataView, dataOffset, littleEndian);
26848         }
26849         values = [];
26850         for (i = 0; i < length; i += 1) {
26851             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26852         }
26853         
26854         if (tagType.ascii) {
26855             str = '';
26856             // Concatenate the chars:
26857             for (i = 0; i < values.length; i += 1) {
26858                 c = values[i];
26859                 // Ignore the terminating NULL byte(s):
26860                 if (c === '\u0000') {
26861                     break;
26862                 }
26863                 str += c;
26864             }
26865             return str;
26866         }
26867         return values;
26868     }
26869     
26870 });
26871
26872 Roo.apply(Roo.bootstrap.UploadCropbox, {
26873     tags : {
26874         'Orientation': 0x0112
26875     },
26876     
26877     Orientation: {
26878             1: 0, //'top-left',
26879 //            2: 'top-right',
26880             3: 180, //'bottom-right',
26881 //            4: 'bottom-left',
26882 //            5: 'left-top',
26883             6: 90, //'right-top',
26884 //            7: 'right-bottom',
26885             8: 270 //'left-bottom'
26886     },
26887     
26888     exifTagTypes : {
26889         // byte, 8-bit unsigned int:
26890         1: {
26891             getValue: function (dataView, dataOffset) {
26892                 return dataView.getUint8(dataOffset);
26893             },
26894             size: 1
26895         },
26896         // ascii, 8-bit byte:
26897         2: {
26898             getValue: function (dataView, dataOffset) {
26899                 return String.fromCharCode(dataView.getUint8(dataOffset));
26900             },
26901             size: 1,
26902             ascii: true
26903         },
26904         // short, 16 bit int:
26905         3: {
26906             getValue: function (dataView, dataOffset, littleEndian) {
26907                 return dataView.getUint16(dataOffset, littleEndian);
26908             },
26909             size: 2
26910         },
26911         // long, 32 bit int:
26912         4: {
26913             getValue: function (dataView, dataOffset, littleEndian) {
26914                 return dataView.getUint32(dataOffset, littleEndian);
26915             },
26916             size: 4
26917         },
26918         // rational = two long values, first is numerator, second is denominator:
26919         5: {
26920             getValue: function (dataView, dataOffset, littleEndian) {
26921                 return dataView.getUint32(dataOffset, littleEndian) /
26922                     dataView.getUint32(dataOffset + 4, littleEndian);
26923             },
26924             size: 8
26925         },
26926         // slong, 32 bit signed int:
26927         9: {
26928             getValue: function (dataView, dataOffset, littleEndian) {
26929                 return dataView.getInt32(dataOffset, littleEndian);
26930             },
26931             size: 4
26932         },
26933         // srational, two slongs, first is numerator, second is denominator:
26934         10: {
26935             getValue: function (dataView, dataOffset, littleEndian) {
26936                 return dataView.getInt32(dataOffset, littleEndian) /
26937                     dataView.getInt32(dataOffset + 4, littleEndian);
26938             },
26939             size: 8
26940         }
26941     },
26942     
26943     footer : {
26944         STANDARD : [
26945             {
26946                 tag : 'div',
26947                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26948                 action : 'rotate-left',
26949                 cn : [
26950                     {
26951                         tag : 'button',
26952                         cls : 'btn btn-default',
26953                         html : '<i class="fa fa-undo"></i>'
26954                     }
26955                 ]
26956             },
26957             {
26958                 tag : 'div',
26959                 cls : 'btn-group roo-upload-cropbox-picture',
26960                 action : 'picture',
26961                 cn : [
26962                     {
26963                         tag : 'button',
26964                         cls : 'btn btn-default',
26965                         html : '<i class="fa fa-picture-o"></i>'
26966                     }
26967                 ]
26968             },
26969             {
26970                 tag : 'div',
26971                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26972                 action : 'rotate-right',
26973                 cn : [
26974                     {
26975                         tag : 'button',
26976                         cls : 'btn btn-default',
26977                         html : '<i class="fa fa-repeat"></i>'
26978                     }
26979                 ]
26980             }
26981         ],
26982         DOCUMENT : [
26983             {
26984                 tag : 'div',
26985                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26986                 action : 'rotate-left',
26987                 cn : [
26988                     {
26989                         tag : 'button',
26990                         cls : 'btn btn-default',
26991                         html : '<i class="fa fa-undo"></i>'
26992                     }
26993                 ]
26994             },
26995             {
26996                 tag : 'div',
26997                 cls : 'btn-group roo-upload-cropbox-download',
26998                 action : 'download',
26999                 cn : [
27000                     {
27001                         tag : 'button',
27002                         cls : 'btn btn-default',
27003                         html : '<i class="fa fa-download"></i>'
27004                     }
27005                 ]
27006             },
27007             {
27008                 tag : 'div',
27009                 cls : 'btn-group roo-upload-cropbox-crop',
27010                 action : 'crop',
27011                 cn : [
27012                     {
27013                         tag : 'button',
27014                         cls : 'btn btn-default',
27015                         html : '<i class="fa fa-crop"></i>'
27016                     }
27017                 ]
27018             },
27019             {
27020                 tag : 'div',
27021                 cls : 'btn-group roo-upload-cropbox-trash',
27022                 action : 'trash',
27023                 cn : [
27024                     {
27025                         tag : 'button',
27026                         cls : 'btn btn-default',
27027                         html : '<i class="fa fa-trash"></i>'
27028                     }
27029                 ]
27030             },
27031             {
27032                 tag : 'div',
27033                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27034                 action : 'rotate-right',
27035                 cn : [
27036                     {
27037                         tag : 'button',
27038                         cls : 'btn btn-default',
27039                         html : '<i class="fa fa-repeat"></i>'
27040                     }
27041                 ]
27042             }
27043         ],
27044         ROTATOR : [
27045             {
27046                 tag : 'div',
27047                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27048                 action : 'rotate-left',
27049                 cn : [
27050                     {
27051                         tag : 'button',
27052                         cls : 'btn btn-default',
27053                         html : '<i class="fa fa-undo"></i>'
27054                     }
27055                 ]
27056             },
27057             {
27058                 tag : 'div',
27059                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27060                 action : 'rotate-right',
27061                 cn : [
27062                     {
27063                         tag : 'button',
27064                         cls : 'btn btn-default',
27065                         html : '<i class="fa fa-repeat"></i>'
27066                     }
27067                 ]
27068             }
27069         ]
27070     }
27071 });
27072
27073 /*
27074 * Licence: LGPL
27075 */
27076
27077 /**
27078  * @class Roo.bootstrap.DocumentManager
27079  * @extends Roo.bootstrap.Component
27080  * Bootstrap DocumentManager class
27081  * @cfg {String} paramName default 'imageUpload'
27082  * @cfg {String} method default POST
27083  * @cfg {String} url action url
27084  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27085  * @cfg {Boolean} multiple multiple upload default true
27086  * @cfg {Number} thumbSize default 300
27087  * @cfg {String} fieldLabel
27088  * @cfg {Number} labelWidth default 4
27089  * @cfg {String} labelAlign (left|top) default left
27090  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27091  * 
27092  * @constructor
27093  * Create a new DocumentManager
27094  * @param {Object} config The config object
27095  */
27096
27097 Roo.bootstrap.DocumentManager = function(config){
27098     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27099     
27100     this.addEvents({
27101         /**
27102          * @event initial
27103          * Fire when initial the DocumentManager
27104          * @param {Roo.bootstrap.DocumentManager} this
27105          */
27106         "initial" : true,
27107         /**
27108          * @event inspect
27109          * inspect selected file
27110          * @param {Roo.bootstrap.DocumentManager} this
27111          * @param {File} file
27112          */
27113         "inspect" : true,
27114         /**
27115          * @event exception
27116          * Fire when xhr load exception
27117          * @param {Roo.bootstrap.DocumentManager} this
27118          * @param {XMLHttpRequest} xhr
27119          */
27120         "exception" : true,
27121         /**
27122          * @event prepare
27123          * prepare the form data
27124          * @param {Roo.bootstrap.DocumentManager} this
27125          * @param {Object} formData
27126          */
27127         "prepare" : true,
27128         /**
27129          * @event remove
27130          * Fire when remove the file
27131          * @param {Roo.bootstrap.DocumentManager} this
27132          * @param {Object} file
27133          */
27134         "remove" : true,
27135         /**
27136          * @event refresh
27137          * Fire after refresh the file
27138          * @param {Roo.bootstrap.DocumentManager} this
27139          */
27140         "refresh" : true,
27141         /**
27142          * @event click
27143          * Fire after click the image
27144          * @param {Roo.bootstrap.DocumentManager} this
27145          * @param {Object} file
27146          */
27147         "click" : true,
27148         /**
27149          * @event edit
27150          * Fire when upload a image and editable set to true
27151          * @param {Roo.bootstrap.DocumentManager} this
27152          * @param {Object} file
27153          */
27154         "edit" : true,
27155         /**
27156          * @event beforeselectfile
27157          * Fire before select file
27158          * @param {Roo.bootstrap.DocumentManager} this
27159          */
27160         "beforeselectfile" : true,
27161         /**
27162          * @event process
27163          * Fire before process file
27164          * @param {Roo.bootstrap.DocumentManager} this
27165          * @param {Object} file
27166          */
27167         "process" : true
27168         
27169     });
27170 };
27171
27172 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27173     
27174     boxes : 0,
27175     inputName : '',
27176     thumbSize : 300,
27177     multiple : true,
27178     files : [],
27179     method : 'POST',
27180     url : '',
27181     paramName : 'imageUpload',
27182     fieldLabel : '',
27183     labelWidth : 4,
27184     labelAlign : 'left',
27185     editable : true,
27186     delegates : [],
27187     
27188     
27189     xhr : false, 
27190     
27191     getAutoCreate : function()
27192     {   
27193         var managerWidget = {
27194             tag : 'div',
27195             cls : 'roo-document-manager',
27196             cn : [
27197                 {
27198                     tag : 'input',
27199                     cls : 'roo-document-manager-selector',
27200                     type : 'file'
27201                 },
27202                 {
27203                     tag : 'div',
27204                     cls : 'roo-document-manager-uploader',
27205                     cn : [
27206                         {
27207                             tag : 'div',
27208                             cls : 'roo-document-manager-upload-btn',
27209                             html : '<i class="fa fa-plus"></i>'
27210                         }
27211                     ]
27212                     
27213                 }
27214             ]
27215         };
27216         
27217         var content = [
27218             {
27219                 tag : 'div',
27220                 cls : 'column col-md-12',
27221                 cn : managerWidget
27222             }
27223         ];
27224         
27225         if(this.fieldLabel.length){
27226             
27227             content = [
27228                 {
27229                     tag : 'div',
27230                     cls : 'column col-md-12',
27231                     html : this.fieldLabel
27232                 },
27233                 {
27234                     tag : 'div',
27235                     cls : 'column col-md-12',
27236                     cn : managerWidget
27237                 }
27238             ];
27239
27240             if(this.labelAlign == 'left'){
27241                 content = [
27242                     {
27243                         tag : 'div',
27244                         cls : 'column col-md-' + this.labelWidth,
27245                         html : this.fieldLabel
27246                     },
27247                     {
27248                         tag : 'div',
27249                         cls : 'column col-md-' + (12 - this.labelWidth),
27250                         cn : managerWidget
27251                     }
27252                 ];
27253                 
27254             }
27255         }
27256         
27257         var cfg = {
27258             tag : 'div',
27259             cls : 'row clearfix',
27260             cn : content
27261         };
27262         
27263         return cfg;
27264         
27265     },
27266     
27267     initEvents : function()
27268     {
27269         this.managerEl = this.el.select('.roo-document-manager', true).first();
27270         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27271         
27272         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27273         this.selectorEl.hide();
27274         
27275         if(this.multiple){
27276             this.selectorEl.attr('multiple', 'multiple');
27277         }
27278         
27279         this.selectorEl.on('change', this.onFileSelected, this);
27280         
27281         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27282         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27283         
27284         this.uploader.on('click', this.onUploaderClick, this);
27285         
27286         this.renderProgressDialog();
27287         
27288         var _this = this;
27289         
27290         window.addEventListener("resize", function() { _this.refresh(); } );
27291         
27292         this.fireEvent('initial', this);
27293     },
27294     
27295     renderProgressDialog : function()
27296     {
27297         var _this = this;
27298         
27299         this.progressDialog = new Roo.bootstrap.Modal({
27300             cls : 'roo-document-manager-progress-dialog',
27301             allow_close : false,
27302             title : '',
27303             buttons : [
27304                 {
27305                     name  :'cancel',
27306                     weight : 'danger',
27307                     html : 'Cancel'
27308                 }
27309             ], 
27310             listeners : { 
27311                 btnclick : function() {
27312                     _this.uploadCancel();
27313                     this.hide();
27314                 }
27315             }
27316         });
27317          
27318         this.progressDialog.render(Roo.get(document.body));
27319          
27320         this.progress = new Roo.bootstrap.Progress({
27321             cls : 'roo-document-manager-progress',
27322             active : true,
27323             striped : true
27324         });
27325         
27326         this.progress.render(this.progressDialog.getChildContainer());
27327         
27328         this.progressBar = new Roo.bootstrap.ProgressBar({
27329             cls : 'roo-document-manager-progress-bar',
27330             aria_valuenow : 0,
27331             aria_valuemin : 0,
27332             aria_valuemax : 12,
27333             panel : 'success'
27334         });
27335         
27336         this.progressBar.render(this.progress.getChildContainer());
27337     },
27338     
27339     onUploaderClick : function(e)
27340     {
27341         e.preventDefault();
27342      
27343         if(this.fireEvent('beforeselectfile', this) != false){
27344             this.selectorEl.dom.click();
27345         }
27346         
27347     },
27348     
27349     onFileSelected : function(e)
27350     {
27351         e.preventDefault();
27352         
27353         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27354             return;
27355         }
27356         
27357         Roo.each(this.selectorEl.dom.files, function(file){
27358             if(this.fireEvent('inspect', this, file) != false){
27359                 this.files.push(file);
27360             }
27361         }, this);
27362         
27363         this.queue();
27364         
27365     },
27366     
27367     queue : function()
27368     {
27369         this.selectorEl.dom.value = '';
27370         
27371         if(!this.files.length){
27372             return;
27373         }
27374         
27375         if(this.boxes > 0 && this.files.length > this.boxes){
27376             this.files = this.files.slice(0, this.boxes);
27377         }
27378         
27379         this.uploader.show();
27380         
27381         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27382             this.uploader.hide();
27383         }
27384         
27385         var _this = this;
27386         
27387         var files = [];
27388         
27389         var docs = [];
27390         
27391         Roo.each(this.files, function(file){
27392             
27393             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27394                 var f = this.renderPreview(file);
27395                 files.push(f);
27396                 return;
27397             }
27398             
27399             if(file.type.indexOf('image') != -1){
27400                 this.delegates.push(
27401                     (function(){
27402                         _this.process(file);
27403                     }).createDelegate(this)
27404                 );
27405         
27406                 return;
27407             }
27408             
27409             docs.push(
27410                 (function(){
27411                     _this.process(file);
27412                 }).createDelegate(this)
27413             );
27414             
27415         }, this);
27416         
27417         this.files = files;
27418         
27419         this.delegates = this.delegates.concat(docs);
27420         
27421         if(!this.delegates.length){
27422             this.refresh();
27423             return;
27424         }
27425         
27426         this.progressBar.aria_valuemax = this.delegates.length;
27427         
27428         this.arrange();
27429         
27430         return;
27431     },
27432     
27433     arrange : function()
27434     {
27435         if(!this.delegates.length){
27436             this.progressDialog.hide();
27437             this.refresh();
27438             return;
27439         }
27440         
27441         var delegate = this.delegates.shift();
27442         
27443         this.progressDialog.show();
27444         
27445         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27446         
27447         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27448         
27449         delegate();
27450     },
27451     
27452     refresh : function()
27453     {
27454         this.uploader.show();
27455         
27456         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27457             this.uploader.hide();
27458         }
27459         
27460         Roo.isTouch ? this.closable(false) : this.closable(true);
27461         
27462         this.fireEvent('refresh', this);
27463     },
27464     
27465     onRemove : function(e, el, o)
27466     {
27467         e.preventDefault();
27468         
27469         this.fireEvent('remove', this, o);
27470         
27471     },
27472     
27473     remove : function(o)
27474     {
27475         var files = [];
27476         
27477         Roo.each(this.files, function(file){
27478             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27479                 files.push(file);
27480                 return;
27481             }
27482
27483             o.target.remove();
27484
27485         }, this);
27486         
27487         this.files = files;
27488         
27489         this.refresh();
27490     },
27491     
27492     clear : function()
27493     {
27494         Roo.each(this.files, function(file){
27495             if(!file.target){
27496                 return;
27497             }
27498             
27499             file.target.remove();
27500
27501         }, this);
27502         
27503         this.files = [];
27504         
27505         this.refresh();
27506     },
27507     
27508     onClick : function(e, el, o)
27509     {
27510         e.preventDefault();
27511         
27512         this.fireEvent('click', this, o);
27513         
27514     },
27515     
27516     closable : function(closable)
27517     {
27518         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27519             
27520             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27521             
27522             if(closable){
27523                 el.show();
27524                 return;
27525             }
27526             
27527             el.hide();
27528             
27529         }, this);
27530     },
27531     
27532     xhrOnLoad : function(xhr)
27533     {
27534         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27535             el.remove();
27536         }, this);
27537         
27538         if (xhr.readyState !== 4) {
27539             this.arrange();
27540             this.fireEvent('exception', this, xhr);
27541             return;
27542         }
27543
27544         var response = Roo.decode(xhr.responseText);
27545         
27546         if(!response.success){
27547             this.arrange();
27548             this.fireEvent('exception', this, xhr);
27549             return;
27550         }
27551         
27552         var file = this.renderPreview(response.data);
27553         
27554         this.files.push(file);
27555         
27556         this.arrange();
27557         
27558     },
27559     
27560     xhrOnError : function(xhr)
27561     {
27562         Roo.log('xhr on error');
27563         
27564         var response = Roo.decode(xhr.responseText);
27565           
27566         Roo.log(response);
27567         
27568         this.arrange();
27569     },
27570     
27571     process : function(file)
27572     {
27573         if(this.fireEvent('process', this, file) !== false){
27574             if(this.editable && file.type.indexOf('image') != -1){
27575                 this.fireEvent('edit', this, file);
27576                 return;
27577             }
27578
27579             this.uploadStart(file, false);
27580
27581             return;
27582         }
27583         
27584     },
27585     
27586     uploadStart : function(file, crop)
27587     {
27588         this.xhr = new XMLHttpRequest();
27589         
27590         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27591             this.arrange();
27592             return;
27593         }
27594         
27595         file.xhr = this.xhr;
27596             
27597         this.managerEl.createChild({
27598             tag : 'div',
27599             cls : 'roo-document-manager-loading',
27600             cn : [
27601                 {
27602                     tag : 'div',
27603                     tooltip : file.name,
27604                     cls : 'roo-document-manager-thumb',
27605                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27606                 }
27607             ]
27608
27609         });
27610
27611         this.xhr.open(this.method, this.url, true);
27612         
27613         var headers = {
27614             "Accept": "application/json",
27615             "Cache-Control": "no-cache",
27616             "X-Requested-With": "XMLHttpRequest"
27617         };
27618         
27619         for (var headerName in headers) {
27620             var headerValue = headers[headerName];
27621             if (headerValue) {
27622                 this.xhr.setRequestHeader(headerName, headerValue);
27623             }
27624         }
27625         
27626         var _this = this;
27627         
27628         this.xhr.onload = function()
27629         {
27630             _this.xhrOnLoad(_this.xhr);
27631         }
27632         
27633         this.xhr.onerror = function()
27634         {
27635             _this.xhrOnError(_this.xhr);
27636         }
27637         
27638         var formData = new FormData();
27639
27640         formData.append('returnHTML', 'NO');
27641         
27642         if(crop){
27643             formData.append('crop', crop);
27644         }
27645         
27646         formData.append(this.paramName, file, file.name);
27647         
27648         if(this.fireEvent('prepare', this, formData) != false){
27649             this.xhr.send(formData);
27650         };
27651     },
27652     
27653     uploadCancel : function()
27654     {
27655         if (this.xhr) {
27656             this.xhr.abort();
27657         }
27658         
27659         
27660         this.delegates = [];
27661         
27662         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27663             el.remove();
27664         }, this);
27665         
27666         this.arrange();
27667     },
27668     
27669     renderPreview : function(file)
27670     {
27671         if(typeof(file.target) != 'undefined' && file.target){
27672             return file;
27673         }
27674         
27675         var previewEl = this.managerEl.createChild({
27676             tag : 'div',
27677             cls : 'roo-document-manager-preview',
27678             cn : [
27679                 {
27680                     tag : 'div',
27681                     tooltip : file.filename,
27682                     cls : 'roo-document-manager-thumb',
27683                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27684                 },
27685                 {
27686                     tag : 'button',
27687                     cls : 'close',
27688                     html : '<i class="fa fa-times-circle"></i>'
27689                 }
27690             ]
27691         });
27692
27693         var close = previewEl.select('button.close', true).first();
27694
27695         close.on('click', this.onRemove, this, file);
27696
27697         file.target = previewEl;
27698
27699         var image = previewEl.select('img', true).first();
27700         
27701         var _this = this;
27702         
27703         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27704         
27705         image.on('click', this.onClick, this, file);
27706         
27707         return file;
27708         
27709     },
27710     
27711     onPreviewLoad : function(file, image)
27712     {
27713         if(typeof(file.target) == 'undefined' || !file.target){
27714             return;
27715         }
27716         
27717         var width = image.dom.naturalWidth || image.dom.width;
27718         var height = image.dom.naturalHeight || image.dom.height;
27719         
27720         if(width > height){
27721             file.target.addClass('wide');
27722             return;
27723         }
27724         
27725         file.target.addClass('tall');
27726         return;
27727         
27728     },
27729     
27730     uploadFromSource : function(file, crop)
27731     {
27732         this.xhr = new XMLHttpRequest();
27733         
27734         this.managerEl.createChild({
27735             tag : 'div',
27736             cls : 'roo-document-manager-loading',
27737             cn : [
27738                 {
27739                     tag : 'div',
27740                     tooltip : file.name,
27741                     cls : 'roo-document-manager-thumb',
27742                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27743                 }
27744             ]
27745
27746         });
27747
27748         this.xhr.open(this.method, this.url, true);
27749         
27750         var headers = {
27751             "Accept": "application/json",
27752             "Cache-Control": "no-cache",
27753             "X-Requested-With": "XMLHttpRequest"
27754         };
27755         
27756         for (var headerName in headers) {
27757             var headerValue = headers[headerName];
27758             if (headerValue) {
27759                 this.xhr.setRequestHeader(headerName, headerValue);
27760             }
27761         }
27762         
27763         var _this = this;
27764         
27765         this.xhr.onload = function()
27766         {
27767             _this.xhrOnLoad(_this.xhr);
27768         }
27769         
27770         this.xhr.onerror = function()
27771         {
27772             _this.xhrOnError(_this.xhr);
27773         }
27774         
27775         var formData = new FormData();
27776
27777         formData.append('returnHTML', 'NO');
27778         
27779         formData.append('crop', crop);
27780         
27781         if(typeof(file.filename) != 'undefined'){
27782             formData.append('filename', file.filename);
27783         }
27784         
27785         if(typeof(file.mimetype) != 'undefined'){
27786             formData.append('mimetype', file.mimetype);
27787         }
27788         
27789         if(this.fireEvent('prepare', this, formData) != false){
27790             this.xhr.send(formData);
27791         };
27792     }
27793 });
27794
27795 /*
27796 * Licence: LGPL
27797 */
27798
27799 /**
27800  * @class Roo.bootstrap.DocumentViewer
27801  * @extends Roo.bootstrap.Component
27802  * Bootstrap DocumentViewer class
27803  * 
27804  * @constructor
27805  * Create a new DocumentViewer
27806  * @param {Object} config The config object
27807  */
27808
27809 Roo.bootstrap.DocumentViewer = function(config){
27810     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27811     
27812     this.addEvents({
27813         /**
27814          * @event initial
27815          * Fire after initEvent
27816          * @param {Roo.bootstrap.DocumentViewer} this
27817          */
27818         "initial" : true,
27819         /**
27820          * @event click
27821          * Fire after click
27822          * @param {Roo.bootstrap.DocumentViewer} this
27823          */
27824         "click" : true,
27825         /**
27826          * @event trash
27827          * Fire after trash button
27828          * @param {Roo.bootstrap.DocumentViewer} this
27829          */
27830         "trash" : true
27831         
27832     });
27833 };
27834
27835 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27836     
27837     getAutoCreate : function()
27838     {
27839         var cfg = {
27840             tag : 'div',
27841             cls : 'roo-document-viewer',
27842             cn : [
27843                 {
27844                     tag : 'div',
27845                     cls : 'roo-document-viewer-body',
27846                     cn : [
27847                         {
27848                             tag : 'div',
27849                             cls : 'roo-document-viewer-thumb',
27850                             cn : [
27851                                 {
27852                                     tag : 'img',
27853                                     cls : 'roo-document-viewer-image'
27854                                 }
27855                             ]
27856                         }
27857                     ]
27858                 },
27859                 {
27860                     tag : 'div',
27861                     cls : 'roo-document-viewer-footer',
27862                     cn : {
27863                         tag : 'div',
27864                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27865                         cn : [
27866                             {
27867                                 tag : 'div',
27868                                 cls : 'btn-group',
27869                                 cn : [
27870                                     {
27871                                         tag : 'button',
27872                                         cls : 'btn btn-default roo-document-viewer-trash',
27873                                         html : '<i class="fa fa-trash"></i>'
27874                                     }
27875                                 ]
27876                             }
27877                         ]
27878                     }
27879                 }
27880             ]
27881         };
27882         
27883         return cfg;
27884     },
27885     
27886     initEvents : function()
27887     {
27888         
27889         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27890         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27891         
27892         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27893         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27894         
27895         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27896         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27897         
27898         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27899         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27900         
27901         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27902         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27903         
27904         this.bodyEl.on('click', this.onClick, this);
27905         
27906         this.trashBtn.on('click', this.onTrash, this);
27907         
27908     },
27909     
27910     initial : function()
27911     {
27912 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27913         
27914         
27915         this.fireEvent('initial', this);
27916         
27917     },
27918     
27919     onClick : function(e)
27920     {
27921         e.preventDefault();
27922         
27923         this.fireEvent('click', this);
27924     },
27925     
27926     onTrash : function(e)
27927     {
27928         e.preventDefault();
27929         
27930         this.fireEvent('trash', this);
27931     }
27932     
27933 });
27934 /*
27935  * - LGPL
27936  *
27937  * nav progress bar
27938  * 
27939  */
27940
27941 /**
27942  * @class Roo.bootstrap.NavProgressBar
27943  * @extends Roo.bootstrap.Component
27944  * Bootstrap NavProgressBar class
27945  * 
27946  * @constructor
27947  * Create a new nav progress bar
27948  * @param {Object} config The config object
27949  */
27950
27951 Roo.bootstrap.NavProgressBar = function(config){
27952     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27953
27954     this.bullets = this.bullets || [];
27955    
27956 //    Roo.bootstrap.NavProgressBar.register(this);
27957      this.addEvents({
27958         /**
27959              * @event changed
27960              * Fires when the active item changes
27961              * @param {Roo.bootstrap.NavProgressBar} this
27962              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27963              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27964          */
27965         'changed': true
27966      });
27967     
27968 };
27969
27970 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27971     
27972     bullets : [],
27973     barItems : [],
27974     
27975     getAutoCreate : function()
27976     {
27977         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27978         
27979         cfg = {
27980             tag : 'div',
27981             cls : 'roo-navigation-bar-group',
27982             cn : [
27983                 {
27984                     tag : 'div',
27985                     cls : 'roo-navigation-top-bar'
27986                 },
27987                 {
27988                     tag : 'div',
27989                     cls : 'roo-navigation-bullets-bar',
27990                     cn : [
27991                         {
27992                             tag : 'ul',
27993                             cls : 'roo-navigation-bar'
27994                         }
27995                     ]
27996                 },
27997                 
27998                 {
27999                     tag : 'div',
28000                     cls : 'roo-navigation-bottom-bar'
28001                 }
28002             ]
28003             
28004         };
28005         
28006         return cfg;
28007         
28008     },
28009     
28010     initEvents: function() 
28011     {
28012         
28013     },
28014     
28015     onRender : function(ct, position) 
28016     {
28017         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28018         
28019         if(this.bullets.length){
28020             Roo.each(this.bullets, function(b){
28021                this.addItem(b);
28022             }, this);
28023         }
28024         
28025         this.format();
28026         
28027     },
28028     
28029     addItem : function(cfg)
28030     {
28031         var item = new Roo.bootstrap.NavProgressItem(cfg);
28032         
28033         item.parentId = this.id;
28034         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28035         
28036         if(cfg.html){
28037             var top = new Roo.bootstrap.Element({
28038                 tag : 'div',
28039                 cls : 'roo-navigation-bar-text'
28040             });
28041             
28042             var bottom = new Roo.bootstrap.Element({
28043                 tag : 'div',
28044                 cls : 'roo-navigation-bar-text'
28045             });
28046             
28047             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28048             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28049             
28050             var topText = new Roo.bootstrap.Element({
28051                 tag : 'span',
28052                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28053             });
28054             
28055             var bottomText = new Roo.bootstrap.Element({
28056                 tag : 'span',
28057                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28058             });
28059             
28060             topText.onRender(top.el, null);
28061             bottomText.onRender(bottom.el, null);
28062             
28063             item.topEl = top;
28064             item.bottomEl = bottom;
28065         }
28066         
28067         this.barItems.push(item);
28068         
28069         return item;
28070     },
28071     
28072     getActive : function()
28073     {
28074         var active = false;
28075         
28076         Roo.each(this.barItems, function(v){
28077             
28078             if (!v.isActive()) {
28079                 return;
28080             }
28081             
28082             active = v;
28083             return false;
28084             
28085         });
28086         
28087         return active;
28088     },
28089     
28090     setActiveItem : function(item)
28091     {
28092         var prev = false;
28093         
28094         Roo.each(this.barItems, function(v){
28095             if (v.rid == item.rid) {
28096                 return ;
28097             }
28098             
28099             if (v.isActive()) {
28100                 v.setActive(false);
28101                 prev = v;
28102             }
28103         });
28104
28105         item.setActive(true);
28106         
28107         this.fireEvent('changed', this, item, prev);
28108     },
28109     
28110     getBarItem: function(rid)
28111     {
28112         var ret = false;
28113         
28114         Roo.each(this.barItems, function(e) {
28115             if (e.rid != rid) {
28116                 return;
28117             }
28118             
28119             ret =  e;
28120             return false;
28121         });
28122         
28123         return ret;
28124     },
28125     
28126     indexOfItem : function(item)
28127     {
28128         var index = false;
28129         
28130         Roo.each(this.barItems, function(v, i){
28131             
28132             if (v.rid != item.rid) {
28133                 return;
28134             }
28135             
28136             index = i;
28137             return false
28138         });
28139         
28140         return index;
28141     },
28142     
28143     setActiveNext : function()
28144     {
28145         var i = this.indexOfItem(this.getActive());
28146         
28147         if (i > this.barItems.length) {
28148             return;
28149         }
28150         
28151         this.setActiveItem(this.barItems[i+1]);
28152     },
28153     
28154     setActivePrev : function()
28155     {
28156         var i = this.indexOfItem(this.getActive());
28157         
28158         if (i  < 1) {
28159             return;
28160         }
28161         
28162         this.setActiveItem(this.barItems[i-1]);
28163     },
28164     
28165     format : function()
28166     {
28167         if(!this.barItems.length){
28168             return;
28169         }
28170      
28171         var width = 100 / this.barItems.length;
28172         
28173         Roo.each(this.barItems, function(i){
28174             i.el.setStyle('width', width + '%');
28175             i.topEl.el.setStyle('width', width + '%');
28176             i.bottomEl.el.setStyle('width', width + '%');
28177         }, this);
28178         
28179     }
28180     
28181 });
28182 /*
28183  * - LGPL
28184  *
28185  * Nav Progress Item
28186  * 
28187  */
28188
28189 /**
28190  * @class Roo.bootstrap.NavProgressItem
28191  * @extends Roo.bootstrap.Component
28192  * Bootstrap NavProgressItem class
28193  * @cfg {String} rid the reference id
28194  * @cfg {Boolean} active (true|false) Is item active default false
28195  * @cfg {Boolean} disabled (true|false) Is item active default false
28196  * @cfg {String} html
28197  * @cfg {String} position (top|bottom) text position default bottom
28198  * @cfg {String} icon show icon instead of number
28199  * 
28200  * @constructor
28201  * Create a new NavProgressItem
28202  * @param {Object} config The config object
28203  */
28204 Roo.bootstrap.NavProgressItem = function(config){
28205     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28206     this.addEvents({
28207         // raw events
28208         /**
28209          * @event click
28210          * The raw click event for the entire grid.
28211          * @param {Roo.bootstrap.NavProgressItem} this
28212          * @param {Roo.EventObject} e
28213          */
28214         "click" : true
28215     });
28216    
28217 };
28218
28219 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28220     
28221     rid : '',
28222     active : false,
28223     disabled : false,
28224     html : '',
28225     position : 'bottom',
28226     icon : false,
28227     
28228     getAutoCreate : function()
28229     {
28230         var iconCls = 'roo-navigation-bar-item-icon';
28231         
28232         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28233         
28234         var cfg = {
28235             tag: 'li',
28236             cls: 'roo-navigation-bar-item',
28237             cn : [
28238                 {
28239                     tag : 'i',
28240                     cls : iconCls
28241                 }
28242             ]
28243         };
28244         
28245         if(this.active){
28246             cfg.cls += ' active';
28247         }
28248         if(this.disabled){
28249             cfg.cls += ' disabled';
28250         }
28251         
28252         return cfg;
28253     },
28254     
28255     disable : function()
28256     {
28257         this.setDisabled(true);
28258     },
28259     
28260     enable : function()
28261     {
28262         this.setDisabled(false);
28263     },
28264     
28265     initEvents: function() 
28266     {
28267         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28268         
28269         this.iconEl.on('click', this.onClick, this);
28270     },
28271     
28272     onClick : function(e)
28273     {
28274         e.preventDefault();
28275         
28276         if(this.disabled){
28277             return;
28278         }
28279         
28280         if(this.fireEvent('click', this, e) === false){
28281             return;
28282         };
28283         
28284         this.parent().setActiveItem(this);
28285     },
28286     
28287     isActive: function () 
28288     {
28289         return this.active;
28290     },
28291     
28292     setActive : function(state)
28293     {
28294         if(this.active == state){
28295             return;
28296         }
28297         
28298         this.active = state;
28299         
28300         if (state) {
28301             this.el.addClass('active');
28302             return;
28303         }
28304         
28305         this.el.removeClass('active');
28306         
28307         return;
28308     },
28309     
28310     setDisabled : function(state)
28311     {
28312         if(this.disabled == state){
28313             return;
28314         }
28315         
28316         this.disabled = state;
28317         
28318         if (state) {
28319             this.el.addClass('disabled');
28320             return;
28321         }
28322         
28323         this.el.removeClass('disabled');
28324     },
28325     
28326     tooltipEl : function()
28327     {
28328         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28329     }
28330 });
28331  
28332
28333  /*
28334  * - LGPL
28335  *
28336  * FieldLabel
28337  * 
28338  */
28339
28340 /**
28341  * @class Roo.bootstrap.FieldLabel
28342  * @extends Roo.bootstrap.Component
28343  * Bootstrap FieldLabel class
28344  * @cfg {String} html contents of the element
28345  * @cfg {String} tag tag of the element default label
28346  * @cfg {String} cls class of the element
28347  * @cfg {String} target label target 
28348  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28349  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28350  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28351  * @cfg {String} iconTooltip default "This field is required"
28352  * 
28353  * @constructor
28354  * Create a new FieldLabel
28355  * @param {Object} config The config object
28356  */
28357
28358 Roo.bootstrap.FieldLabel = function(config){
28359     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28360     
28361     this.addEvents({
28362             /**
28363              * @event invalid
28364              * Fires after the field has been marked as invalid.
28365              * @param {Roo.form.FieldLabel} this
28366              * @param {String} msg The validation message
28367              */
28368             invalid : true,
28369             /**
28370              * @event valid
28371              * Fires after the field has been validated with no errors.
28372              * @param {Roo.form.FieldLabel} this
28373              */
28374             valid : true
28375         });
28376 };
28377
28378 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28379     
28380     tag: 'label',
28381     cls: '',
28382     html: '',
28383     target: '',
28384     allowBlank : true,
28385     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28386     validClass : 'text-success fa fa-lg fa-check',
28387     iconTooltip : 'This field is required',
28388     
28389     getAutoCreate : function(){
28390         
28391         var cfg = {
28392             tag : this.tag,
28393             cls : 'roo-bootstrap-field-label ' + this.cls,
28394             for : this.target,
28395             cn : [
28396                 {
28397                     tag : 'i',
28398                     cls : '',
28399                     tooltip : this.iconTooltip
28400                 },
28401                 {
28402                     tag : 'span',
28403                     html : this.html
28404                 }
28405             ] 
28406         };
28407         
28408         return cfg;
28409     },
28410     
28411     initEvents: function() 
28412     {
28413         Roo.bootstrap.Element.superclass.initEvents.call(this);
28414         
28415         this.iconEl = this.el.select('i', true).first();
28416         
28417         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28418         
28419         Roo.bootstrap.FieldLabel.register(this);
28420     },
28421     
28422     /**
28423      * Mark this field as valid
28424      */
28425     markValid : function()
28426     {
28427         this.iconEl.show();
28428         
28429         this.iconEl.removeClass(this.invalidClass);
28430         
28431         this.iconEl.addClass(this.validClass);
28432         
28433         this.fireEvent('valid', this);
28434     },
28435     
28436     /**
28437      * Mark this field as invalid
28438      * @param {String} msg The validation message
28439      */
28440     markInvalid : function(msg)
28441     {
28442         this.iconEl.show();
28443         
28444         this.iconEl.removeClass(this.validClass);
28445         
28446         this.iconEl.addClass(this.invalidClass);
28447         
28448         this.fireEvent('invalid', this, msg);
28449     }
28450     
28451    
28452 });
28453
28454 Roo.apply(Roo.bootstrap.FieldLabel, {
28455     
28456     groups: {},
28457     
28458      /**
28459     * register a FieldLabel Group
28460     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28461     */
28462     register : function(label)
28463     {
28464         if(this.groups.hasOwnProperty(label.target)){
28465             return;
28466         }
28467      
28468         this.groups[label.target] = label;
28469         
28470     },
28471     /**
28472     * fetch a FieldLabel Group based on the target
28473     * @param {string} target
28474     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28475     */
28476     get: function(target) {
28477         if (typeof(this.groups[target]) == 'undefined') {
28478             return false;
28479         }
28480         
28481         return this.groups[target] ;
28482     }
28483 });
28484
28485  
28486
28487  /*
28488  * - LGPL
28489  *
28490  * page DateSplitField.
28491  * 
28492  */
28493
28494
28495 /**
28496  * @class Roo.bootstrap.DateSplitField
28497  * @extends Roo.bootstrap.Component
28498  * Bootstrap DateSplitField class
28499  * @cfg {string} fieldLabel - the label associated
28500  * @cfg {Number} labelWidth set the width of label (0-12)
28501  * @cfg {String} labelAlign (top|left)
28502  * @cfg {Boolean} dayAllowBlank (true|false) default false
28503  * @cfg {Boolean} monthAllowBlank (true|false) default false
28504  * @cfg {Boolean} yearAllowBlank (true|false) default false
28505  * @cfg {string} dayPlaceholder 
28506  * @cfg {string} monthPlaceholder
28507  * @cfg {string} yearPlaceholder
28508  * @cfg {string} dayFormat default 'd'
28509  * @cfg {string} monthFormat default 'm'
28510  * @cfg {string} yearFormat default 'Y'
28511
28512  *     
28513  * @constructor
28514  * Create a new DateSplitField
28515  * @param {Object} config The config object
28516  */
28517
28518 Roo.bootstrap.DateSplitField = function(config){
28519     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28520     
28521     this.addEvents({
28522         // raw events
28523          /**
28524          * @event years
28525          * getting the data of years
28526          * @param {Roo.bootstrap.DateSplitField} this
28527          * @param {Object} years
28528          */
28529         "years" : true,
28530         /**
28531          * @event days
28532          * getting the data of days
28533          * @param {Roo.bootstrap.DateSplitField} this
28534          * @param {Object} days
28535          */
28536         "days" : true,
28537         /**
28538          * @event invalid
28539          * Fires after the field has been marked as invalid.
28540          * @param {Roo.form.Field} this
28541          * @param {String} msg The validation message
28542          */
28543         invalid : true,
28544        /**
28545          * @event valid
28546          * Fires after the field has been validated with no errors.
28547          * @param {Roo.form.Field} this
28548          */
28549         valid : true
28550     });
28551 };
28552
28553 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28554     
28555     fieldLabel : '',
28556     labelAlign : 'top',
28557     labelWidth : 3,
28558     dayAllowBlank : false,
28559     monthAllowBlank : false,
28560     yearAllowBlank : false,
28561     dayPlaceholder : '',
28562     monthPlaceholder : '',
28563     yearPlaceholder : '',
28564     dayFormat : 'd',
28565     monthFormat : 'm',
28566     yearFormat : 'Y',
28567     isFormField : true,
28568     
28569     getAutoCreate : function()
28570     {
28571         var cfg = {
28572             tag : 'div',
28573             cls : 'row roo-date-split-field-group',
28574             cn : [
28575                 {
28576                     tag : 'input',
28577                     type : 'hidden',
28578                     cls : 'form-hidden-field roo-date-split-field-group-value',
28579                     name : this.name
28580                 }
28581             ]
28582         };
28583         
28584         if(this.fieldLabel){
28585             cfg.cn.push({
28586                 tag : 'div',
28587                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28588                 cn : [
28589                     {
28590                         tag : 'label',
28591                         html : this.fieldLabel
28592                     }
28593                 ]
28594             });
28595         }
28596         
28597         Roo.each(['day', 'month', 'year'], function(t){
28598             cfg.cn.push({
28599                 tag : 'div',
28600                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28601             });
28602         }, this);
28603         
28604         return cfg;
28605     },
28606     
28607     inputEl: function ()
28608     {
28609         return this.el.select('.roo-date-split-field-group-value', true).first();
28610     },
28611     
28612     onRender : function(ct, position) 
28613     {
28614         var _this = this;
28615         
28616         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28617         
28618         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28619         
28620         this.dayField = new Roo.bootstrap.ComboBox({
28621             allowBlank : this.dayAllowBlank,
28622             alwaysQuery : true,
28623             displayField : 'value',
28624             editable : false,
28625             fieldLabel : '',
28626             forceSelection : true,
28627             mode : 'local',
28628             placeholder : this.dayPlaceholder,
28629             selectOnFocus : true,
28630             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28631             triggerAction : 'all',
28632             typeAhead : true,
28633             valueField : 'value',
28634             store : new Roo.data.SimpleStore({
28635                 data : (function() {    
28636                     var days = [];
28637                     _this.fireEvent('days', _this, days);
28638                     return days;
28639                 })(),
28640                 fields : [ 'value' ]
28641             }),
28642             listeners : {
28643                 select : function (_self, record, index)
28644                 {
28645                     _this.setValue(_this.getValue());
28646                 }
28647             }
28648         });
28649
28650         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28651         
28652         this.monthField = new Roo.bootstrap.MonthField({
28653             after : '<i class=\"fa fa-calendar\"></i>',
28654             allowBlank : this.monthAllowBlank,
28655             placeholder : this.monthPlaceholder,
28656             readOnly : true,
28657             listeners : {
28658                 render : function (_self)
28659                 {
28660                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28661                         e.preventDefault();
28662                         _self.focus();
28663                     });
28664                 },
28665                 select : function (_self, oldvalue, newvalue)
28666                 {
28667                     _this.setValue(_this.getValue());
28668                 }
28669             }
28670         });
28671         
28672         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28673         
28674         this.yearField = new Roo.bootstrap.ComboBox({
28675             allowBlank : this.yearAllowBlank,
28676             alwaysQuery : true,
28677             displayField : 'value',
28678             editable : false,
28679             fieldLabel : '',
28680             forceSelection : true,
28681             mode : 'local',
28682             placeholder : this.yearPlaceholder,
28683             selectOnFocus : true,
28684             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28685             triggerAction : 'all',
28686             typeAhead : true,
28687             valueField : 'value',
28688             store : new Roo.data.SimpleStore({
28689                 data : (function() {
28690                     var years = [];
28691                     _this.fireEvent('years', _this, years);
28692                     return years;
28693                 })(),
28694                 fields : [ 'value' ]
28695             }),
28696             listeners : {
28697                 select : function (_self, record, index)
28698                 {
28699                     _this.setValue(_this.getValue());
28700                 }
28701             }
28702         });
28703
28704         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28705     },
28706     
28707     setValue : function(v, format)
28708     {
28709         this.inputEl.dom.value = v;
28710         
28711         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28712         
28713         var d = Date.parseDate(v, f);
28714         
28715         if(!d){
28716             this.validate();
28717             return;
28718         }
28719         
28720         this.setDay(d.format(this.dayFormat));
28721         this.setMonth(d.format(this.monthFormat));
28722         this.setYear(d.format(this.yearFormat));
28723         
28724         this.validate();
28725         
28726         return;
28727     },
28728     
28729     setDay : function(v)
28730     {
28731         this.dayField.setValue(v);
28732         this.inputEl.dom.value = this.getValue();
28733         this.validate();
28734         return;
28735     },
28736     
28737     setMonth : function(v)
28738     {
28739         this.monthField.setValue(v, true);
28740         this.inputEl.dom.value = this.getValue();
28741         this.validate();
28742         return;
28743     },
28744     
28745     setYear : function(v)
28746     {
28747         this.yearField.setValue(v);
28748         this.inputEl.dom.value = this.getValue();
28749         this.validate();
28750         return;
28751     },
28752     
28753     getDay : function()
28754     {
28755         return this.dayField.getValue();
28756     },
28757     
28758     getMonth : function()
28759     {
28760         return this.monthField.getValue();
28761     },
28762     
28763     getYear : function()
28764     {
28765         return this.yearField.getValue();
28766     },
28767     
28768     getValue : function()
28769     {
28770         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28771         
28772         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28773         
28774         return date;
28775     },
28776     
28777     reset : function()
28778     {
28779         this.setDay('');
28780         this.setMonth('');
28781         this.setYear('');
28782         this.inputEl.dom.value = '';
28783         this.validate();
28784         return;
28785     },
28786     
28787     validate : function()
28788     {
28789         var d = this.dayField.validate();
28790         var m = this.monthField.validate();
28791         var y = this.yearField.validate();
28792         
28793         var valid = true;
28794         
28795         if(
28796                 (!this.dayAllowBlank && !d) ||
28797                 (!this.monthAllowBlank && !m) ||
28798                 (!this.yearAllowBlank && !y)
28799         ){
28800             valid = false;
28801         }
28802         
28803         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28804             return valid;
28805         }
28806         
28807         if(valid){
28808             this.markValid();
28809             return valid;
28810         }
28811         
28812         this.markInvalid();
28813         
28814         return valid;
28815     },
28816     
28817     markValid : function()
28818     {
28819         
28820         var label = this.el.select('label', true).first();
28821         var icon = this.el.select('i.fa-star', true).first();
28822
28823         if(label && icon){
28824             icon.remove();
28825         }
28826         
28827         this.fireEvent('valid', this);
28828     },
28829     
28830      /**
28831      * Mark this field as invalid
28832      * @param {String} msg The validation message
28833      */
28834     markInvalid : function(msg)
28835     {
28836         
28837         var label = this.el.select('label', true).first();
28838         var icon = this.el.select('i.fa-star', true).first();
28839
28840         if(label && !icon){
28841             this.el.select('.roo-date-split-field-label', true).createChild({
28842                 tag : 'i',
28843                 cls : 'text-danger fa fa-lg fa-star',
28844                 tooltip : 'This field is required',
28845                 style : 'margin-right:5px;'
28846             }, label, true);
28847         }
28848         
28849         this.fireEvent('invalid', this, msg);
28850     },
28851     
28852     clearInvalid : function()
28853     {
28854         var label = this.el.select('label', true).first();
28855         var icon = this.el.select('i.fa-star', true).first();
28856
28857         if(label && icon){
28858             icon.remove();
28859         }
28860         
28861         this.fireEvent('valid', this);
28862     },
28863     
28864     getName: function()
28865     {
28866         return this.name;
28867     }
28868     
28869 });
28870
28871  /**
28872  *
28873  * This is based on 
28874  * http://masonry.desandro.com
28875  *
28876  * The idea is to render all the bricks based on vertical width...
28877  *
28878  * The original code extends 'outlayer' - we might need to use that....
28879  * 
28880  */
28881
28882
28883 /**
28884  * @class Roo.bootstrap.LayoutMasonry
28885  * @extends Roo.bootstrap.Component
28886  * Bootstrap Layout Masonry class
28887  * 
28888  * @constructor
28889  * Create a new Element
28890  * @param {Object} config The config object
28891  */
28892
28893 Roo.bootstrap.LayoutMasonry = function(config){
28894     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28895     
28896     this.bricks = [];
28897     
28898 };
28899
28900 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28901     
28902     /**
28903      * @cfg {Boolean} isLayoutInstant = no animation?
28904      */   
28905     isLayoutInstant : false, // needed?
28906    
28907     /**
28908      * @cfg {Number} boxWidth  width of the columns
28909      */   
28910     boxWidth : 450,
28911     
28912       /**
28913      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28914      */   
28915     boxHeight : 0,
28916     
28917     /**
28918      * @cfg {Number} padWidth padding below box..
28919      */   
28920     padWidth : 10, 
28921     
28922     /**
28923      * @cfg {Number} gutter gutter width..
28924      */   
28925     gutter : 10,
28926     
28927      /**
28928      * @cfg {Number} maxCols maximum number of columns
28929      */   
28930     
28931     maxCols: 0,
28932     
28933     /**
28934      * @cfg {Boolean} isAutoInitial defalut true
28935      */   
28936     isAutoInitial : true, 
28937     
28938     containerWidth: 0,
28939     
28940     /**
28941      * @cfg {Boolean} isHorizontal defalut false
28942      */   
28943     isHorizontal : false, 
28944
28945     currentSize : null,
28946     
28947     tag: 'div',
28948     
28949     cls: '',
28950     
28951     bricks: null, //CompositeElement
28952     
28953     cols : 1,
28954     
28955     _isLayoutInited : false,
28956     
28957 //    isAlternative : false, // only use for vertical layout...
28958     
28959     /**
28960      * @cfg {Number} alternativePadWidth padding below box..
28961      */   
28962     alternativePadWidth : 50, 
28963     
28964     getAutoCreate : function(){
28965         
28966         var cfg = {
28967             tag: this.tag,
28968             cls: 'blog-masonary-wrapper ' + this.cls,
28969             cn : {
28970                 cls : 'mas-boxes masonary'
28971             }
28972         };
28973         
28974         return cfg;
28975     },
28976     
28977     getChildContainer: function( )
28978     {
28979         if (this.boxesEl) {
28980             return this.boxesEl;
28981         }
28982         
28983         this.boxesEl = this.el.select('.mas-boxes').first();
28984         
28985         return this.boxesEl;
28986     },
28987     
28988     
28989     initEvents : function()
28990     {
28991         var _this = this;
28992         
28993         if(this.isAutoInitial){
28994             Roo.log('hook children rendered');
28995             this.on('childrenrendered', function() {
28996                 Roo.log('children rendered');
28997                 _this.initial();
28998             } ,this);
28999         }
29000     },
29001     
29002     initial : function()
29003     {
29004         this.currentSize = this.el.getBox(true);
29005         
29006         Roo.EventManager.onWindowResize(this.resize, this); 
29007
29008         if(!this.isAutoInitial){
29009             this.layout();
29010             return;
29011         }
29012         
29013         this.layout();
29014         
29015         return;
29016         //this.layout.defer(500,this);
29017         
29018     },
29019     
29020     resize : function()
29021     {
29022         Roo.log('resize');
29023         
29024         var cs = this.el.getBox(true);
29025         
29026         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29027             Roo.log("no change in with or X");
29028             return;
29029         }
29030         
29031         this.currentSize = cs;
29032         
29033         this.layout();
29034         
29035     },
29036     
29037     layout : function()
29038     {   
29039         this._resetLayout();
29040         
29041         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29042         
29043         this.layoutItems( isInstant );
29044       
29045         this._isLayoutInited = true;
29046         
29047     },
29048     
29049     _resetLayout : function()
29050     {
29051         if(this.isHorizontal){
29052             this.horizontalMeasureColumns();
29053             return;
29054         }
29055         
29056         this.verticalMeasureColumns();
29057         
29058     },
29059     
29060     verticalMeasureColumns : function()
29061     {
29062         this.getContainerWidth();
29063         
29064 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29065 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29066 //            return;
29067 //        }
29068         
29069         var boxWidth = this.boxWidth + this.padWidth;
29070         
29071         if(this.containerWidth < this.boxWidth){
29072             boxWidth = this.containerWidth
29073         }
29074         
29075         var containerWidth = this.containerWidth;
29076         
29077         var cols = Math.floor(containerWidth / boxWidth);
29078         
29079         this.cols = Math.max( cols, 1 );
29080         
29081         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29082         
29083         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29084         
29085         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29086         
29087         this.colWidth = boxWidth + avail - this.padWidth;
29088         
29089         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29090         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29091     },
29092     
29093     horizontalMeasureColumns : function()
29094     {
29095         this.getContainerWidth();
29096         
29097         var boxWidth = this.boxWidth;
29098         
29099         if(this.containerWidth < boxWidth){
29100             boxWidth = this.containerWidth;
29101         }
29102         
29103         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29104         
29105         this.el.setHeight(boxWidth);
29106         
29107     },
29108     
29109     getContainerWidth : function()
29110     {
29111         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29112     },
29113     
29114     layoutItems : function( isInstant )
29115     {
29116         var items = Roo.apply([], this.bricks);
29117         
29118         if(this.isHorizontal){
29119             this._horizontalLayoutItems( items , isInstant );
29120             return;
29121         }
29122         
29123 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29124 //            this._verticalAlternativeLayoutItems( items , isInstant );
29125 //            return;
29126 //        }
29127         
29128         this._verticalLayoutItems( items , isInstant );
29129         
29130     },
29131     
29132     _verticalLayoutItems : function ( items , isInstant)
29133     {
29134         if ( !items || !items.length ) {
29135             return;
29136         }
29137         
29138         var standard = [
29139             ['xs', 'xs', 'xs', 'tall'],
29140             ['xs', 'xs', 'tall'],
29141             ['xs', 'xs', 'sm'],
29142             ['xs', 'xs', 'xs'],
29143             ['xs', 'tall'],
29144             ['xs', 'sm'],
29145             ['xs', 'xs'],
29146             ['xs'],
29147             
29148             ['sm', 'xs', 'xs'],
29149             ['sm', 'xs'],
29150             ['sm'],
29151             
29152             ['tall', 'xs', 'xs', 'xs'],
29153             ['tall', 'xs', 'xs'],
29154             ['tall', 'xs'],
29155             ['tall']
29156             
29157         ];
29158         
29159         var queue = [];
29160         
29161         var boxes = [];
29162         
29163         var box = [];
29164         
29165         Roo.each(items, function(item, k){
29166             
29167             switch (item.size) {
29168                 // these layouts take up a full box,
29169                 case 'md' :
29170                 case 'md-left' :
29171                 case 'md-right' :
29172                 case 'wide' :
29173                     
29174                     if(box.length){
29175                         boxes.push(box);
29176                         box = [];
29177                     }
29178                     
29179                     boxes.push([item]);
29180                     
29181                     break;
29182                     
29183                 case 'xs' :
29184                 case 'sm' :
29185                 case 'tall' :
29186                     
29187                     box.push(item);
29188                     
29189                     break;
29190                 default :
29191                     break;
29192                     
29193             }
29194             
29195         }, this);
29196         
29197         if(box.length){
29198             boxes.push(box);
29199             box = [];
29200         }
29201         
29202         var filterPattern = function(box, length)
29203         {
29204             if(!box.length){
29205                 return;
29206             }
29207             
29208             var match = false;
29209             
29210             var pattern = box.slice(0, length);
29211             
29212             var format = [];
29213             
29214             Roo.each(pattern, function(i){
29215                 format.push(i.size);
29216             }, this);
29217             
29218             Roo.each(standard, function(s){
29219                 
29220                 if(String(s) != String(format)){
29221                     return;
29222                 }
29223                 
29224                 match = true;
29225                 return false;
29226                 
29227             }, this);
29228             
29229             if(!match && length == 1){
29230                 return;
29231             }
29232             
29233             if(!match){
29234                 filterPattern(box, length - 1);
29235                 return;
29236             }
29237                 
29238             queue.push(pattern);
29239
29240             box = box.slice(length, box.length);
29241
29242             filterPattern(box, 4);
29243
29244             return;
29245             
29246         }
29247         
29248         Roo.each(boxes, function(box, k){
29249             
29250             if(!box.length){
29251                 return;
29252             }
29253             
29254             if(box.length == 1){
29255                 queue.push(box);
29256                 return;
29257             }
29258             
29259             filterPattern(box, 4);
29260             
29261         }, this);
29262         
29263         this._processVerticalLayoutQueue( queue, isInstant );
29264         
29265     },
29266     
29267 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29268 //    {
29269 //        if ( !items || !items.length ) {
29270 //            return;
29271 //        }
29272 //
29273 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29274 //        
29275 //    },
29276     
29277     _horizontalLayoutItems : function ( items , isInstant)
29278     {
29279         if ( !items || !items.length || items.length < 3) {
29280             return;
29281         }
29282         
29283         items.reverse();
29284         
29285         var eItems = items.slice(0, 3);
29286         
29287         items = items.slice(3, items.length);
29288         
29289         var standard = [
29290             ['xs', 'xs', 'xs', 'wide'],
29291             ['xs', 'xs', 'wide'],
29292             ['xs', 'xs', 'sm'],
29293             ['xs', 'xs', 'xs'],
29294             ['xs', 'wide'],
29295             ['xs', 'sm'],
29296             ['xs', 'xs'],
29297             ['xs'],
29298             
29299             ['sm', 'xs', 'xs'],
29300             ['sm', 'xs'],
29301             ['sm'],
29302             
29303             ['wide', 'xs', 'xs', 'xs'],
29304             ['wide', 'xs', 'xs'],
29305             ['wide', 'xs'],
29306             ['wide'],
29307             
29308             ['wide-thin']
29309         ];
29310         
29311         var queue = [];
29312         
29313         var boxes = [];
29314         
29315         var box = [];
29316         
29317         Roo.each(items, function(item, k){
29318             
29319             switch (item.size) {
29320                 case 'md' :
29321                 case 'md-left' :
29322                 case 'md-right' :
29323                 case 'tall' :
29324                     
29325                     if(box.length){
29326                         boxes.push(box);
29327                         box = [];
29328                     }
29329                     
29330                     boxes.push([item]);
29331                     
29332                     break;
29333                     
29334                 case 'xs' :
29335                 case 'sm' :
29336                 case 'wide' :
29337                 case 'wide-thin' :
29338                     
29339                     box.push(item);
29340                     
29341                     break;
29342                 default :
29343                     break;
29344                     
29345             }
29346             
29347         }, this);
29348         
29349         if(box.length){
29350             boxes.push(box);
29351             box = [];
29352         }
29353         
29354         var filterPattern = function(box, length)
29355         {
29356             if(!box.length){
29357                 return;
29358             }
29359             
29360             var match = false;
29361             
29362             var pattern = box.slice(0, length);
29363             
29364             var format = [];
29365             
29366             Roo.each(pattern, function(i){
29367                 format.push(i.size);
29368             }, this);
29369             
29370             Roo.each(standard, function(s){
29371                 
29372                 if(String(s) != String(format)){
29373                     return;
29374                 }
29375                 
29376                 match = true;
29377                 return false;
29378                 
29379             }, this);
29380             
29381             if(!match && length == 1){
29382                 return;
29383             }
29384             
29385             if(!match){
29386                 filterPattern(box, length - 1);
29387                 return;
29388             }
29389                 
29390             queue.push(pattern);
29391
29392             box = box.slice(length, box.length);
29393
29394             filterPattern(box, 4);
29395
29396             return;
29397             
29398         }
29399         
29400         Roo.each(boxes, function(box, k){
29401             
29402             if(!box.length){
29403                 return;
29404             }
29405             
29406             if(box.length == 1){
29407                 queue.push(box);
29408                 return;
29409             }
29410             
29411             filterPattern(box, 4);
29412             
29413         }, this);
29414         
29415         
29416         var prune = [];
29417         
29418         var pos = this.el.getBox(true);
29419         
29420         var minX = pos.x;
29421         
29422         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29423         
29424         var hit_end = false;
29425         
29426         Roo.each(queue, function(box){
29427             
29428             if(hit_end){
29429                 
29430                 Roo.each(box, function(b){
29431                 
29432                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29433                     b.el.hide();
29434
29435                 }, this);
29436
29437                 return;
29438             }
29439             
29440             var mx = 0;
29441             
29442             Roo.each(box, function(b){
29443                 
29444                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29445                 b.el.show();
29446
29447                 mx = Math.max(mx, b.x);
29448                 
29449             }, this);
29450             
29451             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29452             
29453             if(maxX < minX){
29454                 
29455                 Roo.each(box, function(b){
29456                 
29457                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29458                     b.el.hide();
29459                     
29460                 }, this);
29461                 
29462                 hit_end = true;
29463                 
29464                 return;
29465             }
29466             
29467             prune.push(box);
29468             
29469         }, this);
29470         
29471         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29472     },
29473     
29474     /** Sets position of item in DOM
29475     * @param {Element} item
29476     * @param {Number} x - horizontal position
29477     * @param {Number} y - vertical position
29478     * @param {Boolean} isInstant - disables transitions
29479     */
29480     _processVerticalLayoutQueue : function( queue, isInstant )
29481     {
29482         var pos = this.el.getBox(true);
29483         var x = pos.x;
29484         var y = pos.y;
29485         var maxY = [];
29486         
29487         for (var i = 0; i < this.cols; i++){
29488             maxY[i] = pos.y;
29489         }
29490         
29491         Roo.each(queue, function(box, k){
29492             
29493             var col = k % this.cols;
29494             
29495             Roo.each(box, function(b,kk){
29496                 
29497                 b.el.position('absolute');
29498                 
29499                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29500                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29501                 
29502                 if(b.size == 'md-left' || b.size == 'md-right'){
29503                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29504                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29505                 }
29506                 
29507                 b.el.setWidth(width);
29508                 b.el.setHeight(height);
29509                 // iframe?
29510                 b.el.select('iframe',true).setSize(width,height);
29511                 
29512             }, this);
29513             
29514             for (var i = 0; i < this.cols; i++){
29515                 
29516                 if(maxY[i] < maxY[col]){
29517                     col = i;
29518                     continue;
29519                 }
29520                 
29521                 col = Math.min(col, i);
29522                 
29523             }
29524             
29525             x = pos.x + col * (this.colWidth + this.padWidth);
29526             
29527             y = maxY[col];
29528             
29529             var positions = [];
29530             
29531             switch (box.length){
29532                 case 1 :
29533                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29534                     break;
29535                 case 2 :
29536                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29537                     break;
29538                 case 3 :
29539                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29540                     break;
29541                 case 4 :
29542                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29543                     break;
29544                 default :
29545                     break;
29546             }
29547             
29548             Roo.each(box, function(b,kk){
29549                 
29550                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29551                 
29552                 var sz = b.el.getSize();
29553                 
29554                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29555                 
29556             }, this);
29557             
29558         }, this);
29559         
29560         var mY = 0;
29561         
29562         for (var i = 0; i < this.cols; i++){
29563             mY = Math.max(mY, maxY[i]);
29564         }
29565         
29566         this.el.setHeight(mY - pos.y);
29567         
29568     },
29569     
29570 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29571 //    {
29572 //        var pos = this.el.getBox(true);
29573 //        var x = pos.x;
29574 //        var y = pos.y;
29575 //        var maxX = pos.right;
29576 //        
29577 //        var maxHeight = 0;
29578 //        
29579 //        Roo.each(items, function(item, k){
29580 //            
29581 //            var c = k % 2;
29582 //            
29583 //            item.el.position('absolute');
29584 //                
29585 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29586 //
29587 //            item.el.setWidth(width);
29588 //
29589 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29590 //
29591 //            item.el.setHeight(height);
29592 //            
29593 //            if(c == 0){
29594 //                item.el.setXY([x, y], isInstant ? false : true);
29595 //            } else {
29596 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29597 //            }
29598 //            
29599 //            y = y + height + this.alternativePadWidth;
29600 //            
29601 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29602 //            
29603 //        }, this);
29604 //        
29605 //        this.el.setHeight(maxHeight);
29606 //        
29607 //    },
29608     
29609     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29610     {
29611         var pos = this.el.getBox(true);
29612         
29613         var minX = pos.x;
29614         var minY = pos.y;
29615         
29616         var maxX = pos.right;
29617         
29618         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29619         
29620         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29621         
29622         Roo.each(queue, function(box, k){
29623             
29624             Roo.each(box, function(b, kk){
29625                 
29626                 b.el.position('absolute');
29627                 
29628                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29629                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29630                 
29631                 if(b.size == 'md-left' || b.size == 'md-right'){
29632                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29633                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29634                 }
29635                 
29636                 b.el.setWidth(width);
29637                 b.el.setHeight(height);
29638                 
29639             }, this);
29640             
29641             if(!box.length){
29642                 return;
29643             }
29644             
29645             var positions = [];
29646             
29647             switch (box.length){
29648                 case 1 :
29649                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29650                     break;
29651                 case 2 :
29652                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29653                     break;
29654                 case 3 :
29655                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29656                     break;
29657                 case 4 :
29658                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29659                     break;
29660                 default :
29661                     break;
29662             }
29663             
29664             Roo.each(box, function(b,kk){
29665                 
29666                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29667                 
29668                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29669                 
29670             }, this);
29671             
29672         }, this);
29673         
29674     },
29675     
29676     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29677     {
29678         Roo.each(eItems, function(b,k){
29679             
29680             b.size = (k == 0) ? 'sm' : 'xs';
29681             b.x = (k == 0) ? 2 : 1;
29682             b.y = (k == 0) ? 2 : 1;
29683             
29684             b.el.position('absolute');
29685             
29686             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29687                 
29688             b.el.setWidth(width);
29689             
29690             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29691             
29692             b.el.setHeight(height);
29693             
29694         }, this);
29695
29696         var positions = [];
29697         
29698         positions.push({
29699             x : maxX - this.unitWidth * 2 - this.gutter,
29700             y : minY
29701         });
29702         
29703         positions.push({
29704             x : maxX - this.unitWidth,
29705             y : minY + (this.unitWidth + this.gutter) * 2
29706         });
29707         
29708         positions.push({
29709             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29710             y : minY
29711         });
29712         
29713         Roo.each(eItems, function(b,k){
29714             
29715             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29716
29717         }, this);
29718         
29719     },
29720     
29721     getVerticalOneBoxColPositions : function(x, y, box)
29722     {
29723         var pos = [];
29724         
29725         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29726         
29727         if(box[0].size == 'md-left'){
29728             rand = 0;
29729         }
29730         
29731         if(box[0].size == 'md-right'){
29732             rand = 1;
29733         }
29734         
29735         pos.push({
29736             x : x + (this.unitWidth + this.gutter) * rand,
29737             y : y
29738         });
29739         
29740         return pos;
29741     },
29742     
29743     getVerticalTwoBoxColPositions : function(x, y, box)
29744     {
29745         var pos = [];
29746         
29747         if(box[0].size == 'xs'){
29748             
29749             pos.push({
29750                 x : x,
29751                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29752             });
29753
29754             pos.push({
29755                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29756                 y : y
29757             });
29758             
29759             return pos;
29760             
29761         }
29762         
29763         pos.push({
29764             x : x,
29765             y : y
29766         });
29767
29768         pos.push({
29769             x : x + (this.unitWidth + this.gutter) * 2,
29770             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29771         });
29772         
29773         return pos;
29774         
29775     },
29776     
29777     getVerticalThreeBoxColPositions : function(x, y, box)
29778     {
29779         var pos = [];
29780         
29781         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29782             
29783             pos.push({
29784                 x : x,
29785                 y : y
29786             });
29787
29788             pos.push({
29789                 x : x + (this.unitWidth + this.gutter) * 1,
29790                 y : y
29791             });
29792             
29793             pos.push({
29794                 x : x + (this.unitWidth + this.gutter) * 2,
29795                 y : y
29796             });
29797             
29798             return pos;
29799             
29800         }
29801         
29802         if(box[0].size == 'xs' && box[1].size == 'xs'){
29803             
29804             pos.push({
29805                 x : x,
29806                 y : y
29807             });
29808
29809             pos.push({
29810                 x : x,
29811                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29812             });
29813             
29814             pos.push({
29815                 x : x + (this.unitWidth + this.gutter) * 1,
29816                 y : y
29817             });
29818             
29819             return pos;
29820             
29821         }
29822         
29823         pos.push({
29824             x : x,
29825             y : y
29826         });
29827
29828         pos.push({
29829             x : x + (this.unitWidth + this.gutter) * 2,
29830             y : y
29831         });
29832
29833         pos.push({
29834             x : x + (this.unitWidth + this.gutter) * 2,
29835             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29836         });
29837             
29838         return pos;
29839         
29840     },
29841     
29842     getVerticalFourBoxColPositions : function(x, y, box)
29843     {
29844         var pos = [];
29845         
29846         if(box[0].size == 'xs'){
29847             
29848             pos.push({
29849                 x : x,
29850                 y : y
29851             });
29852
29853             pos.push({
29854                 x : x,
29855                 y : y + (this.unitHeight + this.gutter) * 1
29856             });
29857             
29858             pos.push({
29859                 x : x,
29860                 y : y + (this.unitHeight + this.gutter) * 2
29861             });
29862             
29863             pos.push({
29864                 x : x + (this.unitWidth + this.gutter) * 1,
29865                 y : y
29866             });
29867             
29868             return pos;
29869             
29870         }
29871         
29872         pos.push({
29873             x : x,
29874             y : y
29875         });
29876
29877         pos.push({
29878             x : x + (this.unitWidth + this.gutter) * 2,
29879             y : y
29880         });
29881
29882         pos.push({
29883             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29884             y : y + (this.unitHeight + this.gutter) * 1
29885         });
29886
29887         pos.push({
29888             x : x + (this.unitWidth + this.gutter) * 2,
29889             y : y + (this.unitWidth + this.gutter) * 2
29890         });
29891
29892         return pos;
29893         
29894     },
29895     
29896     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29897     {
29898         var pos = [];
29899         
29900         if(box[0].size == 'md-left'){
29901             pos.push({
29902                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29903                 y : minY
29904             });
29905             
29906             return pos;
29907         }
29908         
29909         if(box[0].size == 'md-right'){
29910             pos.push({
29911                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29912                 y : minY + (this.unitWidth + this.gutter) * 1
29913             });
29914             
29915             return pos;
29916         }
29917         
29918         var rand = Math.floor(Math.random() * (4 - box[0].y));
29919         
29920         pos.push({
29921             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29922             y : minY + (this.unitWidth + this.gutter) * rand
29923         });
29924         
29925         return pos;
29926         
29927     },
29928     
29929     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29930     {
29931         var pos = [];
29932         
29933         if(box[0].size == 'xs'){
29934             
29935             pos.push({
29936                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29937                 y : minY
29938             });
29939
29940             pos.push({
29941                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29942                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29943             });
29944             
29945             return pos;
29946             
29947         }
29948         
29949         pos.push({
29950             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29951             y : minY
29952         });
29953
29954         pos.push({
29955             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29956             y : minY + (this.unitWidth + this.gutter) * 2
29957         });
29958         
29959         return pos;
29960         
29961     },
29962     
29963     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29964     {
29965         var pos = [];
29966         
29967         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29968             
29969             pos.push({
29970                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29971                 y : minY
29972             });
29973
29974             pos.push({
29975                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29976                 y : minY + (this.unitWidth + this.gutter) * 1
29977             });
29978             
29979             pos.push({
29980                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29981                 y : minY + (this.unitWidth + this.gutter) * 2
29982             });
29983             
29984             return pos;
29985             
29986         }
29987         
29988         if(box[0].size == 'xs' && box[1].size == 'xs'){
29989             
29990             pos.push({
29991                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29992                 y : minY
29993             });
29994
29995             pos.push({
29996                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29997                 y : minY
29998             });
29999             
30000             pos.push({
30001                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30002                 y : minY + (this.unitWidth + this.gutter) * 1
30003             });
30004             
30005             return pos;
30006             
30007         }
30008         
30009         pos.push({
30010             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30011             y : minY
30012         });
30013
30014         pos.push({
30015             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30016             y : minY + (this.unitWidth + this.gutter) * 2
30017         });
30018
30019         pos.push({
30020             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30021             y : minY + (this.unitWidth + this.gutter) * 2
30022         });
30023             
30024         return pos;
30025         
30026     },
30027     
30028     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30029     {
30030         var pos = [];
30031         
30032         if(box[0].size == 'xs'){
30033             
30034             pos.push({
30035                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30036                 y : minY
30037             });
30038
30039             pos.push({
30040                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30041                 y : minY
30042             });
30043             
30044             pos.push({
30045                 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),
30046                 y : minY
30047             });
30048             
30049             pos.push({
30050                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30051                 y : minY + (this.unitWidth + this.gutter) * 1
30052             });
30053             
30054             return pos;
30055             
30056         }
30057         
30058         pos.push({
30059             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30060             y : minY
30061         });
30062         
30063         pos.push({
30064             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30065             y : minY + (this.unitWidth + this.gutter) * 2
30066         });
30067         
30068         pos.push({
30069             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30070             y : minY + (this.unitWidth + this.gutter) * 2
30071         });
30072         
30073         pos.push({
30074             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),
30075             y : minY + (this.unitWidth + this.gutter) * 2
30076         });
30077
30078         return pos;
30079         
30080     }
30081     
30082 });
30083
30084  
30085
30086  /**
30087  *
30088  * This is based on 
30089  * http://masonry.desandro.com
30090  *
30091  * The idea is to render all the bricks based on vertical width...
30092  *
30093  * The original code extends 'outlayer' - we might need to use that....
30094  * 
30095  */
30096
30097
30098 /**
30099  * @class Roo.bootstrap.LayoutMasonryAuto
30100  * @extends Roo.bootstrap.Component
30101  * Bootstrap Layout Masonry class
30102  * 
30103  * @constructor
30104  * Create a new Element
30105  * @param {Object} config The config object
30106  */
30107
30108 Roo.bootstrap.LayoutMasonryAuto = function(config){
30109     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30110 };
30111
30112 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30113     
30114       /**
30115      * @cfg {Boolean} isFitWidth  - resize the width..
30116      */   
30117     isFitWidth : false,  // options..
30118     /**
30119      * @cfg {Boolean} isOriginLeft = left align?
30120      */   
30121     isOriginLeft : true,
30122     /**
30123      * @cfg {Boolean} isOriginTop = top align?
30124      */   
30125     isOriginTop : false,
30126     /**
30127      * @cfg {Boolean} isLayoutInstant = no animation?
30128      */   
30129     isLayoutInstant : false, // needed?
30130     /**
30131      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30132      */   
30133     isResizingContainer : true,
30134     /**
30135      * @cfg {Number} columnWidth  width of the columns 
30136      */   
30137     
30138     columnWidth : 0,
30139     
30140     /**
30141      * @cfg {Number} maxCols maximum number of columns
30142      */   
30143     
30144     maxCols: 0,
30145     /**
30146      * @cfg {Number} padHeight padding below box..
30147      */   
30148     
30149     padHeight : 10, 
30150     
30151     /**
30152      * @cfg {Boolean} isAutoInitial defalut true
30153      */   
30154     
30155     isAutoInitial : true, 
30156     
30157     // private?
30158     gutter : 0,
30159     
30160     containerWidth: 0,
30161     initialColumnWidth : 0,
30162     currentSize : null,
30163     
30164     colYs : null, // array.
30165     maxY : 0,
30166     padWidth: 10,
30167     
30168     
30169     tag: 'div',
30170     cls: '',
30171     bricks: null, //CompositeElement
30172     cols : 0, // array?
30173     // element : null, // wrapped now this.el
30174     _isLayoutInited : null, 
30175     
30176     
30177     getAutoCreate : function(){
30178         
30179         var cfg = {
30180             tag: this.tag,
30181             cls: 'blog-masonary-wrapper ' + this.cls,
30182             cn : {
30183                 cls : 'mas-boxes masonary'
30184             }
30185         };
30186         
30187         return cfg;
30188     },
30189     
30190     getChildContainer: function( )
30191     {
30192         if (this.boxesEl) {
30193             return this.boxesEl;
30194         }
30195         
30196         this.boxesEl = this.el.select('.mas-boxes').first();
30197         
30198         return this.boxesEl;
30199     },
30200     
30201     
30202     initEvents : function()
30203     {
30204         var _this = this;
30205         
30206         if(this.isAutoInitial){
30207             Roo.log('hook children rendered');
30208             this.on('childrenrendered', function() {
30209                 Roo.log('children rendered');
30210                 _this.initial();
30211             } ,this);
30212         }
30213         
30214     },
30215     
30216     initial : function()
30217     {
30218         this.reloadItems();
30219
30220         this.currentSize = this.el.getBox(true);
30221
30222         /// was window resize... - let's see if this works..
30223         Roo.EventManager.onWindowResize(this.resize, this); 
30224
30225         if(!this.isAutoInitial){
30226             this.layout();
30227             return;
30228         }
30229         
30230         this.layout.defer(500,this);
30231     },
30232     
30233     reloadItems: function()
30234     {
30235         this.bricks = this.el.select('.masonry-brick', true);
30236         
30237         this.bricks.each(function(b) {
30238             //Roo.log(b.getSize());
30239             if (!b.attr('originalwidth')) {
30240                 b.attr('originalwidth',  b.getSize().width);
30241             }
30242             
30243         });
30244         
30245         Roo.log(this.bricks.elements.length);
30246     },
30247     
30248     resize : function()
30249     {
30250         Roo.log('resize');
30251         var cs = this.el.getBox(true);
30252         
30253         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30254             Roo.log("no change in with or X");
30255             return;
30256         }
30257         this.currentSize = cs;
30258         this.layout();
30259     },
30260     
30261     layout : function()
30262     {
30263          Roo.log('layout');
30264         this._resetLayout();
30265         //this._manageStamps();
30266       
30267         // don't animate first layout
30268         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30269         this.layoutItems( isInstant );
30270       
30271         // flag for initalized
30272         this._isLayoutInited = true;
30273     },
30274     
30275     layoutItems : function( isInstant )
30276     {
30277         //var items = this._getItemsForLayout( this.items );
30278         // original code supports filtering layout items.. we just ignore it..
30279         
30280         this._layoutItems( this.bricks , isInstant );
30281       
30282         this._postLayout();
30283     },
30284     _layoutItems : function ( items , isInstant)
30285     {
30286        //this.fireEvent( 'layout', this, items );
30287     
30288
30289         if ( !items || !items.elements.length ) {
30290           // no items, emit event with empty array
30291             return;
30292         }
30293
30294         var queue = [];
30295         items.each(function(item) {
30296             Roo.log("layout item");
30297             Roo.log(item);
30298             // get x/y object from method
30299             var position = this._getItemLayoutPosition( item );
30300             // enqueue
30301             position.item = item;
30302             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30303             queue.push( position );
30304         }, this);
30305       
30306         this._processLayoutQueue( queue );
30307     },
30308     /** Sets position of item in DOM
30309     * @param {Element} item
30310     * @param {Number} x - horizontal position
30311     * @param {Number} y - vertical position
30312     * @param {Boolean} isInstant - disables transitions
30313     */
30314     _processLayoutQueue : function( queue )
30315     {
30316         for ( var i=0, len = queue.length; i < len; i++ ) {
30317             var obj = queue[i];
30318             obj.item.position('absolute');
30319             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30320         }
30321     },
30322       
30323     
30324     /**
30325     * Any logic you want to do after each layout,
30326     * i.e. size the container
30327     */
30328     _postLayout : function()
30329     {
30330         this.resizeContainer();
30331     },
30332     
30333     resizeContainer : function()
30334     {
30335         if ( !this.isResizingContainer ) {
30336             return;
30337         }
30338         var size = this._getContainerSize();
30339         if ( size ) {
30340             this.el.setSize(size.width,size.height);
30341             this.boxesEl.setSize(size.width,size.height);
30342         }
30343     },
30344     
30345     
30346     
30347     _resetLayout : function()
30348     {
30349         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30350         this.colWidth = this.el.getWidth();
30351         //this.gutter = this.el.getWidth(); 
30352         
30353         this.measureColumns();
30354
30355         // reset column Y
30356         var i = this.cols;
30357         this.colYs = [];
30358         while (i--) {
30359             this.colYs.push( 0 );
30360         }
30361     
30362         this.maxY = 0;
30363     },
30364
30365     measureColumns : function()
30366     {
30367         this.getContainerWidth();
30368       // if columnWidth is 0, default to outerWidth of first item
30369         if ( !this.columnWidth ) {
30370             var firstItem = this.bricks.first();
30371             Roo.log(firstItem);
30372             this.columnWidth  = this.containerWidth;
30373             if (firstItem && firstItem.attr('originalwidth') ) {
30374                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30375             }
30376             // columnWidth fall back to item of first element
30377             Roo.log("set column width?");
30378                         this.initialColumnWidth = this.columnWidth  ;
30379
30380             // if first elem has no width, default to size of container
30381             
30382         }
30383         
30384         
30385         if (this.initialColumnWidth) {
30386             this.columnWidth = this.initialColumnWidth;
30387         }
30388         
30389         
30390             
30391         // column width is fixed at the top - however if container width get's smaller we should
30392         // reduce it...
30393         
30394         // this bit calcs how man columns..
30395             
30396         var columnWidth = this.columnWidth += this.gutter;
30397       
30398         // calculate columns
30399         var containerWidth = this.containerWidth + this.gutter;
30400         
30401         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30402         // fix rounding errors, typically with gutters
30403         var excess = columnWidth - containerWidth % columnWidth;
30404         
30405         
30406         // if overshoot is less than a pixel, round up, otherwise floor it
30407         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30408         cols = Math[ mathMethod ]( cols );
30409         this.cols = Math.max( cols, 1 );
30410         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30411         
30412          // padding positioning..
30413         var totalColWidth = this.cols * this.columnWidth;
30414         var padavail = this.containerWidth - totalColWidth;
30415         // so for 2 columns - we need 3 'pads'
30416         
30417         var padNeeded = (1+this.cols) * this.padWidth;
30418         
30419         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30420         
30421         this.columnWidth += padExtra
30422         //this.padWidth = Math.floor(padavail /  ( this.cols));
30423         
30424         // adjust colum width so that padding is fixed??
30425         
30426         // we have 3 columns ... total = width * 3
30427         // we have X left over... that should be used by 
30428         
30429         //if (this.expandC) {
30430             
30431         //}
30432         
30433         
30434         
30435     },
30436     
30437     getContainerWidth : function()
30438     {
30439        /* // container is parent if fit width
30440         var container = this.isFitWidth ? this.element.parentNode : this.element;
30441         // check that this.size and size are there
30442         // IE8 triggers resize on body size change, so they might not be
30443         
30444         var size = getSize( container );  //FIXME
30445         this.containerWidth = size && size.innerWidth; //FIXME
30446         */
30447          
30448         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30449         
30450     },
30451     
30452     _getItemLayoutPosition : function( item )  // what is item?
30453     {
30454         // we resize the item to our columnWidth..
30455       
30456         item.setWidth(this.columnWidth);
30457         item.autoBoxAdjust  = false;
30458         
30459         var sz = item.getSize();
30460  
30461         // how many columns does this brick span
30462         var remainder = this.containerWidth % this.columnWidth;
30463         
30464         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30465         // round if off by 1 pixel, otherwise use ceil
30466         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30467         colSpan = Math.min( colSpan, this.cols );
30468         
30469         // normally this should be '1' as we dont' currently allow multi width columns..
30470         
30471         var colGroup = this._getColGroup( colSpan );
30472         // get the minimum Y value from the columns
30473         var minimumY = Math.min.apply( Math, colGroup );
30474         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30475         
30476         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30477          
30478         // position the brick
30479         var position = {
30480             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30481             y: this.currentSize.y + minimumY + this.padHeight
30482         };
30483         
30484         Roo.log(position);
30485         // apply setHeight to necessary columns
30486         var setHeight = minimumY + sz.height + this.padHeight;
30487         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30488         
30489         var setSpan = this.cols + 1 - colGroup.length;
30490         for ( var i = 0; i < setSpan; i++ ) {
30491           this.colYs[ shortColIndex + i ] = setHeight ;
30492         }
30493       
30494         return position;
30495     },
30496     
30497     /**
30498      * @param {Number} colSpan - number of columns the element spans
30499      * @returns {Array} colGroup
30500      */
30501     _getColGroup : function( colSpan )
30502     {
30503         if ( colSpan < 2 ) {
30504           // if brick spans only one column, use all the column Ys
30505           return this.colYs;
30506         }
30507       
30508         var colGroup = [];
30509         // how many different places could this brick fit horizontally
30510         var groupCount = this.cols + 1 - colSpan;
30511         // for each group potential horizontal position
30512         for ( var i = 0; i < groupCount; i++ ) {
30513           // make an array of colY values for that one group
30514           var groupColYs = this.colYs.slice( i, i + colSpan );
30515           // and get the max value of the array
30516           colGroup[i] = Math.max.apply( Math, groupColYs );
30517         }
30518         return colGroup;
30519     },
30520     /*
30521     _manageStamp : function( stamp )
30522     {
30523         var stampSize =  stamp.getSize();
30524         var offset = stamp.getBox();
30525         // get the columns that this stamp affects
30526         var firstX = this.isOriginLeft ? offset.x : offset.right;
30527         var lastX = firstX + stampSize.width;
30528         var firstCol = Math.floor( firstX / this.columnWidth );
30529         firstCol = Math.max( 0, firstCol );
30530         
30531         var lastCol = Math.floor( lastX / this.columnWidth );
30532         // lastCol should not go over if multiple of columnWidth #425
30533         lastCol -= lastX % this.columnWidth ? 0 : 1;
30534         lastCol = Math.min( this.cols - 1, lastCol );
30535         
30536         // set colYs to bottom of the stamp
30537         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30538             stampSize.height;
30539             
30540         for ( var i = firstCol; i <= lastCol; i++ ) {
30541           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30542         }
30543     },
30544     */
30545     
30546     _getContainerSize : function()
30547     {
30548         this.maxY = Math.max.apply( Math, this.colYs );
30549         var size = {
30550             height: this.maxY
30551         };
30552       
30553         if ( this.isFitWidth ) {
30554             size.width = this._getContainerFitWidth();
30555         }
30556       
30557         return size;
30558     },
30559     
30560     _getContainerFitWidth : function()
30561     {
30562         var unusedCols = 0;
30563         // count unused columns
30564         var i = this.cols;
30565         while ( --i ) {
30566           if ( this.colYs[i] !== 0 ) {
30567             break;
30568           }
30569           unusedCols++;
30570         }
30571         // fit container to columns that have been used
30572         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30573     },
30574     
30575     needsResizeLayout : function()
30576     {
30577         var previousWidth = this.containerWidth;
30578         this.getContainerWidth();
30579         return previousWidth !== this.containerWidth;
30580     }
30581  
30582 });
30583
30584  
30585
30586  /*
30587  * - LGPL
30588  *
30589  * element
30590  * 
30591  */
30592
30593 /**
30594  * @class Roo.bootstrap.MasonryBrick
30595  * @extends Roo.bootstrap.Component
30596  * Bootstrap MasonryBrick class
30597  * 
30598  * @constructor
30599  * Create a new MasonryBrick
30600  * @param {Object} config The config object
30601  */
30602
30603 Roo.bootstrap.MasonryBrick = function(config){
30604     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30605     
30606     this.addEvents({
30607         // raw events
30608         /**
30609          * @event click
30610          * When a MasonryBrick is clcik
30611          * @param {Roo.bootstrap.MasonryBrick} this
30612          * @param {Roo.EventObject} e
30613          */
30614         "click" : true
30615     });
30616 };
30617
30618 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30619     
30620     /**
30621      * @cfg {String} title
30622      */   
30623     title : '',
30624     /**
30625      * @cfg {String} html
30626      */   
30627     html : '',
30628     /**
30629      * @cfg {String} bgimage
30630      */   
30631     bgimage : '',
30632     /**
30633      * @cfg {String} videourl
30634      */   
30635     videourl : '',
30636     /**
30637      * @cfg {String} cls
30638      */   
30639     cls : '',
30640     /**
30641      * @cfg {String} href
30642      */   
30643     href : '',
30644     /**
30645      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30646      */   
30647     size : 'xs',
30648     
30649     /**
30650      * @cfg {String} (center|bottom) placetitle
30651      */   
30652     placetitle : '',
30653     
30654     getAutoCreate : function()
30655     {
30656         var cls = 'masonry-brick';
30657         
30658         if(this.href.length){
30659             cls += ' masonry-brick-link';
30660         }
30661         
30662         if(this.bgimage.length){
30663             cls += ' masonry-brick-image';
30664         }
30665         
30666         if(this.size){
30667             cls += ' masonry-' + this.size + '-brick';
30668         }
30669         
30670         if(this.placetitle.length){
30671             
30672             switch (this.placetitle) {
30673                 case 'center' :
30674                     cls += ' masonry-center-title';
30675                     break;
30676                 case 'bottom' :
30677                     cls += ' masonry-bottom-title';
30678                     break;
30679                 default:
30680                     break;
30681             }
30682             
30683         } else {
30684             if(!this.html.length && !this.bgimage.length){
30685                 cls += ' masonry-center-title';
30686             }
30687
30688             if(!this.html.length && this.bgimage.length){
30689                 cls += ' masonry-bottom-title';
30690             }
30691         }
30692         
30693         if(this.cls){
30694             cls += ' ' + this.cls;
30695         }
30696         
30697         var cfg = {
30698             tag: (this.href.length) ? 'a' : 'div',
30699             cls: cls,
30700             cn: [
30701                 {
30702                     tag: 'div',
30703                     cls: 'masonry-brick-paragraph',
30704                     cn: []
30705                 }
30706             ]
30707         };
30708         
30709         if(this.href.length){
30710             cfg.href = this.href;
30711         }
30712         
30713         var cn = cfg.cn[0].cn;
30714         
30715         if(this.title.length){
30716             cn.push({
30717                 tag: 'h4',
30718                 cls: 'masonry-brick-title',
30719                 html: this.title
30720             });
30721         }
30722         
30723         if(this.html.length){
30724             cn.push({
30725                 tag: 'p',
30726                 cls: 'masonry-brick-text',
30727                 html: this.html
30728             });
30729         }  
30730         if (!this.title.length && !this.html.length) {
30731             cfg.cn[0].cls += ' hide';
30732         }
30733         
30734         if(this.bgimage.length){
30735             cfg.cn.push({
30736                 tag: 'img',
30737                 cls: 'masonry-brick-image-view',
30738                 src: this.bgimage
30739             });
30740         }
30741         if(this.videourl.length){
30742             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30743             // youtube support only?
30744             cfg.cn.push({
30745                 tag: 'iframe',
30746                 cls: 'masonry-brick-image-view',
30747                 src: vurl,
30748                 frameborder : 0,
30749                 allowfullscreen : true
30750             });
30751             
30752             
30753         }
30754         return cfg;
30755         
30756     },
30757     
30758     initEvents: function() 
30759     {
30760         switch (this.size) {
30761             case 'xs' :
30762 //                this.intSize = 1;
30763                 this.x = 1;
30764                 this.y = 1;
30765                 break;
30766             case 'sm' :
30767 //                this.intSize = 2;
30768                 this.x = 2;
30769                 this.y = 2;
30770                 break;
30771             case 'md' :
30772             case 'md-left' :
30773             case 'md-right' :
30774 //                this.intSize = 3;
30775                 this.x = 3;
30776                 this.y = 3;
30777                 break;
30778             case 'tall' :
30779 //                this.intSize = 3;
30780                 this.x = 2;
30781                 this.y = 3;
30782                 break;
30783             case 'wide' :
30784 //                this.intSize = 3;
30785                 this.x = 3;
30786                 this.y = 2;
30787                 break;
30788             case 'wide-thin' :
30789 //                this.intSize = 3;
30790                 this.x = 3;
30791                 this.y = 1;
30792                 break;
30793                         
30794             default :
30795                 break;
30796         }
30797         
30798         
30799         
30800         if(Roo.isTouch){
30801             this.el.on('touchstart', this.onTouchStart, this);
30802             this.el.on('touchmove', this.onTouchMove, this);
30803             this.el.on('touchend', this.onTouchEnd, this);
30804             this.el.on('contextmenu', this.onContextMenu, this);
30805         } else {
30806             this.el.on('mouseenter'  ,this.enter, this);
30807             this.el.on('mouseleave', this.leave, this);
30808         }
30809         
30810         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30811             this.parent().bricks.push(this);   
30812         }
30813         
30814     },
30815     
30816     onClick: function(e, el)
30817     {
30818         if(!Roo.isTouch){
30819             return;
30820         }
30821         
30822         var time = this.endTimer - this.startTimer;
30823         
30824         //alert(time);
30825         
30826         if(time < 1000){
30827             return;
30828         }
30829         
30830         e.preventDefault();
30831     },
30832     
30833     enter: function(e, el)
30834     {
30835         e.preventDefault();
30836         
30837         if(this.bgimage.length && this.html.length){
30838             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30839         }
30840     },
30841     
30842     leave: function(e, el)
30843     {
30844         e.preventDefault();
30845         
30846         if(this.bgimage.length && this.html.length){
30847             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30848         }
30849     },
30850     
30851     onTouchStart: function(e, el)
30852     {
30853 //        e.preventDefault();
30854         
30855         this.touchmoved = false;
30856         
30857         if(!this.bgimage.length || !this.html.length){
30858             return;
30859         }
30860         
30861         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30862         
30863         this.timer = new Date().getTime();
30864         
30865     },
30866     
30867     onTouchMove: function(e, el)
30868     {
30869         this.touchmoved = true;
30870     },
30871     
30872     onContextMenu : function(e,el)
30873     {
30874         e.preventDefault();
30875         e.stopPropagation();
30876         return false;
30877     },
30878     
30879     onTouchEnd: function(e, el)
30880     {
30881 //        e.preventDefault();
30882         
30883         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30884         
30885             this.leave(e,el);
30886             
30887             return;
30888         }
30889         
30890         if(!this.bgimage.length || !this.html.length){
30891             
30892             if(this.href.length){
30893                 window.location.href = this.href;
30894             }
30895             
30896             return;
30897         }
30898         
30899         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30900         
30901         window.location.href = this.href;
30902     }
30903     
30904 });
30905
30906  
30907
30908  /*
30909  * - LGPL
30910  *
30911  * element
30912  * 
30913  */
30914
30915 /**
30916  * @class Roo.bootstrap.Brick
30917  * @extends Roo.bootstrap.Component
30918  * Bootstrap Brick class
30919  * 
30920  * @constructor
30921  * Create a new Brick
30922  * @param {Object} config The config object
30923  */
30924
30925 Roo.bootstrap.Brick = function(config){
30926     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30927     
30928     this.addEvents({
30929         // raw events
30930         /**
30931          * @event click
30932          * When a Brick is click
30933          * @param {Roo.bootstrap.Brick} this
30934          * @param {Roo.EventObject} e
30935          */
30936         "click" : true
30937     });
30938 };
30939
30940 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30941     
30942     /**
30943      * @cfg {String} title
30944      */   
30945     title : '',
30946     /**
30947      * @cfg {String} html
30948      */   
30949     html : '',
30950     /**
30951      * @cfg {String} bgimage
30952      */   
30953     bgimage : '',
30954     /**
30955      * @cfg {String} cls
30956      */   
30957     cls : '',
30958     /**
30959      * @cfg {String} href
30960      */   
30961     href : '',
30962     /**
30963      * @cfg {String} video
30964      */   
30965     video : '',
30966     /**
30967      * @cfg {Boolean} square
30968      */   
30969     square : true,
30970     
30971     getAutoCreate : function()
30972     {
30973         var cls = 'roo-brick';
30974         
30975         if(this.href.length){
30976             cls += ' roo-brick-link';
30977         }
30978         
30979         if(this.bgimage.length){
30980             cls += ' roo-brick-image';
30981         }
30982         
30983         if(!this.html.length && !this.bgimage.length){
30984             cls += ' roo-brick-center-title';
30985         }
30986         
30987         if(!this.html.length && this.bgimage.length){
30988             cls += ' roo-brick-bottom-title';
30989         }
30990         
30991         if(this.cls){
30992             cls += ' ' + this.cls;
30993         }
30994         
30995         var cfg = {
30996             tag: (this.href.length) ? 'a' : 'div',
30997             cls: cls,
30998             cn: [
30999                 {
31000                     tag: 'div',
31001                     cls: 'roo-brick-paragraph',
31002                     cn: []
31003                 }
31004             ]
31005         };
31006         
31007         if(this.href.length){
31008             cfg.href = this.href;
31009         }
31010         
31011         var cn = cfg.cn[0].cn;
31012         
31013         if(this.title.length){
31014             cn.push({
31015                 tag: 'h4',
31016                 cls: 'roo-brick-title',
31017                 html: this.title
31018             });
31019         }
31020         
31021         if(this.html.length){
31022             cn.push({
31023                 tag: 'p',
31024                 cls: 'roo-brick-text',
31025                 html: this.html
31026             });
31027         } else {
31028             cn.cls += ' hide';
31029         }
31030         
31031         if(this.bgimage.length){
31032             cfg.cn.push({
31033                 tag: 'img',
31034                 cls: 'roo-brick-image-view',
31035                 src: this.bgimage
31036             });
31037         }
31038         
31039         return cfg;
31040     },
31041     
31042     initEvents: function() 
31043     {
31044         if(this.title.length || this.html.length){
31045             this.el.on('mouseenter'  ,this.enter, this);
31046             this.el.on('mouseleave', this.leave, this);
31047         }
31048         
31049         
31050         Roo.EventManager.onWindowResize(this.resize, this); 
31051         
31052         this.resize();
31053     },
31054     
31055     resize : function()
31056     {
31057         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31058         
31059         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31060 //        paragraph.setHeight(paragraph.getWidth());
31061         
31062         if(this.bgimage.length){
31063             var image = this.el.select('.roo-brick-image-view', true).first();
31064             image.setWidth(paragraph.getWidth());
31065             image.setHeight(paragraph.getWidth());
31066         }
31067         
31068     },
31069     
31070     enter: function(e, el)
31071     {
31072         e.preventDefault();
31073         
31074         if(this.bgimage.length){
31075             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31076             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31077         }
31078     },
31079     
31080     leave: function(e, el)
31081     {
31082         e.preventDefault();
31083         
31084         if(this.bgimage.length){
31085             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31086             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31087         }
31088     }
31089     
31090 });
31091
31092  
31093
31094  /*
31095  * Based on:
31096  * Ext JS Library 1.1.1
31097  * Copyright(c) 2006-2007, Ext JS, LLC.
31098  *
31099  * Originally Released Under LGPL - original licence link has changed is not relivant.
31100  *
31101  * Fork - LGPL
31102  * <script type="text/javascript">
31103  */
31104
31105
31106 /**
31107  * @class Roo.bootstrap.SplitBar
31108  * @extends Roo.util.Observable
31109  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31110  * <br><br>
31111  * Usage:
31112  * <pre><code>
31113 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31114                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31115 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31116 split.minSize = 100;
31117 split.maxSize = 600;
31118 split.animate = true;
31119 split.on('moved', splitterMoved);
31120 </code></pre>
31121  * @constructor
31122  * Create a new SplitBar
31123  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31124  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31125  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31126  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31127                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31128                         position of the SplitBar).
31129  */
31130 Roo.bootstrap.SplitBar = function(cfg){
31131     
31132     /** @private */
31133     
31134     //{
31135     //  dragElement : elm
31136     //  resizingElement: el,
31137         // optional..
31138     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31139     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31140         // existingProxy ???
31141     //}
31142     
31143     this.el = Roo.get(cfg.dragElement, true);
31144     this.el.dom.unselectable = "on";
31145     /** @private */
31146     this.resizingEl = Roo.get(cfg.resizingElement, true);
31147
31148     /**
31149      * @private
31150      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31151      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31152      * @type Number
31153      */
31154     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31155     
31156     /**
31157      * The minimum size of the resizing element. (Defaults to 0)
31158      * @type Number
31159      */
31160     this.minSize = 0;
31161     
31162     /**
31163      * The maximum size of the resizing element. (Defaults to 2000)
31164      * @type Number
31165      */
31166     this.maxSize = 2000;
31167     
31168     /**
31169      * Whether to animate the transition to the new size
31170      * @type Boolean
31171      */
31172     this.animate = false;
31173     
31174     /**
31175      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31176      * @type Boolean
31177      */
31178     this.useShim = false;
31179     
31180     /** @private */
31181     this.shim = null;
31182     
31183     if(!cfg.existingProxy){
31184         /** @private */
31185         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31186     }else{
31187         this.proxy = Roo.get(cfg.existingProxy).dom;
31188     }
31189     /** @private */
31190     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31191     
31192     /** @private */
31193     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31194     
31195     /** @private */
31196     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31197     
31198     /** @private */
31199     this.dragSpecs = {};
31200     
31201     /**
31202      * @private The adapter to use to positon and resize elements
31203      */
31204     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31205     this.adapter.init(this);
31206     
31207     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31208         /** @private */
31209         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31210         this.el.addClass("roo-splitbar-h");
31211     }else{
31212         /** @private */
31213         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31214         this.el.addClass("roo-splitbar-v");
31215     }
31216     
31217     this.addEvents({
31218         /**
31219          * @event resize
31220          * Fires when the splitter is moved (alias for {@link #event-moved})
31221          * @param {Roo.bootstrap.SplitBar} this
31222          * @param {Number} newSize the new width or height
31223          */
31224         "resize" : true,
31225         /**
31226          * @event moved
31227          * Fires when the splitter is moved
31228          * @param {Roo.bootstrap.SplitBar} this
31229          * @param {Number} newSize the new width or height
31230          */
31231         "moved" : true,
31232         /**
31233          * @event beforeresize
31234          * Fires before the splitter is dragged
31235          * @param {Roo.bootstrap.SplitBar} this
31236          */
31237         "beforeresize" : true,
31238
31239         "beforeapply" : true
31240     });
31241
31242     Roo.util.Observable.call(this);
31243 };
31244
31245 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31246     onStartProxyDrag : function(x, y){
31247         this.fireEvent("beforeresize", this);
31248         if(!this.overlay){
31249             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31250             o.unselectable();
31251             o.enableDisplayMode("block");
31252             // all splitbars share the same overlay
31253             Roo.bootstrap.SplitBar.prototype.overlay = o;
31254         }
31255         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31256         this.overlay.show();
31257         Roo.get(this.proxy).setDisplayed("block");
31258         var size = this.adapter.getElementSize(this);
31259         this.activeMinSize = this.getMinimumSize();;
31260         this.activeMaxSize = this.getMaximumSize();;
31261         var c1 = size - this.activeMinSize;
31262         var c2 = Math.max(this.activeMaxSize - size, 0);
31263         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31264             this.dd.resetConstraints();
31265             this.dd.setXConstraint(
31266                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31267                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31268             );
31269             this.dd.setYConstraint(0, 0);
31270         }else{
31271             this.dd.resetConstraints();
31272             this.dd.setXConstraint(0, 0);
31273             this.dd.setYConstraint(
31274                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31275                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31276             );
31277          }
31278         this.dragSpecs.startSize = size;
31279         this.dragSpecs.startPoint = [x, y];
31280         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31281     },
31282     
31283     /** 
31284      * @private Called after the drag operation by the DDProxy
31285      */
31286     onEndProxyDrag : function(e){
31287         Roo.get(this.proxy).setDisplayed(false);
31288         var endPoint = Roo.lib.Event.getXY(e);
31289         if(this.overlay){
31290             this.overlay.hide();
31291         }
31292         var newSize;
31293         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31294             newSize = this.dragSpecs.startSize + 
31295                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31296                     endPoint[0] - this.dragSpecs.startPoint[0] :
31297                     this.dragSpecs.startPoint[0] - endPoint[0]
31298                 );
31299         }else{
31300             newSize = this.dragSpecs.startSize + 
31301                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31302                     endPoint[1] - this.dragSpecs.startPoint[1] :
31303                     this.dragSpecs.startPoint[1] - endPoint[1]
31304                 );
31305         }
31306         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31307         if(newSize != this.dragSpecs.startSize){
31308             if(this.fireEvent('beforeapply', this, newSize) !== false){
31309                 this.adapter.setElementSize(this, newSize);
31310                 this.fireEvent("moved", this, newSize);
31311                 this.fireEvent("resize", this, newSize);
31312             }
31313         }
31314     },
31315     
31316     /**
31317      * Get the adapter this SplitBar uses
31318      * @return The adapter object
31319      */
31320     getAdapter : function(){
31321         return this.adapter;
31322     },
31323     
31324     /**
31325      * Set the adapter this SplitBar uses
31326      * @param {Object} adapter A SplitBar adapter object
31327      */
31328     setAdapter : function(adapter){
31329         this.adapter = adapter;
31330         this.adapter.init(this);
31331     },
31332     
31333     /**
31334      * Gets the minimum size for the resizing element
31335      * @return {Number} The minimum size
31336      */
31337     getMinimumSize : function(){
31338         return this.minSize;
31339     },
31340     
31341     /**
31342      * Sets the minimum size for the resizing element
31343      * @param {Number} minSize The minimum size
31344      */
31345     setMinimumSize : function(minSize){
31346         this.minSize = minSize;
31347     },
31348     
31349     /**
31350      * Gets the maximum size for the resizing element
31351      * @return {Number} The maximum size
31352      */
31353     getMaximumSize : function(){
31354         return this.maxSize;
31355     },
31356     
31357     /**
31358      * Sets the maximum size for the resizing element
31359      * @param {Number} maxSize The maximum size
31360      */
31361     setMaximumSize : function(maxSize){
31362         this.maxSize = maxSize;
31363     },
31364     
31365     /**
31366      * Sets the initialize size for the resizing element
31367      * @param {Number} size The initial size
31368      */
31369     setCurrentSize : function(size){
31370         var oldAnimate = this.animate;
31371         this.animate = false;
31372         this.adapter.setElementSize(this, size);
31373         this.animate = oldAnimate;
31374     },
31375     
31376     /**
31377      * Destroy this splitbar. 
31378      * @param {Boolean} removeEl True to remove the element
31379      */
31380     destroy : function(removeEl){
31381         if(this.shim){
31382             this.shim.remove();
31383         }
31384         this.dd.unreg();
31385         this.proxy.parentNode.removeChild(this.proxy);
31386         if(removeEl){
31387             this.el.remove();
31388         }
31389     }
31390 });
31391
31392 /**
31393  * @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.
31394  */
31395 Roo.bootstrap.SplitBar.createProxy = function(dir){
31396     var proxy = new Roo.Element(document.createElement("div"));
31397     proxy.unselectable();
31398     var cls = 'roo-splitbar-proxy';
31399     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31400     document.body.appendChild(proxy.dom);
31401     return proxy.dom;
31402 };
31403
31404 /** 
31405  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31406  * Default Adapter. It assumes the splitter and resizing element are not positioned
31407  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31408  */
31409 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31410 };
31411
31412 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31413     // do nothing for now
31414     init : function(s){
31415     
31416     },
31417     /**
31418      * Called before drag operations to get the current size of the resizing element. 
31419      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31420      */
31421      getElementSize : function(s){
31422         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31423             return s.resizingEl.getWidth();
31424         }else{
31425             return s.resizingEl.getHeight();
31426         }
31427     },
31428     
31429     /**
31430      * Called after drag operations to set the size of the resizing element.
31431      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31432      * @param {Number} newSize The new size to set
31433      * @param {Function} onComplete A function to be invoked when resizing is complete
31434      */
31435     setElementSize : function(s, newSize, onComplete){
31436         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31437             if(!s.animate){
31438                 s.resizingEl.setWidth(newSize);
31439                 if(onComplete){
31440                     onComplete(s, newSize);
31441                 }
31442             }else{
31443                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31444             }
31445         }else{
31446             
31447             if(!s.animate){
31448                 s.resizingEl.setHeight(newSize);
31449                 if(onComplete){
31450                     onComplete(s, newSize);
31451                 }
31452             }else{
31453                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31454             }
31455         }
31456     }
31457 };
31458
31459 /** 
31460  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31461  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31462  * Adapter that  moves the splitter element to align with the resized sizing element. 
31463  * Used with an absolute positioned SplitBar.
31464  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31465  * document.body, make sure you assign an id to the body element.
31466  */
31467 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31468     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31469     this.container = Roo.get(container);
31470 };
31471
31472 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31473     init : function(s){
31474         this.basic.init(s);
31475     },
31476     
31477     getElementSize : function(s){
31478         return this.basic.getElementSize(s);
31479     },
31480     
31481     setElementSize : function(s, newSize, onComplete){
31482         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31483     },
31484     
31485     moveSplitter : function(s){
31486         var yes = Roo.bootstrap.SplitBar;
31487         switch(s.placement){
31488             case yes.LEFT:
31489                 s.el.setX(s.resizingEl.getRight());
31490                 break;
31491             case yes.RIGHT:
31492                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31493                 break;
31494             case yes.TOP:
31495                 s.el.setY(s.resizingEl.getBottom());
31496                 break;
31497             case yes.BOTTOM:
31498                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31499                 break;
31500         }
31501     }
31502 };
31503
31504 /**
31505  * Orientation constant - Create a vertical SplitBar
31506  * @static
31507  * @type Number
31508  */
31509 Roo.bootstrap.SplitBar.VERTICAL = 1;
31510
31511 /**
31512  * Orientation constant - Create a horizontal SplitBar
31513  * @static
31514  * @type Number
31515  */
31516 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31517
31518 /**
31519  * Placement constant - The resizing element is to the left of the splitter element
31520  * @static
31521  * @type Number
31522  */
31523 Roo.bootstrap.SplitBar.LEFT = 1;
31524
31525 /**
31526  * Placement constant - The resizing element is to the right of the splitter element
31527  * @static
31528  * @type Number
31529  */
31530 Roo.bootstrap.SplitBar.RIGHT = 2;
31531
31532 /**
31533  * Placement constant - The resizing element is positioned above the splitter element
31534  * @static
31535  * @type Number
31536  */
31537 Roo.bootstrap.SplitBar.TOP = 3;
31538
31539 /**
31540  * Placement constant - The resizing element is positioned under splitter element
31541  * @static
31542  * @type Number
31543  */
31544 Roo.bootstrap.SplitBar.BOTTOM = 4;
31545 Roo.namespace("Roo.bootstrap.layout");/*
31546  * Based on:
31547  * Ext JS Library 1.1.1
31548  * Copyright(c) 2006-2007, Ext JS, LLC.
31549  *
31550  * Originally Released Under LGPL - original licence link has changed is not relivant.
31551  *
31552  * Fork - LGPL
31553  * <script type="text/javascript">
31554  */
31555  
31556 /**
31557  * @class Roo.bootstrap.layout.Manager
31558  * @extends Roo.bootstrap.Component
31559  * Base class for layout managers.
31560  */
31561 Roo.bootstrap.layout.Manager = function(config)
31562 {
31563     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31564     
31565     
31566      
31567     
31568     
31569     /** false to disable window resize monitoring @type Boolean */
31570     this.monitorWindowResize = true;
31571     this.regions = {};
31572     this.addEvents({
31573         /**
31574          * @event layout
31575          * Fires when a layout is performed. 
31576          * @param {Roo.LayoutManager} this
31577          */
31578         "layout" : true,
31579         /**
31580          * @event regionresized
31581          * Fires when the user resizes a region. 
31582          * @param {Roo.LayoutRegion} region The resized region
31583          * @param {Number} newSize The new size (width for east/west, height for north/south)
31584          */
31585         "regionresized" : true,
31586         /**
31587          * @event regioncollapsed
31588          * Fires when a region is collapsed. 
31589          * @param {Roo.LayoutRegion} region The collapsed region
31590          */
31591         "regioncollapsed" : true,
31592         /**
31593          * @event regionexpanded
31594          * Fires when a region is expanded.  
31595          * @param {Roo.LayoutRegion} region The expanded region
31596          */
31597         "regionexpanded" : true
31598     });
31599     this.updating = false;
31600     
31601     if (config.el) {
31602         this.el = Roo.get(config.el);
31603         this.initEvents();
31604     }
31605     
31606 };
31607
31608 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31609     
31610     
31611     regions : null,
31612     
31613     monitorWindowResize : true,
31614     
31615     
31616     updating : false,
31617     
31618     
31619     onRender : function(ct, position)
31620     {
31621         if(!this.el){
31622             this.el = Roo.get(ct);
31623             this.initEvents();
31624         }
31625     },
31626     
31627     
31628     initEvents: function()
31629     {
31630         
31631         
31632         // ie scrollbar fix
31633         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31634             document.body.scroll = "no";
31635         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31636             this.el.position('relative');
31637         }
31638         this.id = this.el.id;
31639         this.el.addClass("roo-layout-container");
31640         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31641         if(this.el.dom != document.body ) {
31642             this.el.on('resize', this.layout,this);
31643             this.el.on('show', this.layout,this);
31644         }
31645
31646     },
31647     
31648     /**
31649      * Returns true if this layout is currently being updated
31650      * @return {Boolean}
31651      */
31652     isUpdating : function(){
31653         return this.updating; 
31654     },
31655     
31656     /**
31657      * Suspend the LayoutManager from doing auto-layouts while
31658      * making multiple add or remove calls
31659      */
31660     beginUpdate : function(){
31661         this.updating = true;    
31662     },
31663     
31664     /**
31665      * Restore auto-layouts and optionally disable the manager from performing a layout
31666      * @param {Boolean} noLayout true to disable a layout update 
31667      */
31668     endUpdate : function(noLayout){
31669         this.updating = false;
31670         if(!noLayout){
31671             this.layout();
31672         }    
31673     },
31674     
31675     layout: function(){
31676         // abstract...
31677     },
31678     
31679     onRegionResized : function(region, newSize){
31680         this.fireEvent("regionresized", region, newSize);
31681         this.layout();
31682     },
31683     
31684     onRegionCollapsed : function(region){
31685         this.fireEvent("regioncollapsed", region);
31686     },
31687     
31688     onRegionExpanded : function(region){
31689         this.fireEvent("regionexpanded", region);
31690     },
31691         
31692     /**
31693      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31694      * performs box-model adjustments.
31695      * @return {Object} The size as an object {width: (the width), height: (the height)}
31696      */
31697     getViewSize : function()
31698     {
31699         var size;
31700         if(this.el.dom != document.body){
31701             size = this.el.getSize();
31702         }else{
31703             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31704         }
31705         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31706         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31707         return size;
31708     },
31709     
31710     /**
31711      * Returns the Element this layout is bound to.
31712      * @return {Roo.Element}
31713      */
31714     getEl : function(){
31715         return this.el;
31716     },
31717     
31718     /**
31719      * Returns the specified region.
31720      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31721      * @return {Roo.LayoutRegion}
31722      */
31723     getRegion : function(target){
31724         return this.regions[target.toLowerCase()];
31725     },
31726     
31727     onWindowResize : function(){
31728         if(this.monitorWindowResize){
31729             this.layout();
31730         }
31731     }
31732 });/*
31733  * Based on:
31734  * Ext JS Library 1.1.1
31735  * Copyright(c) 2006-2007, Ext JS, LLC.
31736  *
31737  * Originally Released Under LGPL - original licence link has changed is not relivant.
31738  *
31739  * Fork - LGPL
31740  * <script type="text/javascript">
31741  */
31742 /**
31743  * @class Roo.bootstrap.layout.Border
31744  * @extends Roo.bootstrap.layout.Manager
31745  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31746  * please see: examples/bootstrap/nested.html<br><br>
31747  
31748 <b>The container the layout is rendered into can be either the body element or any other element.
31749 If it is not the body element, the container needs to either be an absolute positioned element,
31750 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31751 the container size if it is not the body element.</b>
31752
31753 * @constructor
31754 * Create a new Border
31755 * @param {Object} config Configuration options
31756  */
31757 Roo.bootstrap.layout.Border = function(config){
31758     config = config || {};
31759     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31760     
31761     
31762     
31763     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31764         if(config[region]){
31765             config[region].region = region;
31766             this.addRegion(config[region]);
31767         }
31768     },this);
31769     
31770 };
31771
31772 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31773
31774 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31775     /**
31776      * Creates and adds a new region if it doesn't already exist.
31777      * @param {String} target The target region key (north, south, east, west or center).
31778      * @param {Object} config The regions config object
31779      * @return {BorderLayoutRegion} The new region
31780      */
31781     addRegion : function(config)
31782     {
31783         if(!this.regions[config.region]){
31784             var r = this.factory(config);
31785             this.bindRegion(r);
31786         }
31787         return this.regions[config.region];
31788     },
31789
31790     // private (kinda)
31791     bindRegion : function(r){
31792         this.regions[r.config.region] = r;
31793         
31794         r.on("visibilitychange",    this.layout, this);
31795         r.on("paneladded",          this.layout, this);
31796         r.on("panelremoved",        this.layout, this);
31797         r.on("invalidated",         this.layout, this);
31798         r.on("resized",             this.onRegionResized, this);
31799         r.on("collapsed",           this.onRegionCollapsed, this);
31800         r.on("expanded",            this.onRegionExpanded, this);
31801     },
31802
31803     /**
31804      * Performs a layout update.
31805      */
31806     layout : function()
31807     {
31808         if(this.updating) {
31809             return;
31810         }
31811         
31812         // render all the rebions if they have not been done alreayd?
31813         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31814             if(this.regions[region] && !this.regions[region].bodyEl){
31815                 this.regions[region].onRender(this.el)
31816             }
31817         },this);
31818         
31819         var size = this.getViewSize();
31820         var w = size.width;
31821         var h = size.height;
31822         var centerW = w;
31823         var centerH = h;
31824         var centerY = 0;
31825         var centerX = 0;
31826         //var x = 0, y = 0;
31827
31828         var rs = this.regions;
31829         var north = rs["north"];
31830         var south = rs["south"]; 
31831         var west = rs["west"];
31832         var east = rs["east"];
31833         var center = rs["center"];
31834         //if(this.hideOnLayout){ // not supported anymore
31835             //c.el.setStyle("display", "none");
31836         //}
31837         if(north && north.isVisible()){
31838             var b = north.getBox();
31839             var m = north.getMargins();
31840             b.width = w - (m.left+m.right);
31841             b.x = m.left;
31842             b.y = m.top;
31843             centerY = b.height + b.y + m.bottom;
31844             centerH -= centerY;
31845             north.updateBox(this.safeBox(b));
31846         }
31847         if(south && south.isVisible()){
31848             var b = south.getBox();
31849             var m = south.getMargins();
31850             b.width = w - (m.left+m.right);
31851             b.x = m.left;
31852             var totalHeight = (b.height + m.top + m.bottom);
31853             b.y = h - totalHeight + m.top;
31854             centerH -= totalHeight;
31855             south.updateBox(this.safeBox(b));
31856         }
31857         if(west && west.isVisible()){
31858             var b = west.getBox();
31859             var m = west.getMargins();
31860             b.height = centerH - (m.top+m.bottom);
31861             b.x = m.left;
31862             b.y = centerY + m.top;
31863             var totalWidth = (b.width + m.left + m.right);
31864             centerX += totalWidth;
31865             centerW -= totalWidth;
31866             west.updateBox(this.safeBox(b));
31867         }
31868         if(east && east.isVisible()){
31869             var b = east.getBox();
31870             var m = east.getMargins();
31871             b.height = centerH - (m.top+m.bottom);
31872             var totalWidth = (b.width + m.left + m.right);
31873             b.x = w - totalWidth + m.left;
31874             b.y = centerY + m.top;
31875             centerW -= totalWidth;
31876             east.updateBox(this.safeBox(b));
31877         }
31878         if(center){
31879             var m = center.getMargins();
31880             var centerBox = {
31881                 x: centerX + m.left,
31882                 y: centerY + m.top,
31883                 width: centerW - (m.left+m.right),
31884                 height: centerH - (m.top+m.bottom)
31885             };
31886             //if(this.hideOnLayout){
31887                 //center.el.setStyle("display", "block");
31888             //}
31889             center.updateBox(this.safeBox(centerBox));
31890         }
31891         this.el.repaint();
31892         this.fireEvent("layout", this);
31893     },
31894
31895     // private
31896     safeBox : function(box){
31897         box.width = Math.max(0, box.width);
31898         box.height = Math.max(0, box.height);
31899         return box;
31900     },
31901
31902     /**
31903      * Adds a ContentPanel (or subclass) to this layout.
31904      * @param {String} target The target region key (north, south, east, west or center).
31905      * @param {Roo.ContentPanel} panel The panel to add
31906      * @return {Roo.ContentPanel} The added panel
31907      */
31908     add : function(target, panel){
31909          
31910         target = target.toLowerCase();
31911         return this.regions[target].add(panel);
31912     },
31913
31914     /**
31915      * Remove a ContentPanel (or subclass) to this layout.
31916      * @param {String} target The target region key (north, south, east, west or center).
31917      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31918      * @return {Roo.ContentPanel} The removed panel
31919      */
31920     remove : function(target, panel){
31921         target = target.toLowerCase();
31922         return this.regions[target].remove(panel);
31923     },
31924
31925     /**
31926      * Searches all regions for a panel with the specified id
31927      * @param {String} panelId
31928      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31929      */
31930     findPanel : function(panelId){
31931         var rs = this.regions;
31932         for(var target in rs){
31933             if(typeof rs[target] != "function"){
31934                 var p = rs[target].getPanel(panelId);
31935                 if(p){
31936                     return p;
31937                 }
31938             }
31939         }
31940         return null;
31941     },
31942
31943     /**
31944      * Searches all regions for a panel with the specified id and activates (shows) it.
31945      * @param {String/ContentPanel} panelId The panels id or the panel itself
31946      * @return {Roo.ContentPanel} The shown panel or null
31947      */
31948     showPanel : function(panelId) {
31949       var rs = this.regions;
31950       for(var target in rs){
31951          var r = rs[target];
31952          if(typeof r != "function"){
31953             if(r.hasPanel(panelId)){
31954                return r.showPanel(panelId);
31955             }
31956          }
31957       }
31958       return null;
31959    },
31960
31961    /**
31962      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31963      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31964      */
31965    /*
31966     restoreState : function(provider){
31967         if(!provider){
31968             provider = Roo.state.Manager;
31969         }
31970         var sm = new Roo.LayoutStateManager();
31971         sm.init(this, provider);
31972     },
31973 */
31974  
31975  
31976     /**
31977      * Adds a xtype elements to the layout.
31978      * <pre><code>
31979
31980 layout.addxtype({
31981        xtype : 'ContentPanel',
31982        region: 'west',
31983        items: [ .... ]
31984    }
31985 );
31986
31987 layout.addxtype({
31988         xtype : 'NestedLayoutPanel',
31989         region: 'west',
31990         layout: {
31991            center: { },
31992            west: { }   
31993         },
31994         items : [ ... list of content panels or nested layout panels.. ]
31995    }
31996 );
31997 </code></pre>
31998      * @param {Object} cfg Xtype definition of item to add.
31999      */
32000     addxtype : function(cfg)
32001     {
32002         // basically accepts a pannel...
32003         // can accept a layout region..!?!?
32004         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32005         
32006         
32007         // theory?  children can only be panels??
32008         
32009         //if (!cfg.xtype.match(/Panel$/)) {
32010         //    return false;
32011         //}
32012         var ret = false;
32013         
32014         if (typeof(cfg.region) == 'undefined') {
32015             Roo.log("Failed to add Panel, region was not set");
32016             Roo.log(cfg);
32017             return false;
32018         }
32019         var region = cfg.region;
32020         delete cfg.region;
32021         
32022           
32023         var xitems = [];
32024         if (cfg.items) {
32025             xitems = cfg.items;
32026             delete cfg.items;
32027         }
32028         var nb = false;
32029         
32030         switch(cfg.xtype) 
32031         {
32032             case 'Content':  // ContentPanel (el, cfg)
32033             case 'Scroll':  // ContentPanel (el, cfg)
32034             case 'View': 
32035                 cfg.autoCreate = true;
32036                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32037                 //} else {
32038                 //    var el = this.el.createChild();
32039                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32040                 //}
32041                 
32042                 this.add(region, ret);
32043                 break;
32044             
32045             /*
32046             case 'TreePanel': // our new panel!
32047                 cfg.el = this.el.createChild();
32048                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32049                 this.add(region, ret);
32050                 break;
32051             */
32052             
32053             case 'Nest': 
32054                 // create a new Layout (which is  a Border Layout...
32055                 
32056                 var clayout = cfg.layout;
32057                 clayout.el  = this.el.createChild();
32058                 clayout.items   = clayout.items  || [];
32059                 
32060                 delete cfg.layout;
32061                 
32062                 // replace this exitems with the clayout ones..
32063                 xitems = clayout.items;
32064                  
32065                 // force background off if it's in center...
32066                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32067                     cfg.background = false;
32068                 }
32069                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
32070                 
32071                 
32072                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32073                 //console.log('adding nested layout panel '  + cfg.toSource());
32074                 this.add(region, ret);
32075                 nb = {}; /// find first...
32076                 break;
32077             
32078             case 'Grid':
32079                 
32080                 // needs grid and region
32081                 
32082                 //var el = this.getRegion(region).el.createChild();
32083                 /*
32084                  *var el = this.el.createChild();
32085                 // create the grid first...
32086                 cfg.grid.container = el;
32087                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32088                 */
32089                 
32090                 if (region == 'center' && this.active ) {
32091                     cfg.background = false;
32092                 }
32093                 
32094                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32095                 
32096                 this.add(region, ret);
32097                 /*
32098                 if (cfg.background) {
32099                     // render grid on panel activation (if panel background)
32100                     ret.on('activate', function(gp) {
32101                         if (!gp.grid.rendered) {
32102                     //        gp.grid.render(el);
32103                         }
32104                     });
32105                 } else {
32106                   //  cfg.grid.render(el);
32107                 }
32108                 */
32109                 break;
32110            
32111            
32112             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32113                 // it was the old xcomponent building that caused this before.
32114                 // espeically if border is the top element in the tree.
32115                 ret = this;
32116                 break; 
32117                 
32118                     
32119                 
32120                 
32121                 
32122             default:
32123                 /*
32124                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32125                     
32126                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32127                     this.add(region, ret);
32128                 } else {
32129                 */
32130                     Roo.log(cfg);
32131                     throw "Can not add '" + cfg.xtype + "' to Border";
32132                     return null;
32133              
32134                                 
32135              
32136         }
32137         this.beginUpdate();
32138         // add children..
32139         var region = '';
32140         var abn = {};
32141         Roo.each(xitems, function(i)  {
32142             region = nb && i.region ? i.region : false;
32143             
32144             var add = ret.addxtype(i);
32145            
32146             if (region) {
32147                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32148                 if (!i.background) {
32149                     abn[region] = nb[region] ;
32150                 }
32151             }
32152             
32153         });
32154         this.endUpdate();
32155
32156         // make the last non-background panel active..
32157         //if (nb) { Roo.log(abn); }
32158         if (nb) {
32159             
32160             for(var r in abn) {
32161                 region = this.getRegion(r);
32162                 if (region) {
32163                     // tried using nb[r], but it does not work..
32164                      
32165                     region.showPanel(abn[r]);
32166                    
32167                 }
32168             }
32169         }
32170         return ret;
32171         
32172     },
32173     
32174     
32175 // private
32176     factory : function(cfg)
32177     {
32178         
32179         var validRegions = Roo.bootstrap.layout.Border.regions;
32180
32181         var target = cfg.region;
32182         cfg.mgr = this;
32183         
32184         var r = Roo.bootstrap.layout;
32185         Roo.log(target);
32186         switch(target){
32187             case "north":
32188                 return new r.North(cfg);
32189             case "south":
32190                 return new r.South(cfg);
32191             case "east":
32192                 return new r.East(cfg);
32193             case "west":
32194                 return new r.West(cfg);
32195             case "center":
32196                 return new r.Center(cfg);
32197         }
32198         throw 'Layout region "'+target+'" not supported.';
32199     }
32200     
32201     
32202 });
32203  /*
32204  * Based on:
32205  * Ext JS Library 1.1.1
32206  * Copyright(c) 2006-2007, Ext JS, LLC.
32207  *
32208  * Originally Released Under LGPL - original licence link has changed is not relivant.
32209  *
32210  * Fork - LGPL
32211  * <script type="text/javascript">
32212  */
32213  
32214 /**
32215  * @class Roo.bootstrap.layout.Basic
32216  * @extends Roo.util.Observable
32217  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32218  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32219  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32220  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32221  * @cfg {string}   region  the region that it inhabits..
32222  * @cfg {bool}   skipConfig skip config?
32223  * 
32224
32225  */
32226 Roo.bootstrap.layout.Basic = function(config){
32227     
32228     this.mgr = config.mgr;
32229     
32230     this.position = config.region;
32231     
32232     var skipConfig = config.skipConfig;
32233     
32234     this.events = {
32235         /**
32236          * @scope Roo.BasicLayoutRegion
32237          */
32238         
32239         /**
32240          * @event beforeremove
32241          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32242          * @param {Roo.LayoutRegion} this
32243          * @param {Roo.ContentPanel} panel The panel
32244          * @param {Object} e The cancel event object
32245          */
32246         "beforeremove" : true,
32247         /**
32248          * @event invalidated
32249          * Fires when the layout for this region is changed.
32250          * @param {Roo.LayoutRegion} this
32251          */
32252         "invalidated" : true,
32253         /**
32254          * @event visibilitychange
32255          * Fires when this region is shown or hidden 
32256          * @param {Roo.LayoutRegion} this
32257          * @param {Boolean} visibility true or false
32258          */
32259         "visibilitychange" : true,
32260         /**
32261          * @event paneladded
32262          * Fires when a panel is added. 
32263          * @param {Roo.LayoutRegion} this
32264          * @param {Roo.ContentPanel} panel The panel
32265          */
32266         "paneladded" : true,
32267         /**
32268          * @event panelremoved
32269          * Fires when a panel is removed. 
32270          * @param {Roo.LayoutRegion} this
32271          * @param {Roo.ContentPanel} panel The panel
32272          */
32273         "panelremoved" : true,
32274         /**
32275          * @event beforecollapse
32276          * Fires when this region before collapse.
32277          * @param {Roo.LayoutRegion} this
32278          */
32279         "beforecollapse" : true,
32280         /**
32281          * @event collapsed
32282          * Fires when this region is collapsed.
32283          * @param {Roo.LayoutRegion} this
32284          */
32285         "collapsed" : true,
32286         /**
32287          * @event expanded
32288          * Fires when this region is expanded.
32289          * @param {Roo.LayoutRegion} this
32290          */
32291         "expanded" : true,
32292         /**
32293          * @event slideshow
32294          * Fires when this region is slid into view.
32295          * @param {Roo.LayoutRegion} this
32296          */
32297         "slideshow" : true,
32298         /**
32299          * @event slidehide
32300          * Fires when this region slides out of view. 
32301          * @param {Roo.LayoutRegion} this
32302          */
32303         "slidehide" : true,
32304         /**
32305          * @event panelactivated
32306          * Fires when a panel is activated. 
32307          * @param {Roo.LayoutRegion} this
32308          * @param {Roo.ContentPanel} panel The activated panel
32309          */
32310         "panelactivated" : true,
32311         /**
32312          * @event resized
32313          * Fires when the user resizes this region. 
32314          * @param {Roo.LayoutRegion} this
32315          * @param {Number} newSize The new size (width for east/west, height for north/south)
32316          */
32317         "resized" : true
32318     };
32319     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32320     this.panels = new Roo.util.MixedCollection();
32321     this.panels.getKey = this.getPanelId.createDelegate(this);
32322     this.box = null;
32323     this.activePanel = null;
32324     // ensure listeners are added...
32325     
32326     if (config.listeners || config.events) {
32327         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32328             listeners : config.listeners || {},
32329             events : config.events || {}
32330         });
32331     }
32332     
32333     if(skipConfig !== true){
32334         this.applyConfig(config);
32335     }
32336 };
32337
32338 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32339 {
32340     getPanelId : function(p){
32341         return p.getId();
32342     },
32343     
32344     applyConfig : function(config){
32345         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32346         this.config = config;
32347         
32348     },
32349     
32350     /**
32351      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32352      * the width, for horizontal (north, south) the height.
32353      * @param {Number} newSize The new width or height
32354      */
32355     resizeTo : function(newSize){
32356         var el = this.el ? this.el :
32357                  (this.activePanel ? this.activePanel.getEl() : null);
32358         if(el){
32359             switch(this.position){
32360                 case "east":
32361                 case "west":
32362                     el.setWidth(newSize);
32363                     this.fireEvent("resized", this, newSize);
32364                 break;
32365                 case "north":
32366                 case "south":
32367                     el.setHeight(newSize);
32368                     this.fireEvent("resized", this, newSize);
32369                 break;                
32370             }
32371         }
32372     },
32373     
32374     getBox : function(){
32375         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32376     },
32377     
32378     getMargins : function(){
32379         return this.margins;
32380     },
32381     
32382     updateBox : function(box){
32383         this.box = box;
32384         var el = this.activePanel.getEl();
32385         el.dom.style.left = box.x + "px";
32386         el.dom.style.top = box.y + "px";
32387         this.activePanel.setSize(box.width, box.height);
32388     },
32389     
32390     /**
32391      * Returns the container element for this region.
32392      * @return {Roo.Element}
32393      */
32394     getEl : function(){
32395         return this.activePanel;
32396     },
32397     
32398     /**
32399      * Returns true if this region is currently visible.
32400      * @return {Boolean}
32401      */
32402     isVisible : function(){
32403         return this.activePanel ? true : false;
32404     },
32405     
32406     setActivePanel : function(panel){
32407         panel = this.getPanel(panel);
32408         if(this.activePanel && this.activePanel != panel){
32409             this.activePanel.setActiveState(false);
32410             this.activePanel.getEl().setLeftTop(-10000,-10000);
32411         }
32412         this.activePanel = panel;
32413         panel.setActiveState(true);
32414         if(this.box){
32415             panel.setSize(this.box.width, this.box.height);
32416         }
32417         this.fireEvent("panelactivated", this, panel);
32418         this.fireEvent("invalidated");
32419     },
32420     
32421     /**
32422      * Show the specified panel.
32423      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32424      * @return {Roo.ContentPanel} The shown panel or null
32425      */
32426     showPanel : function(panel){
32427         panel = this.getPanel(panel);
32428         if(panel){
32429             this.setActivePanel(panel);
32430         }
32431         return panel;
32432     },
32433     
32434     /**
32435      * Get the active panel for this region.
32436      * @return {Roo.ContentPanel} The active panel or null
32437      */
32438     getActivePanel : function(){
32439         return this.activePanel;
32440     },
32441     
32442     /**
32443      * Add the passed ContentPanel(s)
32444      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32445      * @return {Roo.ContentPanel} The panel added (if only one was added)
32446      */
32447     add : function(panel){
32448         if(arguments.length > 1){
32449             for(var i = 0, len = arguments.length; i < len; i++) {
32450                 this.add(arguments[i]);
32451             }
32452             return null;
32453         }
32454         if(this.hasPanel(panel)){
32455             this.showPanel(panel);
32456             return panel;
32457         }
32458         var el = panel.getEl();
32459         if(el.dom.parentNode != this.mgr.el.dom){
32460             this.mgr.el.dom.appendChild(el.dom);
32461         }
32462         if(panel.setRegion){
32463             panel.setRegion(this);
32464         }
32465         this.panels.add(panel);
32466         el.setStyle("position", "absolute");
32467         if(!panel.background){
32468             this.setActivePanel(panel);
32469             if(this.config.initialSize && this.panels.getCount()==1){
32470                 this.resizeTo(this.config.initialSize);
32471             }
32472         }
32473         this.fireEvent("paneladded", this, panel);
32474         return panel;
32475     },
32476     
32477     /**
32478      * Returns true if the panel is in this region.
32479      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32480      * @return {Boolean}
32481      */
32482     hasPanel : function(panel){
32483         if(typeof panel == "object"){ // must be panel obj
32484             panel = panel.getId();
32485         }
32486         return this.getPanel(panel) ? true : false;
32487     },
32488     
32489     /**
32490      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32491      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32492      * @param {Boolean} preservePanel Overrides the config preservePanel option
32493      * @return {Roo.ContentPanel} The panel that was removed
32494      */
32495     remove : function(panel, preservePanel){
32496         panel = this.getPanel(panel);
32497         if(!panel){
32498             return null;
32499         }
32500         var e = {};
32501         this.fireEvent("beforeremove", this, panel, e);
32502         if(e.cancel === true){
32503             return null;
32504         }
32505         var panelId = panel.getId();
32506         this.panels.removeKey(panelId);
32507         return panel;
32508     },
32509     
32510     /**
32511      * Returns the panel specified or null if it's not in this region.
32512      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32513      * @return {Roo.ContentPanel}
32514      */
32515     getPanel : function(id){
32516         if(typeof id == "object"){ // must be panel obj
32517             return id;
32518         }
32519         return this.panels.get(id);
32520     },
32521     
32522     /**
32523      * Returns this regions position (north/south/east/west/center).
32524      * @return {String} 
32525      */
32526     getPosition: function(){
32527         return this.position;    
32528     }
32529 });/*
32530  * Based on:
32531  * Ext JS Library 1.1.1
32532  * Copyright(c) 2006-2007, Ext JS, LLC.
32533  *
32534  * Originally Released Under LGPL - original licence link has changed is not relivant.
32535  *
32536  * Fork - LGPL
32537  * <script type="text/javascript">
32538  */
32539  
32540 /**
32541  * @class Roo.bootstrap.layout.Region
32542  * @extends Roo.bootstrap.layout.Basic
32543  * This class represents a region in a layout manager.
32544  
32545  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32546  * @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})
32547  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32548  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32549  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32550  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32551  * @cfg {String}    title           The title for the region (overrides panel titles)
32552  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32553  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32554  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32555  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32556  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32557  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32558  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32559  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32560  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32561  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32562
32563  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32564  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32565  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32566  * @cfg {Number}    width           For East/West panels
32567  * @cfg {Number}    height          For North/South panels
32568  * @cfg {Boolean}   split           To show the splitter
32569  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32570  * 
32571  * @cfg {string}   cls             Extra CSS classes to add to region
32572  * 
32573  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32574  * @cfg {string}   region  the region that it inhabits..
32575  *
32576
32577  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32578  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32579
32580  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32581  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32582  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32583  */
32584 Roo.bootstrap.layout.Region = function(config)
32585 {
32586     this.applyConfig(config);
32587
32588     var mgr = config.mgr;
32589     var pos = config.region;
32590     config.skipConfig = true;
32591     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32592     
32593     if (mgr.el) {
32594         this.onRender(mgr.el);   
32595     }
32596      
32597     this.visible = true;
32598     this.collapsed = false;
32599     this.unrendered_panels = [];
32600 };
32601
32602 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32603
32604     position: '', // set by wrapper (eg. north/south etc..)
32605     unrendered_panels : null,  // unrendered panels.
32606     createBody : function(){
32607         /** This region's body element 
32608         * @type Roo.Element */
32609         this.bodyEl = this.el.createChild({
32610                 tag: "div",
32611                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32612         });
32613     },
32614
32615     onRender: function(ctr, pos)
32616     {
32617         var dh = Roo.DomHelper;
32618         /** This region's container element 
32619         * @type Roo.Element */
32620         this.el = dh.append(ctr.dom, {
32621                 tag: "div",
32622                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32623             }, true);
32624         /** This region's title element 
32625         * @type Roo.Element */
32626     
32627         this.titleEl = dh.append(this.el.dom,
32628             {
32629                     tag: "div",
32630                     unselectable: "on",
32631                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32632                     children:[
32633                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32634                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32635                     ]}, true);
32636         
32637         this.titleEl.enableDisplayMode();
32638         /** This region's title text element 
32639         * @type HTMLElement */
32640         this.titleTextEl = this.titleEl.dom.firstChild;
32641         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32642         /*
32643         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32644         this.closeBtn.enableDisplayMode();
32645         this.closeBtn.on("click", this.closeClicked, this);
32646         this.closeBtn.hide();
32647     */
32648         this.createBody(this.config);
32649         if(this.config.hideWhenEmpty){
32650             this.hide();
32651             this.on("paneladded", this.validateVisibility, this);
32652             this.on("panelremoved", this.validateVisibility, this);
32653         }
32654         if(this.autoScroll){
32655             this.bodyEl.setStyle("overflow", "auto");
32656         }else{
32657             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32658         }
32659         //if(c.titlebar !== false){
32660             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32661                 this.titleEl.hide();
32662             }else{
32663                 this.titleEl.show();
32664                 if(this.config.title){
32665                     this.titleTextEl.innerHTML = this.config.title;
32666                 }
32667             }
32668         //}
32669         if(this.config.collapsed){
32670             this.collapse(true);
32671         }
32672         if(this.config.hidden){
32673             this.hide();
32674         }
32675         
32676         if (this.unrendered_panels && this.unrendered_panels.length) {
32677             for (var i =0;i< this.unrendered_panels.length; i++) {
32678                 this.add(this.unrendered_panels[i]);
32679             }
32680             this.unrendered_panels = null;
32681             
32682         }
32683         
32684     },
32685     
32686     applyConfig : function(c)
32687     {
32688         /*
32689          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32690             var dh = Roo.DomHelper;
32691             if(c.titlebar !== false){
32692                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32693                 this.collapseBtn.on("click", this.collapse, this);
32694                 this.collapseBtn.enableDisplayMode();
32695                 /*
32696                 if(c.showPin === true || this.showPin){
32697                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32698                     this.stickBtn.enableDisplayMode();
32699                     this.stickBtn.on("click", this.expand, this);
32700                     this.stickBtn.hide();
32701                 }
32702                 
32703             }
32704             */
32705             /** This region's collapsed element
32706             * @type Roo.Element */
32707             /*
32708              *
32709             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32710                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32711             ]}, true);
32712             
32713             if(c.floatable !== false){
32714                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32715                this.collapsedEl.on("click", this.collapseClick, this);
32716             }
32717
32718             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32719                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32720                    id: "message", unselectable: "on", style:{"float":"left"}});
32721                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32722              }
32723             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32724             this.expandBtn.on("click", this.expand, this);
32725             
32726         }
32727         
32728         if(this.collapseBtn){
32729             this.collapseBtn.setVisible(c.collapsible == true);
32730         }
32731         
32732         this.cmargins = c.cmargins || this.cmargins ||
32733                          (this.position == "west" || this.position == "east" ?
32734                              {top: 0, left: 2, right:2, bottom: 0} :
32735                              {top: 2, left: 0, right:0, bottom: 2});
32736         */
32737         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32738         
32739         
32740         this.bottomTabs = c.tabPosition != "top";
32741         
32742         this.autoScroll = c.autoScroll || false;
32743         
32744         
32745        
32746         
32747         this.duration = c.duration || .30;
32748         this.slideDuration = c.slideDuration || .45;
32749         this.config = c;
32750        
32751     },
32752     /**
32753      * Returns true if this region is currently visible.
32754      * @return {Boolean}
32755      */
32756     isVisible : function(){
32757         return this.visible;
32758     },
32759
32760     /**
32761      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32762      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32763      */
32764     //setCollapsedTitle : function(title){
32765     //    title = title || "&#160;";
32766      //   if(this.collapsedTitleTextEl){
32767       //      this.collapsedTitleTextEl.innerHTML = title;
32768        // }
32769     //},
32770
32771     getBox : function(){
32772         var b;
32773       //  if(!this.collapsed){
32774             b = this.el.getBox(false, true);
32775        // }else{
32776           //  b = this.collapsedEl.getBox(false, true);
32777         //}
32778         return b;
32779     },
32780
32781     getMargins : function(){
32782         return this.margins;
32783         //return this.collapsed ? this.cmargins : this.margins;
32784     },
32785 /*
32786     highlight : function(){
32787         this.el.addClass("x-layout-panel-dragover");
32788     },
32789
32790     unhighlight : function(){
32791         this.el.removeClass("x-layout-panel-dragover");
32792     },
32793 */
32794     updateBox : function(box)
32795     {
32796         if (!this.bodyEl) {
32797             return; // not rendered yet..
32798         }
32799         
32800         this.box = box;
32801         if(!this.collapsed){
32802             this.el.dom.style.left = box.x + "px";
32803             this.el.dom.style.top = box.y + "px";
32804             this.updateBody(box.width, box.height);
32805         }else{
32806             this.collapsedEl.dom.style.left = box.x + "px";
32807             this.collapsedEl.dom.style.top = box.y + "px";
32808             this.collapsedEl.setSize(box.width, box.height);
32809         }
32810         if(this.tabs){
32811             this.tabs.autoSizeTabs();
32812         }
32813     },
32814
32815     updateBody : function(w, h)
32816     {
32817         if(w !== null){
32818             this.el.setWidth(w);
32819             w -= this.el.getBorderWidth("rl");
32820             if(this.config.adjustments){
32821                 w += this.config.adjustments[0];
32822             }
32823         }
32824         if(h !== null){
32825             this.el.setHeight(h);
32826             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32827             h -= this.el.getBorderWidth("tb");
32828             if(this.config.adjustments){
32829                 h += this.config.adjustments[1];
32830             }
32831             this.bodyEl.setHeight(h);
32832             if(this.tabs){
32833                 h = this.tabs.syncHeight(h);
32834             }
32835         }
32836         if(this.panelSize){
32837             w = w !== null ? w : this.panelSize.width;
32838             h = h !== null ? h : this.panelSize.height;
32839         }
32840         if(this.activePanel){
32841             var el = this.activePanel.getEl();
32842             w = w !== null ? w : el.getWidth();
32843             h = h !== null ? h : el.getHeight();
32844             this.panelSize = {width: w, height: h};
32845             this.activePanel.setSize(w, h);
32846         }
32847         if(Roo.isIE && this.tabs){
32848             this.tabs.el.repaint();
32849         }
32850     },
32851
32852     /**
32853      * Returns the container element for this region.
32854      * @return {Roo.Element}
32855      */
32856     getEl : function(){
32857         return this.el;
32858     },
32859
32860     /**
32861      * Hides this region.
32862      */
32863     hide : function(){
32864         //if(!this.collapsed){
32865             this.el.dom.style.left = "-2000px";
32866             this.el.hide();
32867         //}else{
32868          //   this.collapsedEl.dom.style.left = "-2000px";
32869          //   this.collapsedEl.hide();
32870        // }
32871         this.visible = false;
32872         this.fireEvent("visibilitychange", this, false);
32873     },
32874
32875     /**
32876      * Shows this region if it was previously hidden.
32877      */
32878     show : function(){
32879         //if(!this.collapsed){
32880             this.el.show();
32881         //}else{
32882         //    this.collapsedEl.show();
32883        // }
32884         this.visible = true;
32885         this.fireEvent("visibilitychange", this, true);
32886     },
32887 /*
32888     closeClicked : function(){
32889         if(this.activePanel){
32890             this.remove(this.activePanel);
32891         }
32892     },
32893
32894     collapseClick : function(e){
32895         if(this.isSlid){
32896            e.stopPropagation();
32897            this.slideIn();
32898         }else{
32899            e.stopPropagation();
32900            this.slideOut();
32901         }
32902     },
32903 */
32904     /**
32905      * Collapses this region.
32906      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32907      */
32908     /*
32909     collapse : function(skipAnim, skipCheck = false){
32910         if(this.collapsed) {
32911             return;
32912         }
32913         
32914         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32915             
32916             this.collapsed = true;
32917             if(this.split){
32918                 this.split.el.hide();
32919             }
32920             if(this.config.animate && skipAnim !== true){
32921                 this.fireEvent("invalidated", this);
32922                 this.animateCollapse();
32923             }else{
32924                 this.el.setLocation(-20000,-20000);
32925                 this.el.hide();
32926                 this.collapsedEl.show();
32927                 this.fireEvent("collapsed", this);
32928                 this.fireEvent("invalidated", this);
32929             }
32930         }
32931         
32932     },
32933 */
32934     animateCollapse : function(){
32935         // overridden
32936     },
32937
32938     /**
32939      * Expands this region if it was previously collapsed.
32940      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32941      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32942      */
32943     /*
32944     expand : function(e, skipAnim){
32945         if(e) {
32946             e.stopPropagation();
32947         }
32948         if(!this.collapsed || this.el.hasActiveFx()) {
32949             return;
32950         }
32951         if(this.isSlid){
32952             this.afterSlideIn();
32953             skipAnim = true;
32954         }
32955         this.collapsed = false;
32956         if(this.config.animate && skipAnim !== true){
32957             this.animateExpand();
32958         }else{
32959             this.el.show();
32960             if(this.split){
32961                 this.split.el.show();
32962             }
32963             this.collapsedEl.setLocation(-2000,-2000);
32964             this.collapsedEl.hide();
32965             this.fireEvent("invalidated", this);
32966             this.fireEvent("expanded", this);
32967         }
32968     },
32969 */
32970     animateExpand : function(){
32971         // overridden
32972     },
32973
32974     initTabs : function()
32975     {
32976         this.bodyEl.setStyle("overflow", "hidden");
32977         var ts = new Roo.bootstrap.panel.Tabs({
32978                 el: this.bodyEl.dom,
32979                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32980                 disableTooltips: this.config.disableTabTips,
32981                 toolbar : this.config.toolbar
32982             });
32983         
32984         if(this.config.hideTabs){
32985             ts.stripWrap.setDisplayed(false);
32986         }
32987         this.tabs = ts;
32988         ts.resizeTabs = this.config.resizeTabs === true;
32989         ts.minTabWidth = this.config.minTabWidth || 40;
32990         ts.maxTabWidth = this.config.maxTabWidth || 250;
32991         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32992         ts.monitorResize = false;
32993         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32994         ts.bodyEl.addClass('roo-layout-tabs-body');
32995         this.panels.each(this.initPanelAsTab, this);
32996     },
32997
32998     initPanelAsTab : function(panel){
32999         var ti = this.tabs.addTab(
33000                     panel.getEl().id,
33001                     panel.getTitle(),
33002                     null,
33003                     this.config.closeOnTab && panel.isClosable()
33004             );
33005         if(panel.tabTip !== undefined){
33006             ti.setTooltip(panel.tabTip);
33007         }
33008         ti.on("activate", function(){
33009               this.setActivePanel(panel);
33010         }, this);
33011         
33012         if(this.config.closeOnTab){
33013             ti.on("beforeclose", function(t, e){
33014                 e.cancel = true;
33015                 this.remove(panel);
33016             }, this);
33017         }
33018         return ti;
33019     },
33020
33021     updatePanelTitle : function(panel, title)
33022     {
33023         if(this.activePanel == panel){
33024             this.updateTitle(title);
33025         }
33026         if(this.tabs){
33027             var ti = this.tabs.getTab(panel.getEl().id);
33028             ti.setText(title);
33029             if(panel.tabTip !== undefined){
33030                 ti.setTooltip(panel.tabTip);
33031             }
33032         }
33033     },
33034
33035     updateTitle : function(title){
33036         if(this.titleTextEl && !this.config.title){
33037             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33038         }
33039     },
33040
33041     setActivePanel : function(panel)
33042     {
33043         panel = this.getPanel(panel);
33044         if(this.activePanel && this.activePanel != panel){
33045             this.activePanel.setActiveState(false);
33046         }
33047         this.activePanel = panel;
33048         panel.setActiveState(true);
33049         if(this.panelSize){
33050             panel.setSize(this.panelSize.width, this.panelSize.height);
33051         }
33052         if(this.closeBtn){
33053             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33054         }
33055         this.updateTitle(panel.getTitle());
33056         if(this.tabs){
33057             this.fireEvent("invalidated", this);
33058         }
33059         this.fireEvent("panelactivated", this, panel);
33060     },
33061
33062     /**
33063      * Shows the specified panel.
33064      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33065      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33066      */
33067     showPanel : function(panel)
33068     {
33069         panel = this.getPanel(panel);
33070         if(panel){
33071             if(this.tabs){
33072                 var tab = this.tabs.getTab(panel.getEl().id);
33073                 if(tab.isHidden()){
33074                     this.tabs.unhideTab(tab.id);
33075                 }
33076                 tab.activate();
33077             }else{
33078                 this.setActivePanel(panel);
33079             }
33080         }
33081         return panel;
33082     },
33083
33084     /**
33085      * Get the active panel for this region.
33086      * @return {Roo.ContentPanel} The active panel or null
33087      */
33088     getActivePanel : function(){
33089         return this.activePanel;
33090     },
33091
33092     validateVisibility : function(){
33093         if(this.panels.getCount() < 1){
33094             this.updateTitle("&#160;");
33095             this.closeBtn.hide();
33096             this.hide();
33097         }else{
33098             if(!this.isVisible()){
33099                 this.show();
33100             }
33101         }
33102     },
33103
33104     /**
33105      * Adds the passed ContentPanel(s) to this region.
33106      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33107      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33108      */
33109     add : function(panel)
33110     {
33111         if(arguments.length > 1){
33112             for(var i = 0, len = arguments.length; i < len; i++) {
33113                 this.add(arguments[i]);
33114             }
33115             return null;
33116         }
33117         
33118         // if we have not been rendered yet, then we can not really do much of this..
33119         if (!this.bodyEl) {
33120             this.unrendered_panels.push(panel);
33121             return panel;
33122         }
33123         
33124         
33125         
33126         
33127         if(this.hasPanel(panel)){
33128             this.showPanel(panel);
33129             return panel;
33130         }
33131         panel.setRegion(this);
33132         this.panels.add(panel);
33133        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33134             // sinle panel - no tab...?? would it not be better to render it with the tabs,
33135             // and hide them... ???
33136             this.bodyEl.dom.appendChild(panel.getEl().dom);
33137             if(panel.background !== true){
33138                 this.setActivePanel(panel);
33139             }
33140             this.fireEvent("paneladded", this, panel);
33141             return panel;
33142         }
33143         */
33144         if(!this.tabs){
33145             this.initTabs();
33146         }else{
33147             this.initPanelAsTab(panel);
33148         }
33149         
33150         
33151         if(panel.background !== true){
33152             this.tabs.activate(panel.getEl().id);
33153         }
33154         this.fireEvent("paneladded", this, panel);
33155         return panel;
33156     },
33157
33158     /**
33159      * Hides the tab for the specified panel.
33160      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33161      */
33162     hidePanel : function(panel){
33163         if(this.tabs && (panel = this.getPanel(panel))){
33164             this.tabs.hideTab(panel.getEl().id);
33165         }
33166     },
33167
33168     /**
33169      * Unhides the tab for a previously hidden panel.
33170      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33171      */
33172     unhidePanel : function(panel){
33173         if(this.tabs && (panel = this.getPanel(panel))){
33174             this.tabs.unhideTab(panel.getEl().id);
33175         }
33176     },
33177
33178     clearPanels : function(){
33179         while(this.panels.getCount() > 0){
33180              this.remove(this.panels.first());
33181         }
33182     },
33183
33184     /**
33185      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33186      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33187      * @param {Boolean} preservePanel Overrides the config preservePanel option
33188      * @return {Roo.ContentPanel} The panel that was removed
33189      */
33190     remove : function(panel, preservePanel)
33191     {
33192         panel = this.getPanel(panel);
33193         if(!panel){
33194             return null;
33195         }
33196         var e = {};
33197         this.fireEvent("beforeremove", this, panel, e);
33198         if(e.cancel === true){
33199             return null;
33200         }
33201         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33202         var panelId = panel.getId();
33203         this.panels.removeKey(panelId);
33204         if(preservePanel){
33205             document.body.appendChild(panel.getEl().dom);
33206         }
33207         if(this.tabs){
33208             this.tabs.removeTab(panel.getEl().id);
33209         }else if (!preservePanel){
33210             this.bodyEl.dom.removeChild(panel.getEl().dom);
33211         }
33212         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33213             var p = this.panels.first();
33214             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33215             tempEl.appendChild(p.getEl().dom);
33216             this.bodyEl.update("");
33217             this.bodyEl.dom.appendChild(p.getEl().dom);
33218             tempEl = null;
33219             this.updateTitle(p.getTitle());
33220             this.tabs = null;
33221             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33222             this.setActivePanel(p);
33223         }
33224         panel.setRegion(null);
33225         if(this.activePanel == panel){
33226             this.activePanel = null;
33227         }
33228         if(this.config.autoDestroy !== false && preservePanel !== true){
33229             try{panel.destroy();}catch(e){}
33230         }
33231         this.fireEvent("panelremoved", this, panel);
33232         return panel;
33233     },
33234
33235     /**
33236      * Returns the TabPanel component used by this region
33237      * @return {Roo.TabPanel}
33238      */
33239     getTabs : function(){
33240         return this.tabs;
33241     },
33242
33243     createTool : function(parentEl, className){
33244         var btn = Roo.DomHelper.append(parentEl, {
33245             tag: "div",
33246             cls: "x-layout-tools-button",
33247             children: [ {
33248                 tag: "div",
33249                 cls: "roo-layout-tools-button-inner " + className,
33250                 html: "&#160;"
33251             }]
33252         }, true);
33253         btn.addClassOnOver("roo-layout-tools-button-over");
33254         return btn;
33255     }
33256 });/*
33257  * Based on:
33258  * Ext JS Library 1.1.1
33259  * Copyright(c) 2006-2007, Ext JS, LLC.
33260  *
33261  * Originally Released Under LGPL - original licence link has changed is not relivant.
33262  *
33263  * Fork - LGPL
33264  * <script type="text/javascript">
33265  */
33266  
33267
33268
33269 /**
33270  * @class Roo.SplitLayoutRegion
33271  * @extends Roo.LayoutRegion
33272  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33273  */
33274 Roo.bootstrap.layout.Split = function(config){
33275     this.cursor = config.cursor;
33276     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33277 };
33278
33279 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33280 {
33281     splitTip : "Drag to resize.",
33282     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33283     useSplitTips : false,
33284
33285     applyConfig : function(config){
33286         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33287     },
33288     
33289     onRender : function(ctr,pos) {
33290         
33291         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33292         if(!this.config.split){
33293             return;
33294         }
33295         if(!this.split){
33296             
33297             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33298                             tag: "div",
33299                             id: this.el.id + "-split",
33300                             cls: "roo-layout-split roo-layout-split-"+this.position,
33301                             html: "&#160;"
33302             });
33303             /** The SplitBar for this region 
33304             * @type Roo.SplitBar */
33305             // does not exist yet...
33306             Roo.log([this.position, this.orientation]);
33307             
33308             this.split = new Roo.bootstrap.SplitBar({
33309                 dragElement : splitEl,
33310                 resizingElement: this.el,
33311                 orientation : this.orientation
33312             });
33313             
33314             this.split.on("moved", this.onSplitMove, this);
33315             this.split.useShim = this.config.useShim === true;
33316             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33317             if(this.useSplitTips){
33318                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33319             }
33320             //if(config.collapsible){
33321             //    this.split.el.on("dblclick", this.collapse,  this);
33322             //}
33323         }
33324         if(typeof this.config.minSize != "undefined"){
33325             this.split.minSize = this.config.minSize;
33326         }
33327         if(typeof this.config.maxSize != "undefined"){
33328             this.split.maxSize = this.config.maxSize;
33329         }
33330         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33331             this.hideSplitter();
33332         }
33333         
33334     },
33335
33336     getHMaxSize : function(){
33337          var cmax = this.config.maxSize || 10000;
33338          var center = this.mgr.getRegion("center");
33339          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33340     },
33341
33342     getVMaxSize : function(){
33343          var cmax = this.config.maxSize || 10000;
33344          var center = this.mgr.getRegion("center");
33345          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33346     },
33347
33348     onSplitMove : function(split, newSize){
33349         this.fireEvent("resized", this, newSize);
33350     },
33351     
33352     /** 
33353      * Returns the {@link Roo.SplitBar} for this region.
33354      * @return {Roo.SplitBar}
33355      */
33356     getSplitBar : function(){
33357         return this.split;
33358     },
33359     
33360     hide : function(){
33361         this.hideSplitter();
33362         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33363     },
33364
33365     hideSplitter : function(){
33366         if(this.split){
33367             this.split.el.setLocation(-2000,-2000);
33368             this.split.el.hide();
33369         }
33370     },
33371
33372     show : function(){
33373         if(this.split){
33374             this.split.el.show();
33375         }
33376         Roo.bootstrap.layout.Split.superclass.show.call(this);
33377     },
33378     
33379     beforeSlide: function(){
33380         if(Roo.isGecko){// firefox overflow auto bug workaround
33381             this.bodyEl.clip();
33382             if(this.tabs) {
33383                 this.tabs.bodyEl.clip();
33384             }
33385             if(this.activePanel){
33386                 this.activePanel.getEl().clip();
33387                 
33388                 if(this.activePanel.beforeSlide){
33389                     this.activePanel.beforeSlide();
33390                 }
33391             }
33392         }
33393     },
33394     
33395     afterSlide : function(){
33396         if(Roo.isGecko){// firefox overflow auto bug workaround
33397             this.bodyEl.unclip();
33398             if(this.tabs) {
33399                 this.tabs.bodyEl.unclip();
33400             }
33401             if(this.activePanel){
33402                 this.activePanel.getEl().unclip();
33403                 if(this.activePanel.afterSlide){
33404                     this.activePanel.afterSlide();
33405                 }
33406             }
33407         }
33408     },
33409
33410     initAutoHide : function(){
33411         if(this.autoHide !== false){
33412             if(!this.autoHideHd){
33413                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33414                 this.autoHideHd = {
33415                     "mouseout": function(e){
33416                         if(!e.within(this.el, true)){
33417                             st.delay(500);
33418                         }
33419                     },
33420                     "mouseover" : function(e){
33421                         st.cancel();
33422                     },
33423                     scope : this
33424                 };
33425             }
33426             this.el.on(this.autoHideHd);
33427         }
33428     },
33429
33430     clearAutoHide : function(){
33431         if(this.autoHide !== false){
33432             this.el.un("mouseout", this.autoHideHd.mouseout);
33433             this.el.un("mouseover", this.autoHideHd.mouseover);
33434         }
33435     },
33436
33437     clearMonitor : function(){
33438         Roo.get(document).un("click", this.slideInIf, this);
33439     },
33440
33441     // these names are backwards but not changed for compat
33442     slideOut : function(){
33443         if(this.isSlid || this.el.hasActiveFx()){
33444             return;
33445         }
33446         this.isSlid = true;
33447         if(this.collapseBtn){
33448             this.collapseBtn.hide();
33449         }
33450         this.closeBtnState = this.closeBtn.getStyle('display');
33451         this.closeBtn.hide();
33452         if(this.stickBtn){
33453             this.stickBtn.show();
33454         }
33455         this.el.show();
33456         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33457         this.beforeSlide();
33458         this.el.setStyle("z-index", 10001);
33459         this.el.slideIn(this.getSlideAnchor(), {
33460             callback: function(){
33461                 this.afterSlide();
33462                 this.initAutoHide();
33463                 Roo.get(document).on("click", this.slideInIf, this);
33464                 this.fireEvent("slideshow", this);
33465             },
33466             scope: this,
33467             block: true
33468         });
33469     },
33470
33471     afterSlideIn : function(){
33472         this.clearAutoHide();
33473         this.isSlid = false;
33474         this.clearMonitor();
33475         this.el.setStyle("z-index", "");
33476         if(this.collapseBtn){
33477             this.collapseBtn.show();
33478         }
33479         this.closeBtn.setStyle('display', this.closeBtnState);
33480         if(this.stickBtn){
33481             this.stickBtn.hide();
33482         }
33483         this.fireEvent("slidehide", this);
33484     },
33485
33486     slideIn : function(cb){
33487         if(!this.isSlid || this.el.hasActiveFx()){
33488             Roo.callback(cb);
33489             return;
33490         }
33491         this.isSlid = false;
33492         this.beforeSlide();
33493         this.el.slideOut(this.getSlideAnchor(), {
33494             callback: function(){
33495                 this.el.setLeftTop(-10000, -10000);
33496                 this.afterSlide();
33497                 this.afterSlideIn();
33498                 Roo.callback(cb);
33499             },
33500             scope: this,
33501             block: true
33502         });
33503     },
33504     
33505     slideInIf : function(e){
33506         if(!e.within(this.el)){
33507             this.slideIn();
33508         }
33509     },
33510
33511     animateCollapse : function(){
33512         this.beforeSlide();
33513         this.el.setStyle("z-index", 20000);
33514         var anchor = this.getSlideAnchor();
33515         this.el.slideOut(anchor, {
33516             callback : function(){
33517                 this.el.setStyle("z-index", "");
33518                 this.collapsedEl.slideIn(anchor, {duration:.3});
33519                 this.afterSlide();
33520                 this.el.setLocation(-10000,-10000);
33521                 this.el.hide();
33522                 this.fireEvent("collapsed", this);
33523             },
33524             scope: this,
33525             block: true
33526         });
33527     },
33528
33529     animateExpand : function(){
33530         this.beforeSlide();
33531         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33532         this.el.setStyle("z-index", 20000);
33533         this.collapsedEl.hide({
33534             duration:.1
33535         });
33536         this.el.slideIn(this.getSlideAnchor(), {
33537             callback : function(){
33538                 this.el.setStyle("z-index", "");
33539                 this.afterSlide();
33540                 if(this.split){
33541                     this.split.el.show();
33542                 }
33543                 this.fireEvent("invalidated", this);
33544                 this.fireEvent("expanded", this);
33545             },
33546             scope: this,
33547             block: true
33548         });
33549     },
33550
33551     anchors : {
33552         "west" : "left",
33553         "east" : "right",
33554         "north" : "top",
33555         "south" : "bottom"
33556     },
33557
33558     sanchors : {
33559         "west" : "l",
33560         "east" : "r",
33561         "north" : "t",
33562         "south" : "b"
33563     },
33564
33565     canchors : {
33566         "west" : "tl-tr",
33567         "east" : "tr-tl",
33568         "north" : "tl-bl",
33569         "south" : "bl-tl"
33570     },
33571
33572     getAnchor : function(){
33573         return this.anchors[this.position];
33574     },
33575
33576     getCollapseAnchor : function(){
33577         return this.canchors[this.position];
33578     },
33579
33580     getSlideAnchor : function(){
33581         return this.sanchors[this.position];
33582     },
33583
33584     getAlignAdj : function(){
33585         var cm = this.cmargins;
33586         switch(this.position){
33587             case "west":
33588                 return [0, 0];
33589             break;
33590             case "east":
33591                 return [0, 0];
33592             break;
33593             case "north":
33594                 return [0, 0];
33595             break;
33596             case "south":
33597                 return [0, 0];
33598             break;
33599         }
33600     },
33601
33602     getExpandAdj : function(){
33603         var c = this.collapsedEl, cm = this.cmargins;
33604         switch(this.position){
33605             case "west":
33606                 return [-(cm.right+c.getWidth()+cm.left), 0];
33607             break;
33608             case "east":
33609                 return [cm.right+c.getWidth()+cm.left, 0];
33610             break;
33611             case "north":
33612                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33613             break;
33614             case "south":
33615                 return [0, cm.top+cm.bottom+c.getHeight()];
33616             break;
33617         }
33618     }
33619 });/*
33620  * Based on:
33621  * Ext JS Library 1.1.1
33622  * Copyright(c) 2006-2007, Ext JS, LLC.
33623  *
33624  * Originally Released Under LGPL - original licence link has changed is not relivant.
33625  *
33626  * Fork - LGPL
33627  * <script type="text/javascript">
33628  */
33629 /*
33630  * These classes are private internal classes
33631  */
33632 Roo.bootstrap.layout.Center = function(config){
33633     config.region = "center";
33634     Roo.bootstrap.layout.Region.call(this, config);
33635     this.visible = true;
33636     this.minWidth = config.minWidth || 20;
33637     this.minHeight = config.minHeight || 20;
33638 };
33639
33640 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33641     hide : function(){
33642         // center panel can't be hidden
33643     },
33644     
33645     show : function(){
33646         // center panel can't be hidden
33647     },
33648     
33649     getMinWidth: function(){
33650         return this.minWidth;
33651     },
33652     
33653     getMinHeight: function(){
33654         return this.minHeight;
33655     }
33656 });
33657
33658
33659
33660
33661  
33662
33663
33664
33665
33666
33667 Roo.bootstrap.layout.North = function(config)
33668 {
33669     config.region = 'north';
33670     config.cursor = 'n-resize';
33671     
33672     Roo.bootstrap.layout.Split.call(this, config);
33673     
33674     
33675     if(this.split){
33676         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33677         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33678         this.split.el.addClass("roo-layout-split-v");
33679     }
33680     var size = config.initialSize || config.height;
33681     if(typeof size != "undefined"){
33682         this.el.setHeight(size);
33683     }
33684 };
33685 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33686 {
33687     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33688     
33689     
33690     
33691     getBox : function(){
33692         if(this.collapsed){
33693             return this.collapsedEl.getBox();
33694         }
33695         var box = this.el.getBox();
33696         if(this.split){
33697             box.height += this.split.el.getHeight();
33698         }
33699         return box;
33700     },
33701     
33702     updateBox : function(box){
33703         if(this.split && !this.collapsed){
33704             box.height -= this.split.el.getHeight();
33705             this.split.el.setLeft(box.x);
33706             this.split.el.setTop(box.y+box.height);
33707             this.split.el.setWidth(box.width);
33708         }
33709         if(this.collapsed){
33710             this.updateBody(box.width, null);
33711         }
33712         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33713     }
33714 });
33715
33716
33717
33718
33719
33720 Roo.bootstrap.layout.South = function(config){
33721     config.region = 'south';
33722     config.cursor = 's-resize';
33723     Roo.bootstrap.layout.Split.call(this, config);
33724     if(this.split){
33725         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33726         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33727         this.split.el.addClass("roo-layout-split-v");
33728     }
33729     var size = config.initialSize || config.height;
33730     if(typeof size != "undefined"){
33731         this.el.setHeight(size);
33732     }
33733 };
33734
33735 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33736     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33737     getBox : function(){
33738         if(this.collapsed){
33739             return this.collapsedEl.getBox();
33740         }
33741         var box = this.el.getBox();
33742         if(this.split){
33743             var sh = this.split.el.getHeight();
33744             box.height += sh;
33745             box.y -= sh;
33746         }
33747         return box;
33748     },
33749     
33750     updateBox : function(box){
33751         if(this.split && !this.collapsed){
33752             var sh = this.split.el.getHeight();
33753             box.height -= sh;
33754             box.y += sh;
33755             this.split.el.setLeft(box.x);
33756             this.split.el.setTop(box.y-sh);
33757             this.split.el.setWidth(box.width);
33758         }
33759         if(this.collapsed){
33760             this.updateBody(box.width, null);
33761         }
33762         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33763     }
33764 });
33765
33766 Roo.bootstrap.layout.East = function(config){
33767     config.region = "east";
33768     config.cursor = "e-resize";
33769     Roo.bootstrap.layout.Split.call(this, config);
33770     if(this.split){
33771         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33772         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33773         this.split.el.addClass("roo-layout-split-h");
33774     }
33775     var size = config.initialSize || config.width;
33776     if(typeof size != "undefined"){
33777         this.el.setWidth(size);
33778     }
33779 };
33780 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33781     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33782     getBox : function(){
33783         if(this.collapsed){
33784             return this.collapsedEl.getBox();
33785         }
33786         var box = this.el.getBox();
33787         if(this.split){
33788             var sw = this.split.el.getWidth();
33789             box.width += sw;
33790             box.x -= sw;
33791         }
33792         return box;
33793     },
33794
33795     updateBox : function(box){
33796         if(this.split && !this.collapsed){
33797             var sw = this.split.el.getWidth();
33798             box.width -= sw;
33799             this.split.el.setLeft(box.x);
33800             this.split.el.setTop(box.y);
33801             this.split.el.setHeight(box.height);
33802             box.x += sw;
33803         }
33804         if(this.collapsed){
33805             this.updateBody(null, box.height);
33806         }
33807         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33808     }
33809 });
33810
33811 Roo.bootstrap.layout.West = function(config){
33812     config.region = "west";
33813     config.cursor = "w-resize";
33814     
33815     Roo.bootstrap.layout.Split.call(this, config);
33816     if(this.split){
33817         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33818         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33819         this.split.el.addClass("roo-layout-split-h");
33820     }
33821     
33822 };
33823 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33824     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33825     
33826     onRender: function(ctr, pos)
33827     {
33828         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33829         var size = this.config.initialSize || this.config.width;
33830         if(typeof size != "undefined"){
33831             this.el.setWidth(size);
33832         }
33833     },
33834     
33835     getBox : function(){
33836         if(this.collapsed){
33837             return this.collapsedEl.getBox();
33838         }
33839         var box = this.el.getBox();
33840         if(this.split){
33841             box.width += this.split.el.getWidth();
33842         }
33843         return box;
33844     },
33845     
33846     updateBox : function(box){
33847         if(this.split && !this.collapsed){
33848             var sw = this.split.el.getWidth();
33849             box.width -= sw;
33850             this.split.el.setLeft(box.x+box.width);
33851             this.split.el.setTop(box.y);
33852             this.split.el.setHeight(box.height);
33853         }
33854         if(this.collapsed){
33855             this.updateBody(null, box.height);
33856         }
33857         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33858     }
33859 });
33860 Roo.namespace("Roo.bootstrap.panel");/*
33861  * Based on:
33862  * Ext JS Library 1.1.1
33863  * Copyright(c) 2006-2007, Ext JS, LLC.
33864  *
33865  * Originally Released Under LGPL - original licence link has changed is not relivant.
33866  *
33867  * Fork - LGPL
33868  * <script type="text/javascript">
33869  */
33870 /**
33871  * @class Roo.ContentPanel
33872  * @extends Roo.util.Observable
33873  * A basic ContentPanel element.
33874  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33875  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33876  * @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
33877  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33878  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33879  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33880  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33881  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33882  * @cfg {String} title          The title for this panel
33883  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33884  * @cfg {String} url            Calls {@link #setUrl} with this value
33885  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33886  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33887  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33888  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33889
33890  * @constructor
33891  * Create a new ContentPanel.
33892  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33893  * @param {String/Object} config A string to set only the title or a config object
33894  * @param {String} content (optional) Set the HTML content for this panel
33895  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33896  */
33897 Roo.bootstrap.panel.Content = function( config){
33898     
33899     var el = config.el;
33900     var content = config.content;
33901
33902     if(config.autoCreate){ // xtype is available if this is called from factory
33903         el = Roo.id();
33904     }
33905     this.el = Roo.get(el);
33906     if(!this.el && config && config.autoCreate){
33907         if(typeof config.autoCreate == "object"){
33908             if(!config.autoCreate.id){
33909                 config.autoCreate.id = config.id||el;
33910             }
33911             this.el = Roo.DomHelper.append(document.body,
33912                         config.autoCreate, true);
33913         }else{
33914             var elcfg =  {   tag: "div",
33915                             cls: "roo-layout-inactive-content",
33916                             id: config.id||el
33917                             };
33918             if (config.html) {
33919                 elcfg.html = config.html;
33920                 
33921             }
33922                         
33923             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33924         }
33925     } 
33926     this.closable = false;
33927     this.loaded = false;
33928     this.active = false;
33929    
33930       
33931     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33932         
33933         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33934         
33935         this.wrapEl = this.el.wrap();
33936         var ti = [];
33937         if (config.toolbar.items) {
33938             ti = config.toolbar.items ;
33939             delete config.toolbar.items ;
33940         }
33941         
33942         var nitems = [];
33943         this.toolbar.render(this.wrapEl, 'before');
33944         for(var i =0;i < ti.length;i++) {
33945           //  Roo.log(['add child', items[i]]);
33946             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33947         }
33948         this.toolbar.items = nitems;
33949         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33950         delete config.toolbar;
33951         
33952     }
33953     /*
33954     // xtype created footer. - not sure if will work as we normally have to render first..
33955     if (this.footer && !this.footer.el && this.footer.xtype) {
33956         if (!this.wrapEl) {
33957             this.wrapEl = this.el.wrap();
33958         }
33959     
33960         this.footer.container = this.wrapEl.createChild();
33961          
33962         this.footer = Roo.factory(this.footer, Roo);
33963         
33964     }
33965     */
33966     
33967      if(typeof config == "string"){
33968         this.title = config;
33969     }else{
33970         Roo.apply(this, config);
33971     }
33972     
33973     if(this.resizeEl){
33974         this.resizeEl = Roo.get(this.resizeEl, true);
33975     }else{
33976         this.resizeEl = this.el;
33977     }
33978     // handle view.xtype
33979     
33980  
33981     
33982     
33983     this.addEvents({
33984         /**
33985          * @event activate
33986          * Fires when this panel is activated. 
33987          * @param {Roo.ContentPanel} this
33988          */
33989         "activate" : true,
33990         /**
33991          * @event deactivate
33992          * Fires when this panel is activated. 
33993          * @param {Roo.ContentPanel} this
33994          */
33995         "deactivate" : true,
33996
33997         /**
33998          * @event resize
33999          * Fires when this panel is resized if fitToFrame is true.
34000          * @param {Roo.ContentPanel} this
34001          * @param {Number} width The width after any component adjustments
34002          * @param {Number} height The height after any component adjustments
34003          */
34004         "resize" : true,
34005         
34006          /**
34007          * @event render
34008          * Fires when this tab is created
34009          * @param {Roo.ContentPanel} this
34010          */
34011         "render" : true
34012         
34013         
34014         
34015     });
34016     
34017
34018     
34019     
34020     if(this.autoScroll){
34021         this.resizeEl.setStyle("overflow", "auto");
34022     } else {
34023         // fix randome scrolling
34024         //this.el.on('scroll', function() {
34025         //    Roo.log('fix random scolling');
34026         //    this.scrollTo('top',0); 
34027         //});
34028     }
34029     content = content || this.content;
34030     if(content){
34031         this.setContent(content);
34032     }
34033     if(config && config.url){
34034         this.setUrl(this.url, this.params, this.loadOnce);
34035     }
34036     
34037     
34038     
34039     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34040     
34041     if (this.view && typeof(this.view.xtype) != 'undefined') {
34042         this.view.el = this.el.appendChild(document.createElement("div"));
34043         this.view = Roo.factory(this.view); 
34044         this.view.render  &&  this.view.render(false, '');  
34045     }
34046     
34047     
34048     this.fireEvent('render', this);
34049 };
34050
34051 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34052     tabTip:'',
34053     setRegion : function(region){
34054         this.region = region;
34055         if(region){
34056            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34057         }else{
34058            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34059         } 
34060     },
34061     
34062     /**
34063      * Returns the toolbar for this Panel if one was configured. 
34064      * @return {Roo.Toolbar} 
34065      */
34066     getToolbar : function(){
34067         return this.toolbar;
34068     },
34069     
34070     setActiveState : function(active){
34071         this.active = active;
34072         if(!active){
34073             this.fireEvent("deactivate", this);
34074         }else{
34075             this.fireEvent("activate", this);
34076         }
34077     },
34078     /**
34079      * Updates this panel's element
34080      * @param {String} content The new content
34081      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34082     */
34083     setContent : function(content, loadScripts){
34084         this.el.update(content, loadScripts);
34085     },
34086
34087     ignoreResize : function(w, h){
34088         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34089             return true;
34090         }else{
34091             this.lastSize = {width: w, height: h};
34092             return false;
34093         }
34094     },
34095     /**
34096      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34097      * @return {Roo.UpdateManager} The UpdateManager
34098      */
34099     getUpdateManager : function(){
34100         return this.el.getUpdateManager();
34101     },
34102      /**
34103      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34104      * @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:
34105 <pre><code>
34106 panel.load({
34107     url: "your-url.php",
34108     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34109     callback: yourFunction,
34110     scope: yourObject, //(optional scope)
34111     discardUrl: false,
34112     nocache: false,
34113     text: "Loading...",
34114     timeout: 30,
34115     scripts: false
34116 });
34117 </code></pre>
34118      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34119      * 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.
34120      * @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}
34121      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34122      * @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.
34123      * @return {Roo.ContentPanel} this
34124      */
34125     load : function(){
34126         var um = this.el.getUpdateManager();
34127         um.update.apply(um, arguments);
34128         return this;
34129     },
34130
34131
34132     /**
34133      * 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.
34134      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34135      * @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)
34136      * @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)
34137      * @return {Roo.UpdateManager} The UpdateManager
34138      */
34139     setUrl : function(url, params, loadOnce){
34140         if(this.refreshDelegate){
34141             this.removeListener("activate", this.refreshDelegate);
34142         }
34143         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34144         this.on("activate", this.refreshDelegate);
34145         return this.el.getUpdateManager();
34146     },
34147     
34148     _handleRefresh : function(url, params, loadOnce){
34149         if(!loadOnce || !this.loaded){
34150             var updater = this.el.getUpdateManager();
34151             updater.update(url, params, this._setLoaded.createDelegate(this));
34152         }
34153     },
34154     
34155     _setLoaded : function(){
34156         this.loaded = true;
34157     }, 
34158     
34159     /**
34160      * Returns this panel's id
34161      * @return {String} 
34162      */
34163     getId : function(){
34164         return this.el.id;
34165     },
34166     
34167     /** 
34168      * Returns this panel's element - used by regiosn to add.
34169      * @return {Roo.Element} 
34170      */
34171     getEl : function(){
34172         return this.wrapEl || this.el;
34173     },
34174     
34175    
34176     
34177     adjustForComponents : function(width, height)
34178     {
34179         //Roo.log('adjustForComponents ');
34180         if(this.resizeEl != this.el){
34181             width -= this.el.getFrameWidth('lr');
34182             height -= this.el.getFrameWidth('tb');
34183         }
34184         if(this.toolbar){
34185             var te = this.toolbar.getEl();
34186             height -= te.getHeight();
34187             te.setWidth(width);
34188         }
34189         if(this.footer){
34190             var te = this.footer.getEl();
34191             Roo.log("footer:" + te.getHeight());
34192             
34193             height -= te.getHeight();
34194             te.setWidth(width);
34195         }
34196         
34197         
34198         if(this.adjustments){
34199             width += this.adjustments[0];
34200             height += this.adjustments[1];
34201         }
34202         return {"width": width, "height": height};
34203     },
34204     
34205     setSize : function(width, height){
34206         if(this.fitToFrame && !this.ignoreResize(width, height)){
34207             if(this.fitContainer && this.resizeEl != this.el){
34208                 this.el.setSize(width, height);
34209             }
34210             var size = this.adjustForComponents(width, height);
34211             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34212             this.fireEvent('resize', this, size.width, size.height);
34213         }
34214     },
34215     
34216     /**
34217      * Returns this panel's title
34218      * @return {String} 
34219      */
34220     getTitle : function(){
34221         return this.title;
34222     },
34223     
34224     /**
34225      * Set this panel's title
34226      * @param {String} title
34227      */
34228     setTitle : function(title){
34229         this.title = title;
34230         if(this.region){
34231             this.region.updatePanelTitle(this, title);
34232         }
34233     },
34234     
34235     /**
34236      * Returns true is this panel was configured to be closable
34237      * @return {Boolean} 
34238      */
34239     isClosable : function(){
34240         return this.closable;
34241     },
34242     
34243     beforeSlide : function(){
34244         this.el.clip();
34245         this.resizeEl.clip();
34246     },
34247     
34248     afterSlide : function(){
34249         this.el.unclip();
34250         this.resizeEl.unclip();
34251     },
34252     
34253     /**
34254      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34255      *   Will fail silently if the {@link #setUrl} method has not been called.
34256      *   This does not activate the panel, just updates its content.
34257      */
34258     refresh : function(){
34259         if(this.refreshDelegate){
34260            this.loaded = false;
34261            this.refreshDelegate();
34262         }
34263     },
34264     
34265     /**
34266      * Destroys this panel
34267      */
34268     destroy : function(){
34269         this.el.removeAllListeners();
34270         var tempEl = document.createElement("span");
34271         tempEl.appendChild(this.el.dom);
34272         tempEl.innerHTML = "";
34273         this.el.remove();
34274         this.el = null;
34275     },
34276     
34277     /**
34278      * form - if the content panel contains a form - this is a reference to it.
34279      * @type {Roo.form.Form}
34280      */
34281     form : false,
34282     /**
34283      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34284      *    This contains a reference to it.
34285      * @type {Roo.View}
34286      */
34287     view : false,
34288     
34289       /**
34290      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34291      * <pre><code>
34292
34293 layout.addxtype({
34294        xtype : 'Form',
34295        items: [ .... ]
34296    }
34297 );
34298
34299 </code></pre>
34300      * @param {Object} cfg Xtype definition of item to add.
34301      */
34302     
34303     
34304     getChildContainer: function () {
34305         return this.getEl();
34306     }
34307     
34308     
34309     /*
34310         var  ret = new Roo.factory(cfg);
34311         return ret;
34312         
34313         
34314         // add form..
34315         if (cfg.xtype.match(/^Form$/)) {
34316             
34317             var el;
34318             //if (this.footer) {
34319             //    el = this.footer.container.insertSibling(false, 'before');
34320             //} else {
34321                 el = this.el.createChild();
34322             //}
34323
34324             this.form = new  Roo.form.Form(cfg);
34325             
34326             
34327             if ( this.form.allItems.length) {
34328                 this.form.render(el.dom);
34329             }
34330             return this.form;
34331         }
34332         // should only have one of theses..
34333         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34334             // views.. should not be just added - used named prop 'view''
34335             
34336             cfg.el = this.el.appendChild(document.createElement("div"));
34337             // factory?
34338             
34339             var ret = new Roo.factory(cfg);
34340              
34341              ret.render && ret.render(false, ''); // render blank..
34342             this.view = ret;
34343             return ret;
34344         }
34345         return false;
34346     }
34347     \*/
34348 });
34349  
34350 /**
34351  * @class Roo.bootstrap.panel.Grid
34352  * @extends Roo.bootstrap.panel.Content
34353  * @constructor
34354  * Create a new GridPanel.
34355  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34356  * @param {Object} config A the config object
34357   
34358  */
34359
34360
34361
34362 Roo.bootstrap.panel.Grid = function(config)
34363 {
34364     
34365       
34366     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34367         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34368
34369     config.el = this.wrapper;
34370     //this.el = this.wrapper;
34371     
34372       if (config.container) {
34373         // ctor'ed from a Border/panel.grid
34374         
34375         
34376         this.wrapper.setStyle("overflow", "hidden");
34377         this.wrapper.addClass('roo-grid-container');
34378
34379     }
34380     
34381     
34382     if(config.toolbar){
34383         var tool_el = this.wrapper.createChild();    
34384         this.toolbar = Roo.factory(config.toolbar);
34385         var ti = [];
34386         if (config.toolbar.items) {
34387             ti = config.toolbar.items ;
34388             delete config.toolbar.items ;
34389         }
34390         
34391         var nitems = [];
34392         this.toolbar.render(tool_el);
34393         for(var i =0;i < ti.length;i++) {
34394           //  Roo.log(['add child', items[i]]);
34395             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34396         }
34397         this.toolbar.items = nitems;
34398         
34399         delete config.toolbar;
34400     }
34401     
34402     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34403     config.grid.scrollBody = true;;
34404     config.grid.monitorWindowResize = false; // turn off autosizing
34405     config.grid.autoHeight = false;
34406     config.grid.autoWidth = false;
34407     
34408     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34409     
34410     if (config.background) {
34411         // render grid on panel activation (if panel background)
34412         this.on('activate', function(gp) {
34413             if (!gp.grid.rendered) {
34414                 gp.grid.render(el);
34415                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34416
34417             }
34418         });
34419             
34420     } else {
34421         this.grid.render(this.wrapper);
34422         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34423
34424     }
34425     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34426     // ??? needed ??? config.el = this.wrapper;
34427     
34428     
34429     
34430   
34431     // xtype created footer. - not sure if will work as we normally have to render first..
34432     if (this.footer && !this.footer.el && this.footer.xtype) {
34433         
34434         var ctr = this.grid.getView().getFooterPanel(true);
34435         this.footer.dataSource = this.grid.dataSource;
34436         this.footer = Roo.factory(this.footer, Roo);
34437         this.footer.render(ctr);
34438         
34439     }
34440     
34441     
34442     
34443     
34444      
34445 };
34446
34447 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34448     getId : function(){
34449         return this.grid.id;
34450     },
34451     
34452     /**
34453      * Returns the grid for this panel
34454      * @return {Roo.bootstrap.Table} 
34455      */
34456     getGrid : function(){
34457         return this.grid;    
34458     },
34459     
34460     setSize : function(width, height){
34461         if(!this.ignoreResize(width, height)){
34462             var grid = this.grid;
34463             var size = this.adjustForComponents(width, height);
34464             var gridel = grid.getGridEl();
34465             gridel.setSize(size.width, size.height);
34466             /*
34467             var thd = grid.getGridEl().select('thead',true).first();
34468             var tbd = grid.getGridEl().select('tbody', true).first();
34469             if (tbd) {
34470                 tbd.setSize(width, height - thd.getHeight());
34471             }
34472             */
34473             grid.autoSize();
34474         }
34475     },
34476      
34477     
34478     
34479     beforeSlide : function(){
34480         this.grid.getView().scroller.clip();
34481     },
34482     
34483     afterSlide : function(){
34484         this.grid.getView().scroller.unclip();
34485     },
34486     
34487     destroy : function(){
34488         this.grid.destroy();
34489         delete this.grid;
34490         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34491     }
34492 });
34493
34494 /**
34495  * @class Roo.bootstrap.panel.Nest
34496  * @extends Roo.bootstrap.panel.Content
34497  * @constructor
34498  * Create a new Panel, that can contain a layout.Border.
34499  * 
34500  * 
34501  * @param {Roo.BorderLayout} layout The layout for this panel
34502  * @param {String/Object} config A string to set only the title or a config object
34503  */
34504 Roo.bootstrap.panel.Nest = function(config)
34505 {
34506     // construct with only one argument..
34507     /* FIXME - implement nicer consturctors
34508     if (layout.layout) {
34509         config = layout;
34510         layout = config.layout;
34511         delete config.layout;
34512     }
34513     if (layout.xtype && !layout.getEl) {
34514         // then layout needs constructing..
34515         layout = Roo.factory(layout, Roo);
34516     }
34517     */
34518     
34519     config.el =  config.layout.getEl();
34520     
34521     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34522     
34523     config.layout.monitorWindowResize = false; // turn off autosizing
34524     this.layout = config.layout;
34525     this.layout.getEl().addClass("roo-layout-nested-layout");
34526     
34527     
34528     
34529     
34530 };
34531
34532 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34533
34534     setSize : function(width, height){
34535         if(!this.ignoreResize(width, height)){
34536             var size = this.adjustForComponents(width, height);
34537             var el = this.layout.getEl();
34538             el.setSize(size.width, size.height);
34539             var touch = el.dom.offsetWidth;
34540             this.layout.layout();
34541             // ie requires a double layout on the first pass
34542             if(Roo.isIE && !this.initialized){
34543                 this.initialized = true;
34544                 this.layout.layout();
34545             }
34546         }
34547     },
34548     
34549     // activate all subpanels if not currently active..
34550     
34551     setActiveState : function(active){
34552         this.active = active;
34553         if(!active){
34554             this.fireEvent("deactivate", this);
34555             return;
34556         }
34557         
34558         this.fireEvent("activate", this);
34559         // not sure if this should happen before or after..
34560         if (!this.layout) {
34561             return; // should not happen..
34562         }
34563         var reg = false;
34564         for (var r in this.layout.regions) {
34565             reg = this.layout.getRegion(r);
34566             if (reg.getActivePanel()) {
34567                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34568                 reg.setActivePanel(reg.getActivePanel());
34569                 continue;
34570             }
34571             if (!reg.panels.length) {
34572                 continue;
34573             }
34574             reg.showPanel(reg.getPanel(0));
34575         }
34576         
34577         
34578         
34579         
34580     },
34581     
34582     /**
34583      * Returns the nested BorderLayout for this panel
34584      * @return {Roo.BorderLayout} 
34585      */
34586     getLayout : function(){
34587         return this.layout;
34588     },
34589     
34590      /**
34591      * Adds a xtype elements to the layout of the nested panel
34592      * <pre><code>
34593
34594 panel.addxtype({
34595        xtype : 'ContentPanel',
34596        region: 'west',
34597        items: [ .... ]
34598    }
34599 );
34600
34601 panel.addxtype({
34602         xtype : 'NestedLayoutPanel',
34603         region: 'west',
34604         layout: {
34605            center: { },
34606            west: { }   
34607         },
34608         items : [ ... list of content panels or nested layout panels.. ]
34609    }
34610 );
34611 </code></pre>
34612      * @param {Object} cfg Xtype definition of item to add.
34613      */
34614     addxtype : function(cfg) {
34615         return this.layout.addxtype(cfg);
34616     
34617     }
34618 });        /*
34619  * Based on:
34620  * Ext JS Library 1.1.1
34621  * Copyright(c) 2006-2007, Ext JS, LLC.
34622  *
34623  * Originally Released Under LGPL - original licence link has changed is not relivant.
34624  *
34625  * Fork - LGPL
34626  * <script type="text/javascript">
34627  */
34628 /**
34629  * @class Roo.TabPanel
34630  * @extends Roo.util.Observable
34631  * A lightweight tab container.
34632  * <br><br>
34633  * Usage:
34634  * <pre><code>
34635 // basic tabs 1, built from existing content
34636 var tabs = new Roo.TabPanel("tabs1");
34637 tabs.addTab("script", "View Script");
34638 tabs.addTab("markup", "View Markup");
34639 tabs.activate("script");
34640
34641 // more advanced tabs, built from javascript
34642 var jtabs = new Roo.TabPanel("jtabs");
34643 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34644
34645 // set up the UpdateManager
34646 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34647 var updater = tab2.getUpdateManager();
34648 updater.setDefaultUrl("ajax1.htm");
34649 tab2.on('activate', updater.refresh, updater, true);
34650
34651 // Use setUrl for Ajax loading
34652 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34653 tab3.setUrl("ajax2.htm", null, true);
34654
34655 // Disabled tab
34656 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34657 tab4.disable();
34658
34659 jtabs.activate("jtabs-1");
34660  * </code></pre>
34661  * @constructor
34662  * Create a new TabPanel.
34663  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34664  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34665  */
34666 Roo.bootstrap.panel.Tabs = function(config){
34667     /**
34668     * The container element for this TabPanel.
34669     * @type Roo.Element
34670     */
34671     this.el = Roo.get(config.el);
34672     delete config.el;
34673     if(config){
34674         if(typeof config == "boolean"){
34675             this.tabPosition = config ? "bottom" : "top";
34676         }else{
34677             Roo.apply(this, config);
34678         }
34679     }
34680     
34681     if(this.tabPosition == "bottom"){
34682         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34683         this.el.addClass("roo-tabs-bottom");
34684     }
34685     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34686     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34687     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34688     if(Roo.isIE){
34689         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34690     }
34691     if(this.tabPosition != "bottom"){
34692         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34693          * @type Roo.Element
34694          */
34695         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34696         this.el.addClass("roo-tabs-top");
34697     }
34698     this.items = [];
34699
34700     this.bodyEl.setStyle("position", "relative");
34701
34702     this.active = null;
34703     this.activateDelegate = this.activate.createDelegate(this);
34704
34705     this.addEvents({
34706         /**
34707          * @event tabchange
34708          * Fires when the active tab changes
34709          * @param {Roo.TabPanel} this
34710          * @param {Roo.TabPanelItem} activePanel The new active tab
34711          */
34712         "tabchange": true,
34713         /**
34714          * @event beforetabchange
34715          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34716          * @param {Roo.TabPanel} this
34717          * @param {Object} e Set cancel to true on this object to cancel the tab change
34718          * @param {Roo.TabPanelItem} tab The tab being changed to
34719          */
34720         "beforetabchange" : true
34721     });
34722
34723     Roo.EventManager.onWindowResize(this.onResize, this);
34724     this.cpad = this.el.getPadding("lr");
34725     this.hiddenCount = 0;
34726
34727
34728     // toolbar on the tabbar support...
34729     if (this.toolbar) {
34730         alert("no toolbar support yet");
34731         this.toolbar  = false;
34732         /*
34733         var tcfg = this.toolbar;
34734         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34735         this.toolbar = new Roo.Toolbar(tcfg);
34736         if (Roo.isSafari) {
34737             var tbl = tcfg.container.child('table', true);
34738             tbl.setAttribute('width', '100%');
34739         }
34740         */
34741         
34742     }
34743    
34744
34745
34746     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34747 };
34748
34749 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34750     /*
34751      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34752      */
34753     tabPosition : "top",
34754     /*
34755      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34756      */
34757     currentTabWidth : 0,
34758     /*
34759      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34760      */
34761     minTabWidth : 40,
34762     /*
34763      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34764      */
34765     maxTabWidth : 250,
34766     /*
34767      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34768      */
34769     preferredTabWidth : 175,
34770     /*
34771      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34772      */
34773     resizeTabs : false,
34774     /*
34775      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34776      */
34777     monitorResize : true,
34778     /*
34779      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34780      */
34781     toolbar : false,
34782
34783     /**
34784      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34785      * @param {String} id The id of the div to use <b>or create</b>
34786      * @param {String} text The text for the tab
34787      * @param {String} content (optional) Content to put in the TabPanelItem body
34788      * @param {Boolean} closable (optional) True to create a close icon on the tab
34789      * @return {Roo.TabPanelItem} The created TabPanelItem
34790      */
34791     addTab : function(id, text, content, closable)
34792     {
34793         var item = new Roo.bootstrap.panel.TabItem({
34794             panel: this,
34795             id : id,
34796             text : text,
34797             closable : closable
34798         });
34799         this.addTabItem(item);
34800         if(content){
34801             item.setContent(content);
34802         }
34803         return item;
34804     },
34805
34806     /**
34807      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34808      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34809      * @return {Roo.TabPanelItem}
34810      */
34811     getTab : function(id){
34812         return this.items[id];
34813     },
34814
34815     /**
34816      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34817      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34818      */
34819     hideTab : function(id){
34820         var t = this.items[id];
34821         if(!t.isHidden()){
34822            t.setHidden(true);
34823            this.hiddenCount++;
34824            this.autoSizeTabs();
34825         }
34826     },
34827
34828     /**
34829      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34830      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34831      */
34832     unhideTab : function(id){
34833         var t = this.items[id];
34834         if(t.isHidden()){
34835            t.setHidden(false);
34836            this.hiddenCount--;
34837            this.autoSizeTabs();
34838         }
34839     },
34840
34841     /**
34842      * Adds an existing {@link Roo.TabPanelItem}.
34843      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34844      */
34845     addTabItem : function(item){
34846         this.items[item.id] = item;
34847         this.items.push(item);
34848       //  if(this.resizeTabs){
34849     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34850   //         this.autoSizeTabs();
34851 //        }else{
34852 //            item.autoSize();
34853        // }
34854     },
34855
34856     /**
34857      * Removes a {@link Roo.TabPanelItem}.
34858      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34859      */
34860     removeTab : function(id){
34861         var items = this.items;
34862         var tab = items[id];
34863         if(!tab) { return; }
34864         var index = items.indexOf(tab);
34865         if(this.active == tab && items.length > 1){
34866             var newTab = this.getNextAvailable(index);
34867             if(newTab) {
34868                 newTab.activate();
34869             }
34870         }
34871         this.stripEl.dom.removeChild(tab.pnode.dom);
34872         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34873             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34874         }
34875         items.splice(index, 1);
34876         delete this.items[tab.id];
34877         tab.fireEvent("close", tab);
34878         tab.purgeListeners();
34879         this.autoSizeTabs();
34880     },
34881
34882     getNextAvailable : function(start){
34883         var items = this.items;
34884         var index = start;
34885         // look for a next tab that will slide over to
34886         // replace the one being removed
34887         while(index < items.length){
34888             var item = items[++index];
34889             if(item && !item.isHidden()){
34890                 return item;
34891             }
34892         }
34893         // if one isn't found select the previous tab (on the left)
34894         index = start;
34895         while(index >= 0){
34896             var item = items[--index];
34897             if(item && !item.isHidden()){
34898                 return item;
34899             }
34900         }
34901         return null;
34902     },
34903
34904     /**
34905      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34906      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34907      */
34908     disableTab : function(id){
34909         var tab = this.items[id];
34910         if(tab && this.active != tab){
34911             tab.disable();
34912         }
34913     },
34914
34915     /**
34916      * Enables a {@link Roo.TabPanelItem} that is disabled.
34917      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34918      */
34919     enableTab : function(id){
34920         var tab = this.items[id];
34921         tab.enable();
34922     },
34923
34924     /**
34925      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34926      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34927      * @return {Roo.TabPanelItem} The TabPanelItem.
34928      */
34929     activate : function(id){
34930         var tab = this.items[id];
34931         if(!tab){
34932             return null;
34933         }
34934         if(tab == this.active || tab.disabled){
34935             return tab;
34936         }
34937         var e = {};
34938         this.fireEvent("beforetabchange", this, e, tab);
34939         if(e.cancel !== true && !tab.disabled){
34940             if(this.active){
34941                 this.active.hide();
34942             }
34943             this.active = this.items[id];
34944             this.active.show();
34945             this.fireEvent("tabchange", this, this.active);
34946         }
34947         return tab;
34948     },
34949
34950     /**
34951      * Gets the active {@link Roo.TabPanelItem}.
34952      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34953      */
34954     getActiveTab : function(){
34955         return this.active;
34956     },
34957
34958     /**
34959      * Updates the tab body element to fit the height of the container element
34960      * for overflow scrolling
34961      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34962      */
34963     syncHeight : function(targetHeight){
34964         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34965         var bm = this.bodyEl.getMargins();
34966         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34967         this.bodyEl.setHeight(newHeight);
34968         return newHeight;
34969     },
34970
34971     onResize : function(){
34972         if(this.monitorResize){
34973             this.autoSizeTabs();
34974         }
34975     },
34976
34977     /**
34978      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34979      */
34980     beginUpdate : function(){
34981         this.updating = true;
34982     },
34983
34984     /**
34985      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34986      */
34987     endUpdate : function(){
34988         this.updating = false;
34989         this.autoSizeTabs();
34990     },
34991
34992     /**
34993      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34994      */
34995     autoSizeTabs : function(){
34996         var count = this.items.length;
34997         var vcount = count - this.hiddenCount;
34998         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34999             return;
35000         }
35001         var w = Math.max(this.el.getWidth() - this.cpad, 10);
35002         var availWidth = Math.floor(w / vcount);
35003         var b = this.stripBody;
35004         if(b.getWidth() > w){
35005             var tabs = this.items;
35006             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35007             if(availWidth < this.minTabWidth){
35008                 /*if(!this.sleft){    // incomplete scrolling code
35009                     this.createScrollButtons();
35010                 }
35011                 this.showScroll();
35012                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35013             }
35014         }else{
35015             if(this.currentTabWidth < this.preferredTabWidth){
35016                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35017             }
35018         }
35019     },
35020
35021     /**
35022      * Returns the number of tabs in this TabPanel.
35023      * @return {Number}
35024      */
35025      getCount : function(){
35026          return this.items.length;
35027      },
35028
35029     /**
35030      * Resizes all the tabs to the passed width
35031      * @param {Number} The new width
35032      */
35033     setTabWidth : function(width){
35034         this.currentTabWidth = width;
35035         for(var i = 0, len = this.items.length; i < len; i++) {
35036                 if(!this.items[i].isHidden()) {
35037                 this.items[i].setWidth(width);
35038             }
35039         }
35040     },
35041
35042     /**
35043      * Destroys this TabPanel
35044      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35045      */
35046     destroy : function(removeEl){
35047         Roo.EventManager.removeResizeListener(this.onResize, this);
35048         for(var i = 0, len = this.items.length; i < len; i++){
35049             this.items[i].purgeListeners();
35050         }
35051         if(removeEl === true){
35052             this.el.update("");
35053             this.el.remove();
35054         }
35055     },
35056     
35057     createStrip : function(container)
35058     {
35059         var strip = document.createElement("nav");
35060         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35061         container.appendChild(strip);
35062         return strip;
35063     },
35064     
35065     createStripList : function(strip)
35066     {
35067         // div wrapper for retard IE
35068         // returns the "tr" element.
35069         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35070         //'<div class="x-tabs-strip-wrap">'+
35071           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35072           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35073         return strip.firstChild; //.firstChild.firstChild.firstChild;
35074     },
35075     createBody : function(container)
35076     {
35077         var body = document.createElement("div");
35078         Roo.id(body, "tab-body");
35079         //Roo.fly(body).addClass("x-tabs-body");
35080         Roo.fly(body).addClass("tab-content");
35081         container.appendChild(body);
35082         return body;
35083     },
35084     createItemBody :function(bodyEl, id){
35085         var body = Roo.getDom(id);
35086         if(!body){
35087             body = document.createElement("div");
35088             body.id = id;
35089         }
35090         //Roo.fly(body).addClass("x-tabs-item-body");
35091         Roo.fly(body).addClass("tab-pane");
35092          bodyEl.insertBefore(body, bodyEl.firstChild);
35093         return body;
35094     },
35095     /** @private */
35096     createStripElements :  function(stripEl, text, closable)
35097     {
35098         var td = document.createElement("li"); // was td..
35099         
35100         
35101         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35102         
35103         
35104         stripEl.appendChild(td);
35105         /*if(closable){
35106             td.className = "x-tabs-closable";
35107             if(!this.closeTpl){
35108                 this.closeTpl = new Roo.Template(
35109                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35110                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35111                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
35112                 );
35113             }
35114             var el = this.closeTpl.overwrite(td, {"text": text});
35115             var close = el.getElementsByTagName("div")[0];
35116             var inner = el.getElementsByTagName("em")[0];
35117             return {"el": el, "close": close, "inner": inner};
35118         } else {
35119         */
35120         // not sure what this is..
35121             if(!this.tabTpl){
35122                 //this.tabTpl = new Roo.Template(
35123                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35124                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35125                 //);
35126                 this.tabTpl = new Roo.Template(
35127                    '<a href="#">' +
35128                    '<span unselectable="on"' +
35129                             (this.disableTooltips ? '' : ' title="{text}"') +
35130                             ' >{text}</span></span></a>'
35131                 );
35132                 
35133             }
35134             var el = this.tabTpl.overwrite(td, {"text": text});
35135             var inner = el.getElementsByTagName("span")[0];
35136             return {"el": el, "inner": inner};
35137         //}
35138     }
35139         
35140     
35141 });
35142
35143 /**
35144  * @class Roo.TabPanelItem
35145  * @extends Roo.util.Observable
35146  * Represents an individual item (tab plus body) in a TabPanel.
35147  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35148  * @param {String} id The id of this TabPanelItem
35149  * @param {String} text The text for the tab of this TabPanelItem
35150  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35151  */
35152 Roo.bootstrap.panel.TabItem = function(config){
35153     /**
35154      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35155      * @type Roo.TabPanel
35156      */
35157     this.tabPanel = config.panel;
35158     /**
35159      * The id for this TabPanelItem
35160      * @type String
35161      */
35162     this.id = config.id;
35163     /** @private */
35164     this.disabled = false;
35165     /** @private */
35166     this.text = config.text;
35167     /** @private */
35168     this.loaded = false;
35169     this.closable = config.closable;
35170
35171     /**
35172      * The body element for this TabPanelItem.
35173      * @type Roo.Element
35174      */
35175     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35176     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35177     this.bodyEl.setStyle("display", "block");
35178     this.bodyEl.setStyle("zoom", "1");
35179     //this.hideAction();
35180
35181     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35182     /** @private */
35183     this.el = Roo.get(els.el);
35184     this.inner = Roo.get(els.inner, true);
35185     this.textEl = Roo.get(this.el.dom.firstChild, true);
35186     this.pnode = Roo.get(els.el.parentNode, true);
35187     this.el.on("mousedown", this.onTabMouseDown, this);
35188     this.el.on("click", this.onTabClick, this);
35189     /** @private */
35190     if(config.closable){
35191         var c = Roo.get(els.close, true);
35192         c.dom.title = this.closeText;
35193         c.addClassOnOver("close-over");
35194         c.on("click", this.closeClick, this);
35195      }
35196
35197     this.addEvents({
35198          /**
35199          * @event activate
35200          * Fires when this tab becomes the active tab.
35201          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35202          * @param {Roo.TabPanelItem} this
35203          */
35204         "activate": true,
35205         /**
35206          * @event beforeclose
35207          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35208          * @param {Roo.TabPanelItem} this
35209          * @param {Object} e Set cancel to true on this object to cancel the close.
35210          */
35211         "beforeclose": true,
35212         /**
35213          * @event close
35214          * Fires when this tab is closed.
35215          * @param {Roo.TabPanelItem} this
35216          */
35217          "close": true,
35218         /**
35219          * @event deactivate
35220          * Fires when this tab is no longer the active tab.
35221          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35222          * @param {Roo.TabPanelItem} this
35223          */
35224          "deactivate" : true
35225     });
35226     this.hidden = false;
35227
35228     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35229 };
35230
35231 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35232            {
35233     purgeListeners : function(){
35234        Roo.util.Observable.prototype.purgeListeners.call(this);
35235        this.el.removeAllListeners();
35236     },
35237     /**
35238      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35239      */
35240     show : function(){
35241         this.pnode.addClass("active");
35242         this.showAction();
35243         if(Roo.isOpera){
35244             this.tabPanel.stripWrap.repaint();
35245         }
35246         this.fireEvent("activate", this.tabPanel, this);
35247     },
35248
35249     /**
35250      * Returns true if this tab is the active tab.
35251      * @return {Boolean}
35252      */
35253     isActive : function(){
35254         return this.tabPanel.getActiveTab() == this;
35255     },
35256
35257     /**
35258      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35259      */
35260     hide : function(){
35261         this.pnode.removeClass("active");
35262         this.hideAction();
35263         this.fireEvent("deactivate", this.tabPanel, this);
35264     },
35265
35266     hideAction : function(){
35267         this.bodyEl.hide();
35268         this.bodyEl.setStyle("position", "absolute");
35269         this.bodyEl.setLeft("-20000px");
35270         this.bodyEl.setTop("-20000px");
35271     },
35272
35273     showAction : function(){
35274         this.bodyEl.setStyle("position", "relative");
35275         this.bodyEl.setTop("");
35276         this.bodyEl.setLeft("");
35277         this.bodyEl.show();
35278     },
35279
35280     /**
35281      * Set the tooltip for the tab.
35282      * @param {String} tooltip The tab's tooltip
35283      */
35284     setTooltip : function(text){
35285         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35286             this.textEl.dom.qtip = text;
35287             this.textEl.dom.removeAttribute('title');
35288         }else{
35289             this.textEl.dom.title = text;
35290         }
35291     },
35292
35293     onTabClick : function(e){
35294         e.preventDefault();
35295         this.tabPanel.activate(this.id);
35296     },
35297
35298     onTabMouseDown : function(e){
35299         e.preventDefault();
35300         this.tabPanel.activate(this.id);
35301     },
35302 /*
35303     getWidth : function(){
35304         return this.inner.getWidth();
35305     },
35306
35307     setWidth : function(width){
35308         var iwidth = width - this.pnode.getPadding("lr");
35309         this.inner.setWidth(iwidth);
35310         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35311         this.pnode.setWidth(width);
35312     },
35313 */
35314     /**
35315      * Show or hide the tab
35316      * @param {Boolean} hidden True to hide or false to show.
35317      */
35318     setHidden : function(hidden){
35319         this.hidden = hidden;
35320         this.pnode.setStyle("display", hidden ? "none" : "");
35321     },
35322
35323     /**
35324      * Returns true if this tab is "hidden"
35325      * @return {Boolean}
35326      */
35327     isHidden : function(){
35328         return this.hidden;
35329     },
35330
35331     /**
35332      * Returns the text for this tab
35333      * @return {String}
35334      */
35335     getText : function(){
35336         return this.text;
35337     },
35338     /*
35339     autoSize : function(){
35340         //this.el.beginMeasure();
35341         this.textEl.setWidth(1);
35342         /*
35343          *  #2804 [new] Tabs in Roojs
35344          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35345          */
35346         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35347         //this.el.endMeasure();
35348     //},
35349
35350     /**
35351      * Sets the text for the tab (Note: this also sets the tooltip text)
35352      * @param {String} text The tab's text and tooltip
35353      */
35354     setText : function(text){
35355         this.text = text;
35356         this.textEl.update(text);
35357         this.setTooltip(text);
35358         //if(!this.tabPanel.resizeTabs){
35359         //    this.autoSize();
35360         //}
35361     },
35362     /**
35363      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35364      */
35365     activate : function(){
35366         this.tabPanel.activate(this.id);
35367     },
35368
35369     /**
35370      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35371      */
35372     disable : function(){
35373         if(this.tabPanel.active != this){
35374             this.disabled = true;
35375             this.pnode.addClass("disabled");
35376         }
35377     },
35378
35379     /**
35380      * Enables this TabPanelItem if it was previously disabled.
35381      */
35382     enable : function(){
35383         this.disabled = false;
35384         this.pnode.removeClass("disabled");
35385     },
35386
35387     /**
35388      * Sets the content for this TabPanelItem.
35389      * @param {String} content The content
35390      * @param {Boolean} loadScripts true to look for and load scripts
35391      */
35392     setContent : function(content, loadScripts){
35393         this.bodyEl.update(content, loadScripts);
35394     },
35395
35396     /**
35397      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35398      * @return {Roo.UpdateManager} The UpdateManager
35399      */
35400     getUpdateManager : function(){
35401         return this.bodyEl.getUpdateManager();
35402     },
35403
35404     /**
35405      * Set a URL to be used to load the content for this TabPanelItem.
35406      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35407      * @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)
35408      * @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)
35409      * @return {Roo.UpdateManager} The UpdateManager
35410      */
35411     setUrl : function(url, params, loadOnce){
35412         if(this.refreshDelegate){
35413             this.un('activate', this.refreshDelegate);
35414         }
35415         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35416         this.on("activate", this.refreshDelegate);
35417         return this.bodyEl.getUpdateManager();
35418     },
35419
35420     /** @private */
35421     _handleRefresh : function(url, params, loadOnce){
35422         if(!loadOnce || !this.loaded){
35423             var updater = this.bodyEl.getUpdateManager();
35424             updater.update(url, params, this._setLoaded.createDelegate(this));
35425         }
35426     },
35427
35428     /**
35429      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35430      *   Will fail silently if the setUrl method has not been called.
35431      *   This does not activate the panel, just updates its content.
35432      */
35433     refresh : function(){
35434         if(this.refreshDelegate){
35435            this.loaded = false;
35436            this.refreshDelegate();
35437         }
35438     },
35439
35440     /** @private */
35441     _setLoaded : function(){
35442         this.loaded = true;
35443     },
35444
35445     /** @private */
35446     closeClick : function(e){
35447         var o = {};
35448         e.stopEvent();
35449         this.fireEvent("beforeclose", this, o);
35450         if(o.cancel !== true){
35451             this.tabPanel.removeTab(this.id);
35452         }
35453     },
35454     /**
35455      * The text displayed in the tooltip for the close icon.
35456      * @type String
35457      */
35458     closeText : "Close this tab"
35459 });