Roo/bootstrap/Table/RowSelectionModel.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  * 
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  * 
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394     Roo.bootstrap.Body.superclass.constructor.call(this, config);
395     this.el = Roo.get(document.body);
396     if (this.cls && this.cls.length) {
397         Roo.get(document.body).addClass(this.cls);
398     }
399 };
400
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
402     
403     is_body : true,// just to make sure it's constructed?
404     
405         autoCreate : {
406         cls: 'container'
407     },
408     onRender : function(ct, position)
409     {
410        /* Roo.log("Roo.bootstrap.Body - onRender");
411         if (this.cls && this.cls.length) {
412             Roo.get(document.body).addClass(this.cls);
413         }
414         // style??? xttr???
415         */
416     }
417     
418     
419  
420    
421 });
422
423  /*
424  * - LGPL
425  *
426  * button group
427  * 
428  */
429
430
431 /**
432  * @class Roo.bootstrap.ButtonGroup
433  * @extends Roo.bootstrap.Component
434  * Bootstrap ButtonGroup class
435  * @cfg {String} size lg | sm | xs (default empty normal)
436  * @cfg {String} align vertical | justified  (default none)
437  * @cfg {String} direction up | down (default down)
438  * @cfg {Boolean} toolbar false | true
439  * @cfg {Boolean} btn true | false
440  * 
441  * 
442  * @constructor
443  * Create a new Input
444  * @param {Object} config The config object
445  */
446
447 Roo.bootstrap.ButtonGroup = function(config){
448     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
449 };
450
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
452     
453     size: '',
454     align: '',
455     direction: '',
456     toolbar: false,
457     btn: true,
458
459     getAutoCreate : function(){
460         var cfg = {
461             cls: 'btn-group',
462             html : null
463         };
464         
465         cfg.html = this.html || cfg.html;
466         
467         if (this.toolbar) {
468             cfg = {
469                 cls: 'btn-toolbar',
470                 html: null
471             };
472             
473             return cfg;
474         }
475         
476         if (['vertical','justified'].indexOf(this.align)!==-1) {
477             cfg.cls = 'btn-group-' + this.align;
478             
479             if (this.align == 'justified') {
480                 console.log(this.items);
481             }
482         }
483         
484         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485             cfg.cls += ' btn-group-' + this.size;
486         }
487         
488         if (this.direction == 'up') {
489             cfg.cls += ' dropup' ;
490         }
491         
492         return cfg;
493     }
494    
495 });
496
497  /*
498  * - LGPL
499  *
500  * button
501  * 
502  */
503
504 /**
505  * @class Roo.bootstrap.Button
506  * @extends Roo.bootstrap.Component
507  * Bootstrap Button class
508  * @cfg {String} html The button content
509  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
510  * @cfg {String} size ( lg | sm | xs)
511  * @cfg {String} tag ( a | input | submit)
512  * @cfg {String} href empty or href
513  * @cfg {Boolean} disabled default false;
514  * @cfg {Boolean} isClose default false;
515  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
516  * @cfg {String} badge text for badge
517  * @cfg {String} theme default 
518  * @cfg {Boolean} inverse 
519  * @cfg {Boolean} toggle 
520  * @cfg {String} ontext text for on toggle state
521  * @cfg {String} offtext text for off toggle state
522  * @cfg {Boolean} defaulton 
523  * @cfg {Boolean} preventDefault  default true
524  * @cfg {Boolean} removeClass remove the standard class..
525  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
526  * 
527  * @constructor
528  * Create a new button
529  * @param {Object} config The config object
530  */
531
532
533 Roo.bootstrap.Button = function(config){
534     Roo.bootstrap.Button.superclass.constructor.call(this, config);
535     this.addEvents({
536         // raw events
537         /**
538          * @event click
539          * When a butotn is pressed
540          * @param {Roo.bootstrap.Button} this
541          * @param {Roo.EventObject} e
542          */
543         "click" : true,
544          /**
545          * @event toggle
546          * After the button has been toggles
547          * @param {Roo.EventObject} e
548          * @param {boolean} pressed (also available as button.pressed)
549          */
550         "toggle" : true
551     });
552 };
553
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
555     html: false,
556     active: false,
557     weight: '',
558     size: '',
559     tag: 'button',
560     href: '',
561     disabled: false,
562     isClose: false,
563     glyphicon: '',
564     badge: '',
565     theme: 'default',
566     inverse: false,
567     
568     toggle: false,
569     ontext: 'ON',
570     offtext: 'OFF',
571     defaulton: true,
572     preventDefault: true,
573     removeClass: false,
574     name: false,
575     target: false,
576     
577     
578     pressed : null,
579      
580     
581     getAutoCreate : function(){
582         
583         var cfg = {
584             tag : 'button',
585             cls : 'roo-button',
586             html: ''
587         };
588         
589         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
591             this.tag = 'button';
592         } else {
593             cfg.tag = this.tag;
594         }
595         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
596         
597         if (this.toggle == true) {
598             cfg={
599                 tag: 'div',
600                 cls: 'slider-frame roo-button',
601                 cn: [
602                     {
603                         tag: 'span',
604                         'data-on-text':'ON',
605                         'data-off-text':'OFF',
606                         cls: 'slider-button',
607                         html: this.offtext
608                     }
609                 ]
610             };
611             
612             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613                 cfg.cls += ' '+this.weight;
614             }
615             
616             return cfg;
617         }
618         
619         if (this.isClose) {
620             cfg.cls += ' close';
621             
622             cfg["aria-hidden"] = true;
623             
624             cfg.html = "&times;";
625             
626             return cfg;
627         }
628         
629          
630         if (this.theme==='default') {
631             cfg.cls = 'btn roo-button';
632             
633             //if (this.parentType != 'Navbar') {
634             this.weight = this.weight.length ?  this.weight : 'default';
635             //}
636             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
637                 
638                 cfg.cls += ' btn-' + this.weight;
639             }
640         } else if (this.theme==='glow') {
641             
642             cfg.tag = 'a';
643             cfg.cls = 'btn-glow roo-button';
644             
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' ' + this.weight;
648             }
649         }
650    
651         
652         if (this.inverse) {
653             this.cls += ' inverse';
654         }
655         
656         
657         if (this.active) {
658             cfg.cls += ' active';
659         }
660         
661         if (this.disabled) {
662             cfg.disabled = 'disabled';
663         }
664         
665         if (this.items) {
666             Roo.log('changing to ul' );
667             cfg.tag = 'ul';
668             this.glyphicon = 'caret';
669         }
670         
671         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
672          
673         //gsRoo.log(this.parentType);
674         if (this.parentType === 'Navbar' && !this.parent().bar) {
675             Roo.log('changing to li?');
676             
677             cfg.tag = 'li';
678             
679             cfg.cls = '';
680             cfg.cn =  [{
681                 tag : 'a',
682                 cls : 'roo-button',
683                 html : this.html,
684                 href : this.href || '#'
685             }];
686             if (this.menu) {
687                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
688                 cfg.cls += ' dropdown';
689             }   
690             
691             delete cfg.html;
692             
693         }
694         
695        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
696         
697         if (this.glyphicon) {
698             cfg.html = ' ' + cfg.html;
699             
700             cfg.cn = [
701                 {
702                     tag: 'span',
703                     cls: 'glyphicon glyphicon-' + this.glyphicon
704                 }
705             ];
706         }
707         
708         if (this.badge) {
709             cfg.html += ' ';
710             
711             cfg.tag = 'a';
712             
713 //            cfg.cls='btn roo-button';
714             
715             cfg.href=this.href;
716             
717             var value = cfg.html;
718             
719             if(this.glyphicon){
720                 value = {
721                             tag: 'span',
722                             cls: 'glyphicon glyphicon-' + this.glyphicon,
723                             html: this.html
724                         };
725                 
726             }
727             
728             cfg.cn = [
729                 value,
730                 {
731                     tag: 'span',
732                     cls: 'badge',
733                     html: this.badge
734                 }
735             ];
736             
737             cfg.html='';
738         }
739         
740         if (this.menu) {
741             cfg.cls += ' dropdown';
742             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
743         }
744         
745         if (cfg.tag !== 'a' && this.href !== '') {
746             throw "Tag must be a to set href.";
747         } else if (this.href.length > 0) {
748             cfg.href = this.href;
749         }
750         
751         if(this.removeClass){
752             cfg.cls = '';
753         }
754         
755         if(this.target){
756             cfg.target = this.target;
757         }
758         
759         return cfg;
760     },
761     initEvents: function() {
762        // Roo.log('init events?');
763 //        Roo.log(this.el.dom);
764         // add the menu...
765         
766         if (typeof (this.menu) != 'undefined') {
767             this.menu.parentType = this.xtype;
768             this.menu.triggerEl = this.el;
769             this.addxtype(Roo.apply({}, this.menu));
770         }
771
772
773        if (this.el.hasClass('roo-button')) {
774             this.el.on('click', this.onClick, this);
775        } else {
776             this.el.select('.roo-button').on('click', this.onClick, this);
777        }
778        
779        if(this.removeClass){
780            this.el.on('click', this.onClick, this);
781        }
782        
783        this.el.enableDisplayMode();
784         
785     },
786     onClick : function(e)
787     {
788         if (this.disabled) {
789             return;
790         }
791         
792         
793         Roo.log('button on click ');
794         if(this.preventDefault){
795             e.preventDefault();
796         }
797         if (this.pressed === true || this.pressed === false) {
798             this.pressed = !this.pressed;
799             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800             this.fireEvent('toggle', this, e, this.pressed);
801         }
802         
803         
804         this.fireEvent('click', this, e);
805     },
806     
807     /**
808      * Enables this button
809      */
810     enable : function()
811     {
812         this.disabled = false;
813         this.el.removeClass('disabled');
814     },
815     
816     /**
817      * Disable this button
818      */
819     disable : function()
820     {
821         this.disabled = true;
822         this.el.addClass('disabled');
823     },
824      /**
825      * sets the active state on/off, 
826      * @param {Boolean} state (optional) Force a particular state
827      */
828     setActive : function(v) {
829         
830         this.el[v ? 'addClass' : 'removeClass']('active');
831     },
832      /**
833      * toggles the current active state 
834      */
835     toggleActive : function()
836     {
837        var active = this.el.hasClass('active');
838        this.setActive(!active);
839        
840         
841     },
842     setText : function(str)
843     {
844         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
845     },
846     getText : function()
847     {
848         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
849     },
850     hide: function() {
851        
852      
853         this.el.hide();   
854     },
855     show: function() {
856        
857         this.el.show();   
858     }
859     
860     
861 });
862
863  /*
864  * - LGPL
865  *
866  * column
867  * 
868  */
869
870 /**
871  * @class Roo.bootstrap.Column
872  * @extends Roo.bootstrap.Component
873  * Bootstrap Column class
874  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
882  *
883  * 
884  * @cfg {Boolean} hidden (true|false) hide the element
885  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886  * @cfg {String} fa (ban|check|...) font awesome icon
887  * @cfg {Number} fasize (1|2|....) font awsome size
888
889  * @cfg {String} icon (info-sign|check|...) glyphicon name
890
891  * @cfg {String} html content of column.
892  * 
893  * @constructor
894  * Create a new Column
895  * @param {Object} config The config object
896  */
897
898 Roo.bootstrap.Column = function(config){
899     Roo.bootstrap.Column.superclass.constructor.call(this, config);
900 };
901
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
903     
904     xs: false,
905     sm: false,
906     md: false,
907     lg: false,
908     xsoff: false,
909     smoff: false,
910     mdoff: false,
911     lgoff: false,
912     html: '',
913     offset: 0,
914     alert: false,
915     fa: false,
916     icon : false,
917     hidden : false,
918     fasize : 1,
919     
920     getAutoCreate : function(){
921         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
922         
923         cfg = {
924             tag: 'div',
925             cls: 'column'
926         };
927         
928         var settings=this;
929         ['xs','sm','md','lg'].map(function(size){
930             //Roo.log( size + ':' + settings[size]);
931             
932             if (settings[size+'off'] !== false) {
933                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
934             }
935             
936             if (settings[size] === false) {
937                 return;
938             }
939             
940             if (!settings[size]) { // 0 = hidden
941                 cfg.cls += ' hidden-' + size;
942                 return;
943             }
944             cfg.cls += ' col-' + size + '-' + settings[size];
945             
946         });
947         
948         if (this.hidden) {
949             cfg.cls += ' hidden';
950         }
951         
952         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953             cfg.cls +=' alert alert-' + this.alert;
954         }
955         
956         
957         if (this.html.length) {
958             cfg.html = this.html;
959         }
960         if (this.fa) {
961             var fasize = '';
962             if (this.fasize > 1) {
963                 fasize = ' fa-' + this.fasize + 'x';
964             }
965             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
966             
967             
968         }
969         if (this.icon) {
970             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
971         }
972         
973         return cfg;
974     }
975    
976 });
977
978  
979
980  /*
981  * - LGPL
982  *
983  * page container.
984  * 
985  */
986
987
988 /**
989  * @class Roo.bootstrap.Container
990  * @extends Roo.bootstrap.Component
991  * Bootstrap Container class
992  * @cfg {Boolean} jumbotron is it a jumbotron element
993  * @cfg {String} html content of element
994  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
996  * @cfg {String} header content of header (for panel)
997  * @cfg {String} footer content of footer (for panel)
998  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999  * @cfg {String} tag (header|aside|section) type of HTML tag.
1000  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001  * @cfg {String} fa font awesome icon
1002  * @cfg {String} icon (info-sign|check|...) glyphicon name
1003  * @cfg {Boolean} hidden (true|false) hide the element
1004  * @cfg {Boolean} expandable (true|false) default false
1005  * @cfg {Boolean} expanded (true|false) default true
1006  * @cfg {String} rheader contet on the right of header
1007  * @cfg {Boolean} clickable (true|false) default false
1008
1009  *     
1010  * @constructor
1011  * Create a new Container
1012  * @param {Object} config The config object
1013  */
1014
1015 Roo.bootstrap.Container = function(config){
1016     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020          /**
1021          * @event expand
1022          * After the panel has been expand
1023          * 
1024          * @param {Roo.bootstrap.Container} this
1025          */
1026         "expand" : true,
1027         /**
1028          * @event collapse
1029          * After the panel has been collapsed
1030          * 
1031          * @param {Roo.bootstrap.Container} this
1032          */
1033         "collapse" : true,
1034         /**
1035          * @event click
1036          * When a element is chick
1037          * @param {Roo.bootstrap.Container} this
1038          * @param {Roo.EventObject} e
1039          */
1040         "click" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1045     
1046     jumbotron : false,
1047     well: '',
1048     panel : '',
1049     header: '',
1050     footer : '',
1051     sticky: '',
1052     tag : false,
1053     alert : false,
1054     fa: false,
1055     icon : false,
1056     expandable : false,
1057     rheader : '',
1058     expanded : true,
1059     clickable: false,
1060   
1061      
1062     getChildContainer : function() {
1063         
1064         if(!this.el){
1065             return false;
1066         }
1067         
1068         if (this.panel.length) {
1069             return this.el.select('.panel-body',true).first();
1070         }
1071         
1072         return this.el;
1073     },
1074     
1075     
1076     getAutoCreate : function(){
1077         
1078         var cfg = {
1079             tag : this.tag || 'div',
1080             html : '',
1081             cls : ''
1082         };
1083         if (this.jumbotron) {
1084             cfg.cls = 'jumbotron';
1085         }
1086         
1087         
1088         
1089         // - this is applied by the parent..
1090         //if (this.cls) {
1091         //    cfg.cls = this.cls + '';
1092         //}
1093         
1094         if (this.sticky.length) {
1095             
1096             var bd = Roo.get(document.body);
1097             if (!bd.hasClass('bootstrap-sticky')) {
1098                 bd.addClass('bootstrap-sticky');
1099                 Roo.select('html',true).setStyle('height', '100%');
1100             }
1101              
1102             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1103         }
1104         
1105         
1106         if (this.well.length) {
1107             switch (this.well) {
1108                 case 'lg':
1109                 case 'sm':
1110                     cfg.cls +=' well well-' +this.well;
1111                     break;
1112                 default:
1113                     cfg.cls +=' well';
1114                     break;
1115             }
1116         }
1117         
1118         if (this.hidden) {
1119             cfg.cls += ' hidden';
1120         }
1121         
1122         
1123         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124             cfg.cls +=' alert alert-' + this.alert;
1125         }
1126         
1127         var body = cfg;
1128         
1129         if (this.panel.length) {
1130             cfg.cls += ' panel panel-' + this.panel;
1131             cfg.cn = [];
1132             if (this.header.length) {
1133                 
1134                 var h = [];
1135                 
1136                 if(this.expandable){
1137                     
1138                     cfg.cls = cfg.cls + ' expandable';
1139                     
1140                     h.push({
1141                         tag: 'i',
1142                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1143                     });
1144                     
1145                 }
1146                 
1147                 h.push(
1148                     {
1149                         tag: 'span',
1150                         cls : 'panel-title',
1151                         html : (this.expandable ? '&nbsp;' : '') + this.header
1152                     },
1153                     {
1154                         tag: 'span',
1155                         cls: 'panel-header-right',
1156                         html: this.rheader
1157                     }
1158                 );
1159                 
1160                 cfg.cn.push({
1161                     cls : 'panel-heading',
1162                     style : this.expandable ? 'cursor: pointer' : '',
1163                     cn : h
1164                 });
1165                 
1166             }
1167             
1168             body = false;
1169             cfg.cn.push({
1170                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1171                 html : this.html
1172             });
1173             
1174             
1175             if (this.footer.length) {
1176                 cfg.cn.push({
1177                     cls : 'panel-footer',
1178                     html : this.footer
1179                     
1180                 });
1181             }
1182             
1183         }
1184         
1185         if (body) {
1186             body.html = this.html || cfg.html;
1187             // prefix with the icons..
1188             if (this.fa) {
1189                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1190             }
1191             if (this.icon) {
1192                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1193             }
1194             
1195             
1196         }
1197         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198             cfg.cls =  'container';
1199         }
1200         
1201         return cfg;
1202     },
1203     
1204     initEvents: function() 
1205     {
1206         if(this.expandable){
1207             var headerEl = this.headerEl();
1208         
1209             if(headerEl){
1210                 headerEl.on('click', this.onToggleClick, this);
1211             }
1212         }
1213         
1214         if(this.clickable){
1215             this.el.on('click', this.onClick, this);
1216         }
1217         
1218     },
1219     
1220     onToggleClick : function()
1221     {
1222         var headerEl = this.headerEl();
1223         
1224         if(!headerEl){
1225             return;
1226         }
1227         
1228         if(this.expanded){
1229             this.collapse();
1230             return;
1231         }
1232         
1233         this.expand();
1234     },
1235     
1236     expand : function()
1237     {
1238         if(this.fireEvent('expand', this)) {
1239             
1240             this.expanded = true;
1241             
1242             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1243             
1244             this.el.select('.panel-body',true).first().removeClass('hide');
1245             
1246             var toggleEl = this.toggleEl();
1247
1248             if(!toggleEl){
1249                 return;
1250             }
1251
1252             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1253         }
1254         
1255     },
1256     
1257     collapse : function()
1258     {
1259         if(this.fireEvent('collapse', this)) {
1260             
1261             this.expanded = false;
1262             
1263             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264             this.el.select('.panel-body',true).first().addClass('hide');
1265         
1266             var toggleEl = this.toggleEl();
1267
1268             if(!toggleEl){
1269                 return;
1270             }
1271
1272             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1273         }
1274     },
1275     
1276     toggleEl : function()
1277     {
1278         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1279             return;
1280         }
1281         
1282         return this.el.select('.panel-heading .fa',true).first();
1283     },
1284     
1285     headerEl : function()
1286     {
1287         if(!this.el || !this.panel.length || !this.header.length){
1288             return;
1289         }
1290         
1291         return this.el.select('.panel-heading',true).first()
1292     },
1293     
1294     titleEl : function()
1295     {
1296         if(!this.el || !this.panel.length || !this.header.length){
1297             return;
1298         }
1299         
1300         return this.el.select('.panel-title',true).first();
1301     },
1302     
1303     setTitle : function(v)
1304     {
1305         var titleEl = this.titleEl();
1306         
1307         if(!titleEl){
1308             return;
1309         }
1310         
1311         titleEl.dom.innerHTML = v;
1312     },
1313     
1314     getTitle : function()
1315     {
1316         
1317         var titleEl = this.titleEl();
1318         
1319         if(!titleEl){
1320             return '';
1321         }
1322         
1323         return titleEl.dom.innerHTML;
1324     },
1325     
1326     setRightTitle : function(v)
1327     {
1328         var t = this.el.select('.panel-header-right',true).first();
1329         
1330         if(!t){
1331             return;
1332         }
1333         
1334         t.dom.innerHTML = v;
1335     },
1336     
1337     onClick : function(e)
1338     {
1339         e.preventDefault();
1340         
1341         this.fireEvent('click', this, e);
1342     }
1343    
1344 });
1345
1346  /*
1347  * - LGPL
1348  *
1349  * image
1350  * 
1351  */
1352
1353
1354 /**
1355  * @class Roo.bootstrap.Img
1356  * @extends Roo.bootstrap.Component
1357  * Bootstrap Img class
1358  * @cfg {Boolean} imgResponsive false | true
1359  * @cfg {String} border rounded | circle | thumbnail
1360  * @cfg {String} src image source
1361  * @cfg {String} alt image alternative text
1362  * @cfg {String} href a tag href
1363  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364  * @cfg {String} xsUrl xs image source
1365  * @cfg {String} smUrl sm image source
1366  * @cfg {String} mdUrl md image source
1367  * @cfg {String} lgUrl lg image source
1368  * 
1369  * @constructor
1370  * Create a new Input
1371  * @param {Object} config The config object
1372  */
1373
1374 Roo.bootstrap.Img = function(config){
1375     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1376     
1377     this.addEvents({
1378         // img events
1379         /**
1380          * @event click
1381          * The img click event for the img.
1382          * @param {Roo.EventObject} e
1383          */
1384         "click" : true
1385     });
1386 };
1387
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1389     
1390     imgResponsive: true,
1391     border: '',
1392     src: 'about:blank',
1393     href: false,
1394     target: false,
1395     xsUrl: '',
1396     smUrl: '',
1397     mdUrl: '',
1398     lgUrl: '',
1399
1400     getAutoCreate : function()
1401     {   
1402         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403             return this.createSingleImg();
1404         }
1405         
1406         var cfg = {
1407             tag: 'div',
1408             cls: 'roo-image-responsive-group',
1409             cn: []
1410         };
1411         var _this = this;
1412         
1413         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1414             
1415             if(!_this[size + 'Url']){
1416                 return;
1417             }
1418             
1419             var img = {
1420                 tag: 'img',
1421                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422                 html: _this.html || cfg.html,
1423                 src: _this[size + 'Url']
1424             };
1425             
1426             img.cls += ' roo-image-responsive-' + size;
1427             
1428             var s = ['xs', 'sm', 'md', 'lg'];
1429             
1430             s.splice(s.indexOf(size), 1);
1431             
1432             Roo.each(s, function(ss){
1433                 img.cls += ' hidden-' + ss;
1434             });
1435             
1436             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437                 cfg.cls += ' img-' + _this.border;
1438             }
1439             
1440             if(_this.alt){
1441                 cfg.alt = _this.alt;
1442             }
1443             
1444             if(_this.href){
1445                 var a = {
1446                     tag: 'a',
1447                     href: _this.href,
1448                     cn: [
1449                         img
1450                     ]
1451                 };
1452
1453                 if(this.target){
1454                     a.target = _this.target;
1455                 }
1456             }
1457             
1458             cfg.cn.push((_this.href) ? a : img);
1459             
1460         });
1461         
1462         return cfg;
1463     },
1464     
1465     createSingleImg : function()
1466     {
1467         var cfg = {
1468             tag: 'img',
1469             cls: (this.imgResponsive) ? 'img-responsive' : '',
1470             html : null,
1471             src : 'about:blank'  // just incase src get's set to undefined?!?
1472         };
1473         
1474         cfg.html = this.html || cfg.html;
1475         
1476         cfg.src = this.src || cfg.src;
1477         
1478         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479             cfg.cls += ' img-' + this.border;
1480         }
1481         
1482         if(this.alt){
1483             cfg.alt = this.alt;
1484         }
1485         
1486         if(this.href){
1487             var a = {
1488                 tag: 'a',
1489                 href: this.href,
1490                 cn: [
1491                     cfg
1492                 ]
1493             };
1494             
1495             if(this.target){
1496                 a.target = this.target;
1497             }
1498             
1499         }
1500         
1501         return (this.href) ? a : cfg;
1502     },
1503     
1504     initEvents: function() 
1505     {
1506         if(!this.href){
1507             this.el.on('click', this.onClick, this);
1508         }
1509         
1510     },
1511     
1512     onClick : function(e)
1513     {
1514         Roo.log('img onclick');
1515         this.fireEvent('click', this, e);
1516     }
1517    
1518 });
1519
1520  /*
1521  * - LGPL
1522  *
1523  * image
1524  * 
1525  */
1526
1527
1528 /**
1529  * @class Roo.bootstrap.Link
1530  * @extends Roo.bootstrap.Component
1531  * Bootstrap Link Class
1532  * @cfg {String} alt image alternative text
1533  * @cfg {String} href a tag href
1534  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1535  * @cfg {String} html the content of the link.
1536  * @cfg {String} anchor name for the anchor link
1537  * @cfg {String} fa - favicon
1538
1539  * @cfg {Boolean} preventDefault (true | false) default false
1540
1541  * 
1542  * @constructor
1543  * Create a new Input
1544  * @param {Object} config The config object
1545  */
1546
1547 Roo.bootstrap.Link = function(config){
1548     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1549     
1550     this.addEvents({
1551         // img events
1552         /**
1553          * @event click
1554          * The img click event for the img.
1555          * @param {Roo.EventObject} e
1556          */
1557         "click" : true
1558     });
1559 };
1560
1561 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1562     
1563     href: false,
1564     target: false,
1565     preventDefault: false,
1566     anchor : false,
1567     alt : false,
1568     fa: false,
1569
1570
1571     getAutoCreate : function()
1572     {
1573         var html = this.html || '';
1574         
1575         if (this.fa !== false) {
1576             html = '<i class="fa fa-' + this.fa + '"></i>';
1577         }
1578         var cfg = {
1579             tag: 'a'
1580         };
1581         // anchor's do not require html/href...
1582         if (this.anchor === false) {
1583             cfg.html = html;
1584             cfg.href = this.href || '#';
1585         } else {
1586             cfg.name = this.anchor;
1587             if (this.html !== false || this.fa !== false) {
1588                 cfg.html = html;
1589             }
1590             if (this.href !== false) {
1591                 cfg.href = this.href;
1592             }
1593         }
1594         
1595         if(this.alt !== false){
1596             cfg.alt = this.alt;
1597         }
1598         
1599         
1600         if(this.target !== false) {
1601             cfg.target = this.target;
1602         }
1603         
1604         return cfg;
1605     },
1606     
1607     initEvents: function() {
1608         
1609         if(!this.href || this.preventDefault){
1610             this.el.on('click', this.onClick, this);
1611         }
1612     },
1613     
1614     onClick : function(e)
1615     {
1616         if(this.preventDefault){
1617             e.preventDefault();
1618         }
1619         //Roo.log('img onclick');
1620         this.fireEvent('click', this, e);
1621     }
1622    
1623 });
1624
1625  /*
1626  * - LGPL
1627  *
1628  * header
1629  * 
1630  */
1631
1632 /**
1633  * @class Roo.bootstrap.Header
1634  * @extends Roo.bootstrap.Component
1635  * Bootstrap Header class
1636  * @cfg {String} html content of header
1637  * @cfg {Number} level (1|2|3|4|5|6) default 1
1638  * 
1639  * @constructor
1640  * Create a new Header
1641  * @param {Object} config The config object
1642  */
1643
1644
1645 Roo.bootstrap.Header  = function(config){
1646     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1650     
1651     //href : false,
1652     html : false,
1653     level : 1,
1654     
1655     
1656     
1657     getAutoCreate : function(){
1658         
1659         
1660         
1661         var cfg = {
1662             tag: 'h' + (1 *this.level),
1663             html: this.html || ''
1664         } ;
1665         
1666         return cfg;
1667     }
1668    
1669 });
1670
1671  
1672
1673  /*
1674  * Based on:
1675  * Ext JS Library 1.1.1
1676  * Copyright(c) 2006-2007, Ext JS, LLC.
1677  *
1678  * Originally Released Under LGPL - original licence link has changed is not relivant.
1679  *
1680  * Fork - LGPL
1681  * <script type="text/javascript">
1682  */
1683  
1684 /**
1685  * @class Roo.bootstrap.MenuMgr
1686  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1687  * @singleton
1688  */
1689 Roo.bootstrap.MenuMgr = function(){
1690    var menus, active, groups = {}, attached = false, lastShow = new Date();
1691
1692    // private - called when first menu is created
1693    function init(){
1694        menus = {};
1695        active = new Roo.util.MixedCollection();
1696        Roo.get(document).addKeyListener(27, function(){
1697            if(active.length > 0){
1698                hideAll();
1699            }
1700        });
1701    }
1702
1703    // private
1704    function hideAll(){
1705        if(active && active.length > 0){
1706            var c = active.clone();
1707            c.each(function(m){
1708                m.hide();
1709            });
1710        }
1711    }
1712
1713    // private
1714    function onHide(m){
1715        active.remove(m);
1716        if(active.length < 1){
1717            Roo.get(document).un("mouseup", onMouseDown);
1718             
1719            attached = false;
1720        }
1721    }
1722
1723    // private
1724    function onShow(m){
1725        var last = active.last();
1726        lastShow = new Date();
1727        active.add(m);
1728        if(!attached){
1729           Roo.get(document).on("mouseup", onMouseDown);
1730            
1731            attached = true;
1732        }
1733        if(m.parentMenu){
1734           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1735           m.parentMenu.activeChild = m;
1736        }else if(last && last.isVisible()){
1737           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1738        }
1739    }
1740
1741    // private
1742    function onBeforeHide(m){
1743        if(m.activeChild){
1744            m.activeChild.hide();
1745        }
1746        if(m.autoHideTimer){
1747            clearTimeout(m.autoHideTimer);
1748            delete m.autoHideTimer;
1749        }
1750    }
1751
1752    // private
1753    function onBeforeShow(m){
1754        var pm = m.parentMenu;
1755        if(!pm && !m.allowOtherMenus){
1756            hideAll();
1757        }else if(pm && pm.activeChild && active != m){
1758            pm.activeChild.hide();
1759        }
1760    }
1761
1762    // private this should really trigger on mouseup..
1763    function onMouseDown(e){
1764         Roo.log("on Mouse Up");
1765         
1766         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1767             Roo.log("MenuManager hideAll");
1768             hideAll();
1769             e.stopEvent();
1770         }
1771         
1772         
1773    }
1774
1775    // private
1776    function onBeforeCheck(mi, state){
1777        if(state){
1778            var g = groups[mi.group];
1779            for(var i = 0, l = g.length; i < l; i++){
1780                if(g[i] != mi){
1781                    g[i].setChecked(false);
1782                }
1783            }
1784        }
1785    }
1786
1787    return {
1788
1789        /**
1790         * Hides all menus that are currently visible
1791         */
1792        hideAll : function(){
1793             hideAll();  
1794        },
1795
1796        // private
1797        register : function(menu){
1798            if(!menus){
1799                init();
1800            }
1801            menus[menu.id] = menu;
1802            menu.on("beforehide", onBeforeHide);
1803            menu.on("hide", onHide);
1804            menu.on("beforeshow", onBeforeShow);
1805            menu.on("show", onShow);
1806            var g = menu.group;
1807            if(g && menu.events["checkchange"]){
1808                if(!groups[g]){
1809                    groups[g] = [];
1810                }
1811                groups[g].push(menu);
1812                menu.on("checkchange", onCheck);
1813            }
1814        },
1815
1816         /**
1817          * Returns a {@link Roo.menu.Menu} object
1818          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1819          * be used to generate and return a new Menu instance.
1820          */
1821        get : function(menu){
1822            if(typeof menu == "string"){ // menu id
1823                return menus[menu];
1824            }else if(menu.events){  // menu instance
1825                return menu;
1826            }
1827            /*else if(typeof menu.length == 'number'){ // array of menu items?
1828                return new Roo.bootstrap.Menu({items:menu});
1829            }else{ // otherwise, must be a config
1830                return new Roo.bootstrap.Menu(menu);
1831            }
1832            */
1833            return false;
1834        },
1835
1836        // private
1837        unregister : function(menu){
1838            delete menus[menu.id];
1839            menu.un("beforehide", onBeforeHide);
1840            menu.un("hide", onHide);
1841            menu.un("beforeshow", onBeforeShow);
1842            menu.un("show", onShow);
1843            var g = menu.group;
1844            if(g && menu.events["checkchange"]){
1845                groups[g].remove(menu);
1846                menu.un("checkchange", onCheck);
1847            }
1848        },
1849
1850        // private
1851        registerCheckable : function(menuItem){
1852            var g = menuItem.group;
1853            if(g){
1854                if(!groups[g]){
1855                    groups[g] = [];
1856                }
1857                groups[g].push(menuItem);
1858                menuItem.on("beforecheckchange", onBeforeCheck);
1859            }
1860        },
1861
1862        // private
1863        unregisterCheckable : function(menuItem){
1864            var g = menuItem.group;
1865            if(g){
1866                groups[g].remove(menuItem);
1867                menuItem.un("beforecheckchange", onBeforeCheck);
1868            }
1869        }
1870    };
1871 }();/*
1872  * - LGPL
1873  *
1874  * menu
1875  * 
1876  */
1877
1878 /**
1879  * @class Roo.bootstrap.Menu
1880  * @extends Roo.bootstrap.Component
1881  * Bootstrap Menu class - container for MenuItems
1882  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1883  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1884  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1885  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1886  * 
1887  * @constructor
1888  * Create a new Menu
1889  * @param {Object} config The config object
1890  */
1891
1892
1893 Roo.bootstrap.Menu = function(config){
1894     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1895     if (this.registerMenu && this.type != 'treeview')  {
1896         Roo.bootstrap.MenuMgr.register(this);
1897     }
1898     this.addEvents({
1899         /**
1900          * @event beforeshow
1901          * Fires before this menu is displayed
1902          * @param {Roo.menu.Menu} this
1903          */
1904         beforeshow : true,
1905         /**
1906          * @event beforehide
1907          * Fires before this menu is hidden
1908          * @param {Roo.menu.Menu} this
1909          */
1910         beforehide : true,
1911         /**
1912          * @event show
1913          * Fires after this menu is displayed
1914          * @param {Roo.menu.Menu} this
1915          */
1916         show : true,
1917         /**
1918          * @event hide
1919          * Fires after this menu is hidden
1920          * @param {Roo.menu.Menu} this
1921          */
1922         hide : true,
1923         /**
1924          * @event click
1925          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1926          * @param {Roo.menu.Menu} this
1927          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928          * @param {Roo.EventObject} e
1929          */
1930         click : true,
1931         /**
1932          * @event mouseover
1933          * Fires when the mouse is hovering over this menu
1934          * @param {Roo.menu.Menu} this
1935          * @param {Roo.EventObject} e
1936          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1937          */
1938         mouseover : true,
1939         /**
1940          * @event mouseout
1941          * Fires when the mouse exits this menu
1942          * @param {Roo.menu.Menu} this
1943          * @param {Roo.EventObject} e
1944          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1945          */
1946         mouseout : true,
1947         /**
1948          * @event itemclick
1949          * Fires when a menu item contained in this menu is clicked
1950          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1951          * @param {Roo.EventObject} e
1952          */
1953         itemclick: true
1954     });
1955     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1956 };
1957
1958 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1959     
1960    /// html : false,
1961     //align : '',
1962     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1963     type: false,
1964     /**
1965      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1966      */
1967     registerMenu : true,
1968     
1969     menuItems :false, // stores the menu items..
1970     
1971     hidden:true,
1972         
1973     parentMenu : false,
1974     
1975     stopEvent : true,
1976     
1977     isLink : false,
1978     
1979     getChildContainer : function() {
1980         return this.el;  
1981     },
1982     
1983     getAutoCreate : function(){
1984          
1985         //if (['right'].indexOf(this.align)!==-1) {
1986         //    cfg.cn[1].cls += ' pull-right'
1987         //}
1988         
1989         
1990         var cfg = {
1991             tag : 'ul',
1992             cls : 'dropdown-menu' ,
1993             style : 'z-index:1000'
1994             
1995         };
1996         
1997         if (this.type === 'submenu') {
1998             cfg.cls = 'submenu active';
1999         }
2000         if (this.type === 'treeview') {
2001             cfg.cls = 'treeview-menu';
2002         }
2003         
2004         return cfg;
2005     },
2006     initEvents : function() {
2007         
2008        // Roo.log("ADD event");
2009        // Roo.log(this.triggerEl.dom);
2010         
2011         this.triggerEl.on('click', this.onTriggerClick, this);
2012         
2013         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2014         
2015         this.triggerEl.addClass('dropdown-toggle');
2016         
2017         if (Roo.isTouch) {
2018             this.el.on('touchstart'  , this.onTouch, this);
2019         }
2020         this.el.on('click' , this.onClick, this);
2021
2022         this.el.on("mouseover", this.onMouseOver, this);
2023         this.el.on("mouseout", this.onMouseOut, this);
2024         
2025     },
2026     
2027     findTargetItem : function(e)
2028     {
2029         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2030         if(!t){
2031             return false;
2032         }
2033         //Roo.log(t);         Roo.log(t.id);
2034         if(t && t.id){
2035             //Roo.log(this.menuitems);
2036             return this.menuitems.get(t.id);
2037             
2038             //return this.items.get(t.menuItemId);
2039         }
2040         
2041         return false;
2042     },
2043     
2044     onTouch : function(e) 
2045     {
2046         Roo.log("menu.onTouch");
2047         //e.stopEvent(); this make the user popdown broken
2048         this.onClick(e);
2049     },
2050     
2051     onClick : function(e)
2052     {
2053         Roo.log("menu.onClick");
2054         
2055         var t = this.findTargetItem(e);
2056         if(!t || t.isContainer){
2057             return;
2058         }
2059         Roo.log(e);
2060         /*
2061         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2062             if(t == this.activeItem && t.shouldDeactivate(e)){
2063                 this.activeItem.deactivate();
2064                 delete this.activeItem;
2065                 return;
2066             }
2067             if(t.canActivate){
2068                 this.setActiveItem(t, true);
2069             }
2070             return;
2071             
2072             
2073         }
2074         */
2075        
2076         Roo.log('pass click event');
2077         
2078         t.onClick(e);
2079         
2080         this.fireEvent("click", this, t, e);
2081         
2082         var _this = this;
2083         
2084         (function() { _this.hide(); }).defer(500);
2085     },
2086     
2087     onMouseOver : function(e){
2088         var t  = this.findTargetItem(e);
2089         //Roo.log(t);
2090         //if(t){
2091         //    if(t.canActivate && !t.disabled){
2092         //        this.setActiveItem(t, true);
2093         //    }
2094         //}
2095         
2096         this.fireEvent("mouseover", this, e, t);
2097     },
2098     isVisible : function(){
2099         return !this.hidden;
2100     },
2101      onMouseOut : function(e){
2102         var t  = this.findTargetItem(e);
2103         
2104         //if(t ){
2105         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2106         //        this.activeItem.deactivate();
2107         //        delete this.activeItem;
2108         //    }
2109         //}
2110         this.fireEvent("mouseout", this, e, t);
2111     },
2112     
2113     
2114     /**
2115      * Displays this menu relative to another element
2116      * @param {String/HTMLElement/Roo.Element} element The element to align to
2117      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2118      * the element (defaults to this.defaultAlign)
2119      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2120      */
2121     show : function(el, pos, parentMenu){
2122         this.parentMenu = parentMenu;
2123         if(!this.el){
2124             this.render();
2125         }
2126         this.fireEvent("beforeshow", this);
2127         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2128     },
2129      /**
2130      * Displays this menu at a specific xy position
2131      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2132      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2133      */
2134     showAt : function(xy, parentMenu, /* private: */_e){
2135         this.parentMenu = parentMenu;
2136         if(!this.el){
2137             this.render();
2138         }
2139         if(_e !== false){
2140             this.fireEvent("beforeshow", this);
2141             //xy = this.el.adjustForConstraints(xy);
2142         }
2143         
2144         //this.el.show();
2145         this.hideMenuItems();
2146         this.hidden = false;
2147         this.triggerEl.addClass('open');
2148         
2149         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2150             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2151         }
2152         
2153         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2154             this.el.setXY(xy);
2155         }
2156         
2157         this.focus();
2158         this.fireEvent("show", this);
2159     },
2160     
2161     focus : function(){
2162         return;
2163         if(!this.hidden){
2164             this.doFocus.defer(50, this);
2165         }
2166     },
2167
2168     doFocus : function(){
2169         if(!this.hidden){
2170             this.focusEl.focus();
2171         }
2172     },
2173
2174     /**
2175      * Hides this menu and optionally all parent menus
2176      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2177      */
2178     hide : function(deep)
2179     {
2180         
2181         this.hideMenuItems();
2182         if(this.el && this.isVisible()){
2183             this.fireEvent("beforehide", this);
2184             if(this.activeItem){
2185                 this.activeItem.deactivate();
2186                 this.activeItem = null;
2187             }
2188             this.triggerEl.removeClass('open');;
2189             this.hidden = true;
2190             this.fireEvent("hide", this);
2191         }
2192         if(deep === true && this.parentMenu){
2193             this.parentMenu.hide(true);
2194         }
2195     },
2196     
2197     onTriggerClick : function(e)
2198     {
2199         Roo.log('trigger click');
2200         
2201         var target = e.getTarget();
2202         
2203         Roo.log(target.nodeName.toLowerCase());
2204         
2205         if(target.nodeName.toLowerCase() === 'i'){
2206             e.preventDefault();
2207         }
2208         
2209     },
2210     
2211     onTriggerPress  : function(e)
2212     {
2213         Roo.log('trigger press');
2214         //Roo.log(e.getTarget());
2215        // Roo.log(this.triggerEl.dom);
2216        
2217         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2218         var pel = Roo.get(e.getTarget());
2219         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2220             Roo.log('is treeview or dropdown?');
2221             return;
2222         }
2223         
2224         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2225             return;
2226         }
2227         
2228         if (this.isVisible()) {
2229             Roo.log('hide');
2230             this.hide();
2231         } else {
2232             Roo.log('show');
2233             this.show(this.triggerEl, false, false);
2234         }
2235         
2236         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2237             e.stopEvent();
2238         }
2239         
2240     },
2241        
2242     
2243     hideMenuItems : function()
2244     {
2245         Roo.log("hide Menu Items");
2246         if (!this.el) { 
2247             return;
2248         }
2249         //$(backdrop).remove()
2250         this.el.select('.open',true).each(function(aa) {
2251             
2252             aa.removeClass('open');
2253           //var parent = getParent($(this))
2254           //var relatedTarget = { relatedTarget: this }
2255           
2256            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2257           //if (e.isDefaultPrevented()) return
2258            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2259         });
2260     },
2261     addxtypeChild : function (tree, cntr) {
2262         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2263           
2264         this.menuitems.add(comp);
2265         return comp;
2266
2267     },
2268     getEl : function()
2269     {
2270         Roo.log(this.el);
2271         return this.el;
2272     }
2273 });
2274
2275  
2276  /*
2277  * - LGPL
2278  *
2279  * menu item
2280  * 
2281  */
2282
2283
2284 /**
2285  * @class Roo.bootstrap.MenuItem
2286  * @extends Roo.bootstrap.Component
2287  * Bootstrap MenuItem class
2288  * @cfg {String} html the menu label
2289  * @cfg {String} href the link
2290  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2291  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2292  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2293  * @cfg {String} fa favicon to show on left of menu item.
2294  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2295  * 
2296  * 
2297  * @constructor
2298  * Create a new MenuItem
2299  * @param {Object} config The config object
2300  */
2301
2302
2303 Roo.bootstrap.MenuItem = function(config){
2304     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2305     this.addEvents({
2306         // raw events
2307         /**
2308          * @event click
2309          * The raw click event for the entire grid.
2310          * @param {Roo.bootstrap.MenuItem} this
2311          * @param {Roo.EventObject} e
2312          */
2313         "click" : true
2314     });
2315 };
2316
2317 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2318     
2319     href : false,
2320     html : false,
2321     preventDefault: true,
2322     isContainer : false,
2323     active : false,
2324     fa: false,
2325     
2326     getAutoCreate : function(){
2327         
2328         if(this.isContainer){
2329             return {
2330                 tag: 'li',
2331                 cls: 'dropdown-menu-item'
2332             };
2333         }
2334         var ctag = {
2335             tag: 'span',
2336             html: 'Link'
2337         };
2338         
2339         var anc = {
2340             tag : 'a',
2341             href : '#',
2342             cn : [  ]
2343         };
2344         
2345         if (this.fa !== false) {
2346             anc.cn.push({
2347                 tag : 'i',
2348                 cls : 'fa fa-' + this.fa
2349             });
2350         }
2351         
2352         anc.cn.push(ctag);
2353         
2354         
2355         var cfg= {
2356             tag: 'li',
2357             cls: 'dropdown-menu-item',
2358             cn: [ anc ]
2359         };
2360         if (this.parent().type == 'treeview') {
2361             cfg.cls = 'treeview-menu';
2362         }
2363         if (this.active) {
2364             cfg.cls += ' active';
2365         }
2366         
2367         
2368         
2369         anc.href = this.href || cfg.cn[0].href ;
2370         ctag.html = this.html || cfg.cn[0].html ;
2371         return cfg;
2372     },
2373     
2374     initEvents: function()
2375     {
2376         if (this.parent().type == 'treeview') {
2377             this.el.select('a').on('click', this.onClick, this);
2378         }
2379         if (this.menu) {
2380             this.menu.parentType = this.xtype;
2381             this.menu.triggerEl = this.el;
2382             this.menu = this.addxtype(Roo.apply({}, this.menu));
2383         }
2384         
2385     },
2386     onClick : function(e)
2387     {
2388         Roo.log('item on click ');
2389         //if(this.preventDefault){
2390         //    e.preventDefault();
2391         //}
2392         //this.parent().hideMenuItems();
2393         
2394         this.fireEvent('click', this, e);
2395     },
2396     getEl : function()
2397     {
2398         return this.el;
2399     } 
2400 });
2401
2402  
2403
2404  /*
2405  * - LGPL
2406  *
2407  * menu separator
2408  * 
2409  */
2410
2411
2412 /**
2413  * @class Roo.bootstrap.MenuSeparator
2414  * @extends Roo.bootstrap.Component
2415  * Bootstrap MenuSeparator class
2416  * 
2417  * @constructor
2418  * Create a new MenuItem
2419  * @param {Object} config The config object
2420  */
2421
2422
2423 Roo.bootstrap.MenuSeparator = function(config){
2424     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2428     
2429     getAutoCreate : function(){
2430         var cfg = {
2431             cls: 'divider',
2432             tag : 'li'
2433         };
2434         
2435         return cfg;
2436     }
2437    
2438 });
2439
2440  
2441
2442  
2443 /*
2444 * Licence: LGPL
2445 */
2446
2447 /**
2448  * @class Roo.bootstrap.Modal
2449  * @extends Roo.bootstrap.Component
2450  * Bootstrap Modal class
2451  * @cfg {String} title Title of dialog
2452  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2453  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2454  * @cfg {Boolean} specificTitle default false
2455  * @cfg {Array} buttons Array of buttons or standard button set..
2456  * @cfg {String} buttonPosition (left|right|center) default right
2457  * @cfg {Boolean} animate default true
2458  * @cfg {Boolean} allow_close default true
2459  * @cfg {Boolean} fitwindow default true
2460  * @cfg {String} size (sm|lg) default empty
2461  * 
2462  * 
2463  * @constructor
2464  * Create a new Modal Dialog
2465  * @param {Object} config The config object
2466  */
2467
2468 Roo.bootstrap.Modal = function(config){
2469     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2470     this.addEvents({
2471         // raw events
2472         /**
2473          * @event btnclick
2474          * The raw btnclick event for the button
2475          * @param {Roo.EventObject} e
2476          */
2477         "btnclick" : true
2478     });
2479     this.buttons = this.buttons || [];
2480      
2481     if (this.tmpl) {
2482         this.tmpl = Roo.factory(this.tmpl);
2483     }
2484     
2485 };
2486
2487 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2488     
2489     title : 'test dialog',
2490    
2491     buttons : false,
2492     
2493     // set on load...
2494      
2495     html: false,
2496     
2497     tmp: false,
2498     
2499     specificTitle: false,
2500     
2501     buttonPosition: 'right',
2502     
2503     allow_close : true,
2504     
2505     animate : true,
2506     
2507     fitwindow: false,
2508     
2509     
2510      // private
2511     dialogEl: false,
2512     bodyEl:  false,
2513     footerEl:  false,
2514     titleEl:  false,
2515     closeEl:  false,
2516     
2517     size: '',
2518     
2519     
2520     onRender : function(ct, position)
2521     {
2522         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2523      
2524         if(!this.el){
2525             var cfg = Roo.apply({},  this.getAutoCreate());
2526             cfg.id = Roo.id();
2527             //if(!cfg.name){
2528             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2529             //}
2530             //if (!cfg.name.length) {
2531             //    delete cfg.name;
2532            // }
2533             if (this.cls) {
2534                 cfg.cls += ' ' + this.cls;
2535             }
2536             if (this.style) {
2537                 cfg.style = this.style;
2538             }
2539             this.el = Roo.get(document.body).createChild(cfg, position);
2540         }
2541         //var type = this.el.dom.type;
2542         
2543         
2544         if(this.tabIndex !== undefined){
2545             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2546         }
2547         
2548         this.dialogEl = this.el.select('.modal-dialog',true).first();
2549         this.bodyEl = this.el.select('.modal-body',true).first();
2550         this.closeEl = this.el.select('.modal-header .close', true).first();
2551         this.footerEl = this.el.select('.modal-footer',true).first();
2552         this.titleEl = this.el.select('.modal-title',true).first();
2553         
2554         
2555          
2556         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2557         this.maskEl.enableDisplayMode("block");
2558         this.maskEl.hide();
2559         //this.el.addClass("x-dlg-modal");
2560     
2561         if (this.buttons.length) {
2562             Roo.each(this.buttons, function(bb) {
2563                 var b = Roo.apply({}, bb);
2564                 b.xns = b.xns || Roo.bootstrap;
2565                 b.xtype = b.xtype || 'Button';
2566                 if (typeof(b.listeners) == 'undefined') {
2567                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2568                 }
2569                 
2570                 var btn = Roo.factory(b);
2571                 
2572                 btn.render(this.el.select('.modal-footer div').first());
2573                 
2574             },this);
2575         }
2576         // render the children.
2577         var nitems = [];
2578         
2579         if(typeof(this.items) != 'undefined'){
2580             var items = this.items;
2581             delete this.items;
2582
2583             for(var i =0;i < items.length;i++) {
2584                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2585             }
2586         }
2587         
2588         this.items = nitems;
2589         
2590         // where are these used - they used to be body/close/footer
2591         
2592        
2593         this.initEvents();
2594         //this.el.addClass([this.fieldClass, this.cls]);
2595         
2596     },
2597     
2598     getAutoCreate : function(){
2599         
2600         
2601         var bdy = {
2602                 cls : 'modal-body',
2603                 html : this.html || ''
2604         };
2605         
2606         var title = {
2607             tag: 'h4',
2608             cls : 'modal-title',
2609             html : this.title
2610         };
2611         
2612         if(this.specificTitle){
2613             title = this.title;
2614             
2615         };
2616         
2617         var header = [];
2618         if (this.allow_close) {
2619             header.push({
2620                 tag: 'button',
2621                 cls : 'close',
2622                 html : '&times'
2623             });
2624         }
2625         
2626         header.push(title);
2627         
2628         var size = '';
2629         
2630         if(this.size.length){
2631             size = 'modal-' + this.size;
2632         }
2633         
2634         var modal = {
2635             cls: "modal",
2636             style : 'display: none',
2637             cn : [
2638                 {
2639                     cls: "modal-dialog " + size,
2640                     cn : [
2641                         {
2642                             cls : "modal-content",
2643                             cn : [
2644                                 {
2645                                     cls : 'modal-header',
2646                                     cn : header
2647                                 },
2648                                 bdy,
2649                                 {
2650                                     cls : 'modal-footer',
2651                                     cn : [
2652                                         {
2653                                             tag: 'div',
2654                                             cls: 'btn-' + this.buttonPosition
2655                                         }
2656                                     ]
2657                                     
2658                                 }
2659                                 
2660                                 
2661                             ]
2662                             
2663                         }
2664                     ]
2665                         
2666                 }
2667             ]
2668         };
2669         
2670         if(this.animate){
2671             modal.cls += ' fade';
2672         }
2673         
2674         return modal;
2675           
2676     },
2677     getChildContainer : function() {
2678          
2679          return this.bodyEl;
2680         
2681     },
2682     getButtonContainer : function() {
2683          return this.el.select('.modal-footer div',true).first();
2684         
2685     },
2686     initEvents : function()
2687     {
2688         if (this.allow_close) {
2689             this.closeEl.on('click', this.hide, this);
2690         }
2691         Roo.EventManager.onWindowResize(this.resize, this, true);
2692         
2693  
2694     },
2695     
2696     resize : function()
2697     {
2698         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2699         if (this.fitwindow) {
2700             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2701             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 30;
2702             this.setSize(w,h)
2703         }
2704     },
2705     
2706     setSize : function(w,h)
2707     {
2708         if (!w && !h) {
2709             return;
2710         }
2711         this.resizeTo(w,h);
2712     },
2713     
2714     show : function() {
2715         
2716         if (!this.rendered) {
2717             this.render();
2718         }
2719         
2720         this.el.setStyle('display', 'block');
2721         
2722         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2723             var _this = this;
2724             (function(){
2725                 this.el.addClass('in');
2726             }).defer(50, this);
2727         }else{
2728             this.el.addClass('in');
2729             
2730         }
2731         
2732         // not sure how we can show data in here.. 
2733         //if (this.tmpl) {
2734         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2735         //}
2736         
2737         Roo.get(document.body).addClass("x-body-masked");
2738         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2739         this.maskEl.show();
2740         this.el.setStyle('zIndex', '10001');
2741        
2742         this.fireEvent('show', this);
2743         this.items.forEach(function(e) {
2744             e.layout ? e.layout() : false;
2745                 
2746         });
2747         this.resize();
2748         
2749         
2750         
2751     },
2752     hide : function()
2753     {
2754         this.maskEl.hide();
2755         Roo.get(document.body).removeClass("x-body-masked");
2756         this.el.removeClass('in');
2757         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2758         
2759         if(this.animate){ // why
2760             var _this = this;
2761             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2762         }else{
2763             this.el.setStyle('display', 'none');
2764         }
2765         
2766         this.fireEvent('hide', this);
2767     },
2768     
2769     addButton : function(str, cb)
2770     {
2771          
2772         
2773         var b = Roo.apply({}, { html : str } );
2774         b.xns = b.xns || Roo.bootstrap;
2775         b.xtype = b.xtype || 'Button';
2776         if (typeof(b.listeners) == 'undefined') {
2777             b.listeners = { click : cb.createDelegate(this)  };
2778         }
2779         
2780         var btn = Roo.factory(b);
2781            
2782         btn.render(this.el.select('.modal-footer div').first());
2783         
2784         return btn;   
2785        
2786     },
2787     
2788     setDefaultButton : function(btn)
2789     {
2790         //this.el.select('.modal-footer').()
2791     },
2792     diff : false,
2793     
2794     resizeTo: function(w,h)
2795     {
2796         // skip.. ?? why??
2797         
2798         this.dialogEl.setWidth(w);
2799         if (this.diff === false) {
2800             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2801         }
2802         
2803         this.bodyEl.setHeight(h-this.diff);
2804         
2805         
2806     },
2807     setContentSize  : function(w, h)
2808     {
2809         
2810     },
2811     onButtonClick: function(btn,e)
2812     {
2813         //Roo.log([a,b,c]);
2814         this.fireEvent('btnclick', btn.name, e);
2815     },
2816      /**
2817      * Set the title of the Dialog
2818      * @param {String} str new Title
2819      */
2820     setTitle: function(str) {
2821         this.titleEl.dom.innerHTML = str;    
2822     },
2823     /**
2824      * Set the body of the Dialog
2825      * @param {String} str new Title
2826      */
2827     setBody: function(str) {
2828         this.bodyEl.dom.innerHTML = str;    
2829     },
2830     /**
2831      * Set the body of the Dialog using the template
2832      * @param {Obj} data - apply this data to the template and replace the body contents.
2833      */
2834     applyBody: function(obj)
2835     {
2836         if (!this.tmpl) {
2837             Roo.log("Error - using apply Body without a template");
2838             //code
2839         }
2840         this.tmpl.overwrite(this.bodyEl, obj);
2841     }
2842     
2843 });
2844
2845
2846 Roo.apply(Roo.bootstrap.Modal,  {
2847     /**
2848          * Button config that displays a single OK button
2849          * @type Object
2850          */
2851         OK :  [{
2852             name : 'ok',
2853             weight : 'primary',
2854             html : 'OK'
2855         }], 
2856         /**
2857          * Button config that displays Yes and No buttons
2858          * @type Object
2859          */
2860         YESNO : [
2861             {
2862                 name  : 'no',
2863                 html : 'No'
2864             },
2865             {
2866                 name  :'yes',
2867                 weight : 'primary',
2868                 html : 'Yes'
2869             }
2870         ],
2871         
2872         /**
2873          * Button config that displays OK and Cancel buttons
2874          * @type Object
2875          */
2876         OKCANCEL : [
2877             {
2878                name : 'cancel',
2879                 html : 'Cancel'
2880             },
2881             {
2882                 name : 'ok',
2883                 weight : 'primary',
2884                 html : 'OK'
2885             }
2886         ],
2887         /**
2888          * Button config that displays Yes, No and Cancel buttons
2889          * @type Object
2890          */
2891         YESNOCANCEL : [
2892             {
2893                 name : 'yes',
2894                 weight : 'primary',
2895                 html : 'Yes'
2896             },
2897             {
2898                 name : 'no',
2899                 html : 'No'
2900             },
2901             {
2902                 name : 'cancel',
2903                 html : 'Cancel'
2904             }
2905         ]
2906 });
2907  
2908  /*
2909  * - LGPL
2910  *
2911  * messagebox - can be used as a replace
2912  * 
2913  */
2914 /**
2915  * @class Roo.MessageBox
2916  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2917  * Example usage:
2918  *<pre><code>
2919 // Basic alert:
2920 Roo.Msg.alert('Status', 'Changes saved successfully.');
2921
2922 // Prompt for user data:
2923 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2924     if (btn == 'ok'){
2925         // process text value...
2926     }
2927 });
2928
2929 // Show a dialog using config options:
2930 Roo.Msg.show({
2931    title:'Save Changes?',
2932    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2933    buttons: Roo.Msg.YESNOCANCEL,
2934    fn: processResult,
2935    animEl: 'elId'
2936 });
2937 </code></pre>
2938  * @singleton
2939  */
2940 Roo.bootstrap.MessageBox = function(){
2941     var dlg, opt, mask, waitTimer;
2942     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2943     var buttons, activeTextEl, bwidth;
2944
2945     
2946     // private
2947     var handleButton = function(button){
2948         dlg.hide();
2949         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2950     };
2951
2952     // private
2953     var handleHide = function(){
2954         if(opt && opt.cls){
2955             dlg.el.removeClass(opt.cls);
2956         }
2957         //if(waitTimer){
2958         //    Roo.TaskMgr.stop(waitTimer);
2959         //    waitTimer = null;
2960         //}
2961     };
2962
2963     // private
2964     var updateButtons = function(b){
2965         var width = 0;
2966         if(!b){
2967             buttons["ok"].hide();
2968             buttons["cancel"].hide();
2969             buttons["yes"].hide();
2970             buttons["no"].hide();
2971             //dlg.footer.dom.style.display = 'none';
2972             return width;
2973         }
2974         dlg.footerEl.dom.style.display = '';
2975         for(var k in buttons){
2976             if(typeof buttons[k] != "function"){
2977                 if(b[k]){
2978                     buttons[k].show();
2979                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2980                     width += buttons[k].el.getWidth()+15;
2981                 }else{
2982                     buttons[k].hide();
2983                 }
2984             }
2985         }
2986         return width;
2987     };
2988
2989     // private
2990     var handleEsc = function(d, k, e){
2991         if(opt && opt.closable !== false){
2992             dlg.hide();
2993         }
2994         if(e){
2995             e.stopEvent();
2996         }
2997     };
2998
2999     return {
3000         /**
3001          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3002          * @return {Roo.BasicDialog} The BasicDialog element
3003          */
3004         getDialog : function(){
3005            if(!dlg){
3006                 dlg = new Roo.bootstrap.Modal( {
3007                     //draggable: true,
3008                     //resizable:false,
3009                     //constraintoviewport:false,
3010                     //fixedcenter:true,
3011                     //collapsible : false,
3012                     //shim:true,
3013                     //modal: true,
3014                   //  width:400,
3015                   //  height:100,
3016                     //buttonAlign:"center",
3017                     closeClick : function(){
3018                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3019                             handleButton("no");
3020                         }else{
3021                             handleButton("cancel");
3022                         }
3023                     }
3024                 });
3025                 dlg.render();
3026                 dlg.on("hide", handleHide);
3027                 mask = dlg.mask;
3028                 //dlg.addKeyListener(27, handleEsc);
3029                 buttons = {};
3030                 this.buttons = buttons;
3031                 var bt = this.buttonText;
3032                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3033                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3034                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3035                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3036                 //Roo.log(buttons);
3037                 bodyEl = dlg.bodyEl.createChild({
3038
3039                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3040                         '<textarea class="roo-mb-textarea"></textarea>' +
3041                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3042                 });
3043                 msgEl = bodyEl.dom.firstChild;
3044                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3045                 textboxEl.enableDisplayMode();
3046                 textboxEl.addKeyListener([10,13], function(){
3047                     if(dlg.isVisible() && opt && opt.buttons){
3048                         if(opt.buttons.ok){
3049                             handleButton("ok");
3050                         }else if(opt.buttons.yes){
3051                             handleButton("yes");
3052                         }
3053                     }
3054                 });
3055                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3056                 textareaEl.enableDisplayMode();
3057                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3058                 progressEl.enableDisplayMode();
3059                 var pf = progressEl.dom.firstChild;
3060                 if (pf) {
3061                     pp = Roo.get(pf.firstChild);
3062                     pp.setHeight(pf.offsetHeight);
3063                 }
3064                 
3065             }
3066             return dlg;
3067         },
3068
3069         /**
3070          * Updates the message box body text
3071          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3072          * the XHTML-compliant non-breaking space character '&amp;#160;')
3073          * @return {Roo.MessageBox} This message box
3074          */
3075         updateText : function(text){
3076             if(!dlg.isVisible() && !opt.width){
3077                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3078             }
3079             msgEl.innerHTML = text || '&#160;';
3080       
3081             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3082             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3083             var w = Math.max(
3084                     Math.min(opt.width || cw , this.maxWidth), 
3085                     Math.max(opt.minWidth || this.minWidth, bwidth)
3086             );
3087             if(opt.prompt){
3088                 activeTextEl.setWidth(w);
3089             }
3090             if(dlg.isVisible()){
3091                 dlg.fixedcenter = false;
3092             }
3093             // to big, make it scroll. = But as usual stupid IE does not support
3094             // !important..
3095             
3096             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3097                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3098                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3099             } else {
3100                 bodyEl.dom.style.height = '';
3101                 bodyEl.dom.style.overflowY = '';
3102             }
3103             if (cw > w) {
3104                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3105             } else {
3106                 bodyEl.dom.style.overflowX = '';
3107             }
3108             
3109             dlg.setContentSize(w, bodyEl.getHeight());
3110             if(dlg.isVisible()){
3111                 dlg.fixedcenter = true;
3112             }
3113             return this;
3114         },
3115
3116         /**
3117          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3118          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3119          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3120          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3121          * @return {Roo.MessageBox} This message box
3122          */
3123         updateProgress : function(value, text){
3124             if(text){
3125                 this.updateText(text);
3126             }
3127             if (pp) { // weird bug on my firefox - for some reason this is not defined
3128                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3129             }
3130             return this;
3131         },        
3132
3133         /**
3134          * Returns true if the message box is currently displayed
3135          * @return {Boolean} True if the message box is visible, else false
3136          */
3137         isVisible : function(){
3138             return dlg && dlg.isVisible();  
3139         },
3140
3141         /**
3142          * Hides the message box if it is displayed
3143          */
3144         hide : function(){
3145             if(this.isVisible()){
3146                 dlg.hide();
3147             }  
3148         },
3149
3150         /**
3151          * Displays a new message box, or reinitializes an existing message box, based on the config options
3152          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3153          * The following config object properties are supported:
3154          * <pre>
3155 Property    Type             Description
3156 ----------  ---------------  ------------------------------------------------------------------------------------
3157 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3158                                    closes (defaults to undefined)
3159 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3160                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3161 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3162                                    progress and wait dialogs will ignore this property and always hide the
3163                                    close button as they can only be closed programmatically.
3164 cls               String           A custom CSS class to apply to the message box element
3165 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3166                                    displayed (defaults to 75)
3167 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3168                                    function will be btn (the name of the button that was clicked, if applicable,
3169                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3170                                    Progress and wait dialogs will ignore this option since they do not respond to
3171                                    user actions and can only be closed programmatically, so any required function
3172                                    should be called by the same code after it closes the dialog.
3173 icon              String           A CSS class that provides a background image to be used as an icon for
3174                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3175 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3176 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3177 modal             Boolean          False to allow user interaction with the page while the message box is
3178                                    displayed (defaults to true)
3179 msg               String           A string that will replace the existing message box body text (defaults
3180                                    to the XHTML-compliant non-breaking space character '&#160;')
3181 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3182 progress          Boolean          True to display a progress bar (defaults to false)
3183 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3184 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3185 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3186 title             String           The title text
3187 value             String           The string value to set into the active textbox element if displayed
3188 wait              Boolean          True to display a progress bar (defaults to false)
3189 width             Number           The width of the dialog in pixels
3190 </pre>
3191          *
3192          * Example usage:
3193          * <pre><code>
3194 Roo.Msg.show({
3195    title: 'Address',
3196    msg: 'Please enter your address:',
3197    width: 300,
3198    buttons: Roo.MessageBox.OKCANCEL,
3199    multiline: true,
3200    fn: saveAddress,
3201    animEl: 'addAddressBtn'
3202 });
3203 </code></pre>
3204          * @param {Object} config Configuration options
3205          * @return {Roo.MessageBox} This message box
3206          */
3207         show : function(options)
3208         {
3209             
3210             // this causes nightmares if you show one dialog after another
3211             // especially on callbacks..
3212              
3213             if(this.isVisible()){
3214                 
3215                 this.hide();
3216                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3217                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3218                 Roo.log("New Dialog Message:" +  options.msg )
3219                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3220                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3221                 
3222             }
3223             var d = this.getDialog();
3224             opt = options;
3225             d.setTitle(opt.title || "&#160;");
3226             d.closeEl.setDisplayed(opt.closable !== false);
3227             activeTextEl = textboxEl;
3228             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3229             if(opt.prompt){
3230                 if(opt.multiline){
3231                     textboxEl.hide();
3232                     textareaEl.show();
3233                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3234                         opt.multiline : this.defaultTextHeight);
3235                     activeTextEl = textareaEl;
3236                 }else{
3237                     textboxEl.show();
3238                     textareaEl.hide();
3239                 }
3240             }else{
3241                 textboxEl.hide();
3242                 textareaEl.hide();
3243             }
3244             progressEl.setDisplayed(opt.progress === true);
3245             this.updateProgress(0);
3246             activeTextEl.dom.value = opt.value || "";
3247             if(opt.prompt){
3248                 dlg.setDefaultButton(activeTextEl);
3249             }else{
3250                 var bs = opt.buttons;
3251                 var db = null;
3252                 if(bs && bs.ok){
3253                     db = buttons["ok"];
3254                 }else if(bs && bs.yes){
3255                     db = buttons["yes"];
3256                 }
3257                 dlg.setDefaultButton(db);
3258             }
3259             bwidth = updateButtons(opt.buttons);
3260             this.updateText(opt.msg);
3261             if(opt.cls){
3262                 d.el.addClass(opt.cls);
3263             }
3264             d.proxyDrag = opt.proxyDrag === true;
3265             d.modal = opt.modal !== false;
3266             d.mask = opt.modal !== false ? mask : false;
3267             if(!d.isVisible()){
3268                 // force it to the end of the z-index stack so it gets a cursor in FF
3269                 document.body.appendChild(dlg.el.dom);
3270                 d.animateTarget = null;
3271                 d.show(options.animEl);
3272             }
3273             return this;
3274         },
3275
3276         /**
3277          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3278          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3279          * and closing the message box when the process is complete.
3280          * @param {String} title The title bar text
3281          * @param {String} msg The message box body text
3282          * @return {Roo.MessageBox} This message box
3283          */
3284         progress : function(title, msg){
3285             this.show({
3286                 title : title,
3287                 msg : msg,
3288                 buttons: false,
3289                 progress:true,
3290                 closable:false,
3291                 minWidth: this.minProgressWidth,
3292                 modal : true
3293             });
3294             return this;
3295         },
3296
3297         /**
3298          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3299          * If a callback function is passed it will be called after the user clicks the button, and the
3300          * id of the button that was clicked will be passed as the only parameter to the callback
3301          * (could also be the top-right close button).
3302          * @param {String} title The title bar text
3303          * @param {String} msg The message box body text
3304          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3305          * @param {Object} scope (optional) The scope of the callback function
3306          * @return {Roo.MessageBox} This message box
3307          */
3308         alert : function(title, msg, fn, scope){
3309             this.show({
3310                 title : title,
3311                 msg : msg,
3312                 buttons: this.OK,
3313                 fn: fn,
3314                 scope : scope,
3315                 modal : true
3316             });
3317             return this;
3318         },
3319
3320         /**
3321          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3322          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3323          * You are responsible for closing the message box when the process is complete.
3324          * @param {String} msg The message box body text
3325          * @param {String} title (optional) The title bar text
3326          * @return {Roo.MessageBox} This message box
3327          */
3328         wait : function(msg, title){
3329             this.show({
3330                 title : title,
3331                 msg : msg,
3332                 buttons: false,
3333                 closable:false,
3334                 progress:true,
3335                 modal:true,
3336                 width:300,
3337                 wait:true
3338             });
3339             waitTimer = Roo.TaskMgr.start({
3340                 run: function(i){
3341                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3342                 },
3343                 interval: 1000
3344             });
3345             return this;
3346         },
3347
3348         /**
3349          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3350          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3351          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3352          * @param {String} title The title bar text
3353          * @param {String} msg The message box body text
3354          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3355          * @param {Object} scope (optional) The scope of the callback function
3356          * @return {Roo.MessageBox} This message box
3357          */
3358         confirm : function(title, msg, fn, scope){
3359             this.show({
3360                 title : title,
3361                 msg : msg,
3362                 buttons: this.YESNO,
3363                 fn: fn,
3364                 scope : scope,
3365                 modal : true
3366             });
3367             return this;
3368         },
3369
3370         /**
3371          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3372          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3373          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3374          * (could also be the top-right close button) and the text that was entered will be passed as the two
3375          * parameters to the callback.
3376          * @param {String} title The title bar text
3377          * @param {String} msg The message box body text
3378          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3379          * @param {Object} scope (optional) The scope of the callback function
3380          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3381          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3382          * @return {Roo.MessageBox} This message box
3383          */
3384         prompt : function(title, msg, fn, scope, multiline){
3385             this.show({
3386                 title : title,
3387                 msg : msg,
3388                 buttons: this.OKCANCEL,
3389                 fn: fn,
3390                 minWidth:250,
3391                 scope : scope,
3392                 prompt:true,
3393                 multiline: multiline,
3394                 modal : true
3395             });
3396             return this;
3397         },
3398
3399         /**
3400          * Button config that displays a single OK button
3401          * @type Object
3402          */
3403         OK : {ok:true},
3404         /**
3405          * Button config that displays Yes and No buttons
3406          * @type Object
3407          */
3408         YESNO : {yes:true, no:true},
3409         /**
3410          * Button config that displays OK and Cancel buttons
3411          * @type Object
3412          */
3413         OKCANCEL : {ok:true, cancel:true},
3414         /**
3415          * Button config that displays Yes, No and Cancel buttons
3416          * @type Object
3417          */
3418         YESNOCANCEL : {yes:true, no:true, cancel:true},
3419
3420         /**
3421          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3422          * @type Number
3423          */
3424         defaultTextHeight : 75,
3425         /**
3426          * The maximum width in pixels of the message box (defaults to 600)
3427          * @type Number
3428          */
3429         maxWidth : 600,
3430         /**
3431          * The minimum width in pixels of the message box (defaults to 100)
3432          * @type Number
3433          */
3434         minWidth : 100,
3435         /**
3436          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3437          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3438          * @type Number
3439          */
3440         minProgressWidth : 250,
3441         /**
3442          * An object containing the default button text strings that can be overriden for localized language support.
3443          * Supported properties are: ok, cancel, yes and no.
3444          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3445          * @type Object
3446          */
3447         buttonText : {
3448             ok : "OK",
3449             cancel : "Cancel",
3450             yes : "Yes",
3451             no : "No"
3452         }
3453     };
3454 }();
3455
3456 /**
3457  * Shorthand for {@link Roo.MessageBox}
3458  */
3459 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3460 Roo.Msg = Roo.Msg || Roo.MessageBox;
3461 /*
3462  * - LGPL
3463  *
3464  * navbar
3465  * 
3466  */
3467
3468 /**
3469  * @class Roo.bootstrap.Navbar
3470  * @extends Roo.bootstrap.Component
3471  * Bootstrap Navbar class
3472
3473  * @constructor
3474  * Create a new Navbar
3475  * @param {Object} config The config object
3476  */
3477
3478
3479 Roo.bootstrap.Navbar = function(config){
3480     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3481     this.addEvents({
3482         // raw events
3483         /**
3484          * @event beforetoggle
3485          * Fire before toggle the menu
3486          * @param {Roo.EventObject} e
3487          */
3488         "beforetoggle" : true
3489     });
3490 };
3491
3492 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3493     
3494     
3495    
3496     // private
3497     navItems : false,
3498     loadMask : false,
3499     
3500     
3501     getAutoCreate : function(){
3502         
3503         
3504         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3505         
3506     },
3507     
3508     initEvents :function ()
3509     {
3510         //Roo.log(this.el.select('.navbar-toggle',true));
3511         this.el.select('.navbar-toggle',true).on('click', function() {
3512             if(this.fireEvent('beforetoggle', this) !== false){
3513                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3514             }
3515             
3516         }, this);
3517         
3518         var mark = {
3519             tag: "div",
3520             cls:"x-dlg-mask"
3521         };
3522         
3523         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3524         
3525         var size = this.el.getSize();
3526         this.maskEl.setSize(size.width, size.height);
3527         this.maskEl.enableDisplayMode("block");
3528         this.maskEl.hide();
3529         
3530         if(this.loadMask){
3531             this.maskEl.show();
3532         }
3533     },
3534     
3535     
3536     getChildContainer : function()
3537     {
3538         if (this.el.select('.collapse').getCount()) {
3539             return this.el.select('.collapse',true).first();
3540         }
3541         
3542         return this.el;
3543     },
3544     
3545     mask : function()
3546     {
3547         this.maskEl.show();
3548     },
3549     
3550     unmask : function()
3551     {
3552         this.maskEl.hide();
3553     } 
3554     
3555     
3556     
3557     
3558 });
3559
3560
3561
3562  
3563
3564  /*
3565  * - LGPL
3566  *
3567  * navbar
3568  * 
3569  */
3570
3571 /**
3572  * @class Roo.bootstrap.NavSimplebar
3573  * @extends Roo.bootstrap.Navbar
3574  * Bootstrap Sidebar class
3575  *
3576  * @cfg {Boolean} inverse is inverted color
3577  * 
3578  * @cfg {String} type (nav | pills | tabs)
3579  * @cfg {Boolean} arrangement stacked | justified
3580  * @cfg {String} align (left | right) alignment
3581  * 
3582  * @cfg {Boolean} main (true|false) main nav bar? default false
3583  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3584  * 
3585  * @cfg {String} tag (header|footer|nav|div) default is nav 
3586
3587  * 
3588  * 
3589  * 
3590  * @constructor
3591  * Create a new Sidebar
3592  * @param {Object} config The config object
3593  */
3594
3595
3596 Roo.bootstrap.NavSimplebar = function(config){
3597     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3598 };
3599
3600 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3601     
3602     inverse: false,
3603     
3604     type: false,
3605     arrangement: '',
3606     align : false,
3607     
3608     
3609     
3610     main : false,
3611     
3612     
3613     tag : false,
3614     
3615     
3616     getAutoCreate : function(){
3617         
3618         
3619         var cfg = {
3620             tag : this.tag || 'div',
3621             cls : 'navbar'
3622         };
3623           
3624         
3625         cfg.cn = [
3626             {
3627                 cls: 'nav',
3628                 tag : 'ul'
3629             }
3630         ];
3631         
3632          
3633         this.type = this.type || 'nav';
3634         if (['tabs','pills'].indexOf(this.type)!==-1) {
3635             cfg.cn[0].cls += ' nav-' + this.type
3636         
3637         
3638         } else {
3639             if (this.type!=='nav') {
3640                 Roo.log('nav type must be nav/tabs/pills')
3641             }
3642             cfg.cn[0].cls += ' navbar-nav'
3643         }
3644         
3645         
3646         
3647         
3648         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3649             cfg.cn[0].cls += ' nav-' + this.arrangement;
3650         }
3651         
3652         
3653         if (this.align === 'right') {
3654             cfg.cn[0].cls += ' navbar-right';
3655         }
3656         
3657         if (this.inverse) {
3658             cfg.cls += ' navbar-inverse';
3659             
3660         }
3661         
3662         
3663         return cfg;
3664     
3665         
3666     }
3667     
3668     
3669     
3670 });
3671
3672
3673
3674  
3675
3676  
3677        /*
3678  * - LGPL
3679  *
3680  * navbar
3681  * 
3682  */
3683
3684 /**
3685  * @class Roo.bootstrap.NavHeaderbar
3686  * @extends Roo.bootstrap.NavSimplebar
3687  * Bootstrap Sidebar class
3688  *
3689  * @cfg {String} brand what is brand
3690  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3691  * @cfg {String} brand_href href of the brand
3692  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3693  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3694  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3695  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3696  * 
3697  * @constructor
3698  * Create a new Sidebar
3699  * @param {Object} config The config object
3700  */
3701
3702
3703 Roo.bootstrap.NavHeaderbar = function(config){
3704     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3705       
3706 };
3707
3708 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3709     
3710     position: '',
3711     brand: '',
3712     brand_href: false,
3713     srButton : true,
3714     autohide : false,
3715     desktopCenter : false,
3716    
3717     
3718     getAutoCreate : function(){
3719         
3720         var   cfg = {
3721             tag: this.nav || 'nav',
3722             cls: 'navbar',
3723             role: 'navigation',
3724             cn: []
3725         };
3726         
3727         var cn = cfg.cn;
3728         if (this.desktopCenter) {
3729             cn.push({cls : 'container', cn : []});
3730             cn = cn[0].cn;
3731         }
3732         
3733         if(this.srButton){
3734             cn.push({
3735                 tag: 'div',
3736                 cls: 'navbar-header',
3737                 cn: [
3738                     {
3739                         tag: 'button',
3740                         type: 'button',
3741                         cls: 'navbar-toggle',
3742                         'data-toggle': 'collapse',
3743                         cn: [
3744                             {
3745                                 tag: 'span',
3746                                 cls: 'sr-only',
3747                                 html: 'Toggle navigation'
3748                             },
3749                             {
3750                                 tag: 'span',
3751                                 cls: 'icon-bar'
3752                             },
3753                             {
3754                                 tag: 'span',
3755                                 cls: 'icon-bar'
3756                             },
3757                             {
3758                                 tag: 'span',
3759                                 cls: 'icon-bar'
3760                             }
3761                         ]
3762                     }
3763                 ]
3764             });
3765         }
3766         
3767         cn.push({
3768             tag: 'div',
3769             cls: 'collapse navbar-collapse',
3770             cn : []
3771         });
3772         
3773         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3774         
3775         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3776             cfg.cls += ' navbar-' + this.position;
3777             
3778             // tag can override this..
3779             
3780             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3781         }
3782         
3783         if (this.brand !== '') {
3784             cn[0].cn.push({
3785                 tag: 'a',
3786                 href: this.brand_href ? this.brand_href : '#',
3787                 cls: 'navbar-brand',
3788                 cn: [
3789                 this.brand
3790                 ]
3791             });
3792         }
3793         
3794         if(this.main){
3795             cfg.cls += ' main-nav';
3796         }
3797         
3798         
3799         return cfg;
3800
3801         
3802     },
3803     getHeaderChildContainer : function()
3804     {
3805         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3806             return this.el.select('.navbar-header',true).first();
3807         }
3808         
3809         return this.getChildContainer();
3810     },
3811     
3812     
3813     initEvents : function()
3814     {
3815         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3816         
3817         if (this.autohide) {
3818             
3819             var prevScroll = 0;
3820             var ft = this.el;
3821             
3822             Roo.get(document).on('scroll',function(e) {
3823                 var ns = Roo.get(document).getScroll().top;
3824                 var os = prevScroll;
3825                 prevScroll = ns;
3826                 
3827                 if(ns > os){
3828                     ft.removeClass('slideDown');
3829                     ft.addClass('slideUp');
3830                     return;
3831                 }
3832                 ft.removeClass('slideUp');
3833                 ft.addClass('slideDown');
3834                  
3835               
3836           },this);
3837         }
3838     }    
3839     
3840 });
3841
3842
3843
3844  
3845
3846  /*
3847  * - LGPL
3848  *
3849  * navbar
3850  * 
3851  */
3852
3853 /**
3854  * @class Roo.bootstrap.NavSidebar
3855  * @extends Roo.bootstrap.Navbar
3856  * Bootstrap Sidebar class
3857  * 
3858  * @constructor
3859  * Create a new Sidebar
3860  * @param {Object} config The config object
3861  */
3862
3863
3864 Roo.bootstrap.NavSidebar = function(config){
3865     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3866 };
3867
3868 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3869     
3870     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3871     
3872     getAutoCreate : function(){
3873         
3874         
3875         return  {
3876             tag: 'div',
3877             cls: 'sidebar sidebar-nav'
3878         };
3879     
3880         
3881     }
3882     
3883     
3884     
3885 });
3886
3887
3888
3889  
3890
3891  /*
3892  * - LGPL
3893  *
3894  * nav group
3895  * 
3896  */
3897
3898 /**
3899  * @class Roo.bootstrap.NavGroup
3900  * @extends Roo.bootstrap.Component
3901  * Bootstrap NavGroup class
3902  * @cfg {String} align (left|right)
3903  * @cfg {Boolean} inverse
3904  * @cfg {String} type (nav|pills|tab) default nav
3905  * @cfg {String} navId - reference Id for navbar.
3906
3907  * 
3908  * @constructor
3909  * Create a new nav group
3910  * @param {Object} config The config object
3911  */
3912
3913 Roo.bootstrap.NavGroup = function(config){
3914     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3915     this.navItems = [];
3916    
3917     Roo.bootstrap.NavGroup.register(this);
3918      this.addEvents({
3919         /**
3920              * @event changed
3921              * Fires when the active item changes
3922              * @param {Roo.bootstrap.NavGroup} this
3923              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3924              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3925          */
3926         'changed': true
3927      });
3928     
3929 };
3930
3931 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3932     
3933     align: '',
3934     inverse: false,
3935     form: false,
3936     type: 'nav',
3937     navId : '',
3938     // private
3939     
3940     navItems : false, 
3941     
3942     getAutoCreate : function()
3943     {
3944         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3945         
3946         cfg = {
3947             tag : 'ul',
3948             cls: 'nav' 
3949         };
3950         
3951         if (['tabs','pills'].indexOf(this.type)!==-1) {
3952             cfg.cls += ' nav-' + this.type
3953         } else {
3954             if (this.type!=='nav') {
3955                 Roo.log('nav type must be nav/tabs/pills')
3956             }
3957             cfg.cls += ' navbar-nav'
3958         }
3959         
3960         if (this.parent().sidebar) {
3961             cfg = {
3962                 tag: 'ul',
3963                 cls: 'dashboard-menu sidebar-menu'
3964             };
3965             
3966             return cfg;
3967         }
3968         
3969         if (this.form === true) {
3970             cfg = {
3971                 tag: 'form',
3972                 cls: 'navbar-form'
3973             };
3974             
3975             if (this.align === 'right') {
3976                 cfg.cls += ' navbar-right';
3977             } else {
3978                 cfg.cls += ' navbar-left';
3979             }
3980         }
3981         
3982         if (this.align === 'right') {
3983             cfg.cls += ' navbar-right';
3984         }
3985         
3986         if (this.inverse) {
3987             cfg.cls += ' navbar-inverse';
3988             
3989         }
3990         
3991         
3992         return cfg;
3993     },
3994     /**
3995     * sets the active Navigation item
3996     * @param {Roo.bootstrap.NavItem} the new current navitem
3997     */
3998     setActiveItem : function(item)
3999     {
4000         var prev = false;
4001         Roo.each(this.navItems, function(v){
4002             if (v == item) {
4003                 return ;
4004             }
4005             if (v.isActive()) {
4006                 v.setActive(false, true);
4007                 prev = v;
4008                 
4009             }
4010             
4011         });
4012
4013         item.setActive(true, true);
4014         this.fireEvent('changed', this, item, prev);
4015         
4016         
4017     },
4018     /**
4019     * gets the active Navigation item
4020     * @return {Roo.bootstrap.NavItem} the current navitem
4021     */
4022     getActive : function()
4023     {
4024         
4025         var prev = false;
4026         Roo.each(this.navItems, function(v){
4027             
4028             if (v.isActive()) {
4029                 prev = v;
4030                 
4031             }
4032             
4033         });
4034         return prev;
4035     },
4036     
4037     indexOfNav : function()
4038     {
4039         
4040         var prev = false;
4041         Roo.each(this.navItems, function(v,i){
4042             
4043             if (v.isActive()) {
4044                 prev = i;
4045                 
4046             }
4047             
4048         });
4049         return prev;
4050     },
4051     /**
4052     * adds a Navigation item
4053     * @param {Roo.bootstrap.NavItem} the navitem to add
4054     */
4055     addItem : function(cfg)
4056     {
4057         var cn = new Roo.bootstrap.NavItem(cfg);
4058         this.register(cn);
4059         cn.parentId = this.id;
4060         cn.onRender(this.el, null);
4061         return cn;
4062     },
4063     /**
4064     * register a Navigation item
4065     * @param {Roo.bootstrap.NavItem} the navitem to add
4066     */
4067     register : function(item)
4068     {
4069         this.navItems.push( item);
4070         item.navId = this.navId;
4071     
4072     },
4073     
4074     /**
4075     * clear all the Navigation item
4076     */
4077    
4078     clearAll : function()
4079     {
4080         this.navItems = [];
4081         this.el.dom.innerHTML = '';
4082     },
4083     
4084     getNavItem: function(tabId)
4085     {
4086         var ret = false;
4087         Roo.each(this.navItems, function(e) {
4088             if (e.tabId == tabId) {
4089                ret =  e;
4090                return false;
4091             }
4092             return true;
4093             
4094         });
4095         return ret;
4096     },
4097     
4098     setActiveNext : function()
4099     {
4100         var i = this.indexOfNav(this.getActive());
4101         if (i > this.navItems.length) {
4102             return;
4103         }
4104         this.setActiveItem(this.navItems[i+1]);
4105     },
4106     setActivePrev : function()
4107     {
4108         var i = this.indexOfNav(this.getActive());
4109         if (i  < 1) {
4110             return;
4111         }
4112         this.setActiveItem(this.navItems[i-1]);
4113     },
4114     clearWasActive : function(except) {
4115         Roo.each(this.navItems, function(e) {
4116             if (e.tabId != except.tabId && e.was_active) {
4117                e.was_active = false;
4118                return false;
4119             }
4120             return true;
4121             
4122         });
4123     },
4124     getWasActive : function ()
4125     {
4126         var r = false;
4127         Roo.each(this.navItems, function(e) {
4128             if (e.was_active) {
4129                r = e;
4130                return false;
4131             }
4132             return true;
4133             
4134         });
4135         return r;
4136     }
4137     
4138     
4139 });
4140
4141  
4142 Roo.apply(Roo.bootstrap.NavGroup, {
4143     
4144     groups: {},
4145      /**
4146     * register a Navigation Group
4147     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4148     */
4149     register : function(navgrp)
4150     {
4151         this.groups[navgrp.navId] = navgrp;
4152         
4153     },
4154     /**
4155     * fetch a Navigation Group based on the navigation ID
4156     * @param {string} the navgroup to add
4157     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4158     */
4159     get: function(navId) {
4160         if (typeof(this.groups[navId]) == 'undefined') {
4161             return false;
4162             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4163         }
4164         return this.groups[navId] ;
4165     }
4166     
4167     
4168     
4169 });
4170
4171  /*
4172  * - LGPL
4173  *
4174  * row
4175  * 
4176  */
4177
4178 /**
4179  * @class Roo.bootstrap.NavItem
4180  * @extends Roo.bootstrap.Component
4181  * Bootstrap Navbar.NavItem class
4182  * @cfg {String} href  link to
4183  * @cfg {String} html content of button
4184  * @cfg {String} badge text inside badge
4185  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4186  * @cfg {String} glyphicon name of glyphicon
4187  * @cfg {String} icon name of font awesome icon
4188  * @cfg {Boolean} active Is item active
4189  * @cfg {Boolean} disabled Is item disabled
4190  
4191  * @cfg {Boolean} preventDefault (true | false) default false
4192  * @cfg {String} tabId the tab that this item activates.
4193  * @cfg {String} tagtype (a|span) render as a href or span?
4194  * @cfg {Boolean} animateRef (true|false) link to element default false  
4195   
4196  * @constructor
4197  * Create a new Navbar Item
4198  * @param {Object} config The config object
4199  */
4200 Roo.bootstrap.NavItem = function(config){
4201     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4202     this.addEvents({
4203         // raw events
4204         /**
4205          * @event click
4206          * The raw click event for the entire grid.
4207          * @param {Roo.EventObject} e
4208          */
4209         "click" : true,
4210          /**
4211             * @event changed
4212             * Fires when the active item active state changes
4213             * @param {Roo.bootstrap.NavItem} this
4214             * @param {boolean} state the new state
4215              
4216          */
4217         'changed': true,
4218         /**
4219             * @event scrollto
4220             * Fires when scroll to element
4221             * @param {Roo.bootstrap.NavItem} this
4222             * @param {Object} options
4223             * @param {Roo.EventObject} e
4224              
4225          */
4226         'scrollto': true
4227     });
4228    
4229 };
4230
4231 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4232     
4233     href: false,
4234     html: '',
4235     badge: '',
4236     icon: false,
4237     glyphicon: false,
4238     active: false,
4239     preventDefault : false,
4240     tabId : false,
4241     tagtype : 'a',
4242     disabled : false,
4243     animateRef : false,
4244     was_active : false,
4245     
4246     getAutoCreate : function(){
4247          
4248         var cfg = {
4249             tag: 'li',
4250             cls: 'nav-item'
4251             
4252         };
4253         
4254         if (this.active) {
4255             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4256         }
4257         if (this.disabled) {
4258             cfg.cls += ' disabled';
4259         }
4260         
4261         if (this.href || this.html || this.glyphicon || this.icon) {
4262             cfg.cn = [
4263                 {
4264                     tag: this.tagtype,
4265                     href : this.href || "#",
4266                     html: this.html || ''
4267                 }
4268             ];
4269             
4270             if (this.icon) {
4271                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4272             }
4273
4274             if(this.glyphicon) {
4275                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4276             }
4277             
4278             if (this.menu) {
4279                 
4280                 cfg.cn[0].html += " <span class='caret'></span>";
4281              
4282             }
4283             
4284             if (this.badge !== '') {
4285                  
4286                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4287             }
4288         }
4289         
4290         
4291         
4292         return cfg;
4293     },
4294     initEvents: function() 
4295     {
4296         if (typeof (this.menu) != 'undefined') {
4297             this.menu.parentType = this.xtype;
4298             this.menu.triggerEl = this.el;
4299             this.menu = this.addxtype(Roo.apply({}, this.menu));
4300         }
4301         
4302         this.el.select('a',true).on('click', this.onClick, this);
4303         
4304         if(this.tagtype == 'span'){
4305             this.el.select('span',true).on('click', this.onClick, this);
4306         }
4307        
4308         // at this point parent should be available..
4309         this.parent().register(this);
4310     },
4311     
4312     onClick : function(e)
4313     {
4314         if (e.getTarget('.dropdown-menu-item')) {
4315             // did you click on a menu itemm.... - then don't trigger onclick..
4316             return;
4317         }
4318         
4319         if(
4320                 this.preventDefault || 
4321                 this.href == '#' 
4322         ){
4323             Roo.log("NavItem - prevent Default?");
4324             e.preventDefault();
4325         }
4326         
4327         if (this.disabled) {
4328             return;
4329         }
4330         
4331         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4332         if (tg && tg.transition) {
4333             Roo.log("waiting for the transitionend");
4334             return;
4335         }
4336         
4337         
4338         
4339         //Roo.log("fire event clicked");
4340         if(this.fireEvent('click', this, e) === false){
4341             return;
4342         };
4343         
4344         if(this.tagtype == 'span'){
4345             return;
4346         }
4347         
4348         //Roo.log(this.href);
4349         var ael = this.el.select('a',true).first();
4350         //Roo.log(ael);
4351         
4352         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4353             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4354             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4355                 return; // ignore... - it's a 'hash' to another page.
4356             }
4357             Roo.log("NavItem - prevent Default?");
4358             e.preventDefault();
4359             this.scrollToElement(e);
4360         }
4361         
4362         
4363         var p =  this.parent();
4364    
4365         if (['tabs','pills'].indexOf(p.type)!==-1) {
4366             if (typeof(p.setActiveItem) !== 'undefined') {
4367                 p.setActiveItem(this);
4368             }
4369         }
4370         
4371         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4372         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4373             // remove the collapsed menu expand...
4374             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4375         }
4376     },
4377     
4378     isActive: function () {
4379         return this.active
4380     },
4381     setActive : function(state, fire, is_was_active)
4382     {
4383         if (this.active && !state && this.navId) {
4384             this.was_active = true;
4385             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4386             if (nv) {
4387                 nv.clearWasActive(this);
4388             }
4389             
4390         }
4391         this.active = state;
4392         
4393         if (!state ) {
4394             this.el.removeClass('active');
4395         } else if (!this.el.hasClass('active')) {
4396             this.el.addClass('active');
4397         }
4398         if (fire) {
4399             this.fireEvent('changed', this, state);
4400         }
4401         
4402         // show a panel if it's registered and related..
4403         
4404         if (!this.navId || !this.tabId || !state || is_was_active) {
4405             return;
4406         }
4407         
4408         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4409         if (!tg) {
4410             return;
4411         }
4412         var pan = tg.getPanelByName(this.tabId);
4413         if (!pan) {
4414             return;
4415         }
4416         // if we can not flip to new panel - go back to old nav highlight..
4417         if (false == tg.showPanel(pan)) {
4418             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4419             if (nv) {
4420                 var onav = nv.getWasActive();
4421                 if (onav) {
4422                     onav.setActive(true, false, true);
4423                 }
4424             }
4425             
4426         }
4427         
4428         
4429         
4430     },
4431      // this should not be here...
4432     setDisabled : function(state)
4433     {
4434         this.disabled = state;
4435         if (!state ) {
4436             this.el.removeClass('disabled');
4437         } else if (!this.el.hasClass('disabled')) {
4438             this.el.addClass('disabled');
4439         }
4440         
4441     },
4442     
4443     /**
4444      * Fetch the element to display the tooltip on.
4445      * @return {Roo.Element} defaults to this.el
4446      */
4447     tooltipEl : function()
4448     {
4449         return this.el.select('' + this.tagtype + '', true).first();
4450     },
4451     
4452     scrollToElement : function(e)
4453     {
4454         var c = document.body;
4455         
4456         /*
4457          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4458          */
4459         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4460             c = document.documentElement;
4461         }
4462         
4463         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4464         
4465         if(!target){
4466             return;
4467         }
4468
4469         var o = target.calcOffsetsTo(c);
4470         
4471         var options = {
4472             target : target,
4473             value : o[1]
4474         };
4475         
4476         this.fireEvent('scrollto', this, options, e);
4477         
4478         Roo.get(c).scrollTo('top', options.value, true);
4479         
4480         return;
4481     }
4482 });
4483  
4484
4485  /*
4486  * - LGPL
4487  *
4488  * sidebar item
4489  *
4490  *  li
4491  *    <span> icon </span>
4492  *    <span> text </span>
4493  *    <span>badge </span>
4494  */
4495
4496 /**
4497  * @class Roo.bootstrap.NavSidebarItem
4498  * @extends Roo.bootstrap.NavItem
4499  * Bootstrap Navbar.NavSidebarItem class
4500  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4501  * {bool} open is the menu open
4502  * @constructor
4503  * Create a new Navbar Button
4504  * @param {Object} config The config object
4505  */
4506 Roo.bootstrap.NavSidebarItem = function(config){
4507     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4508     this.addEvents({
4509         // raw events
4510         /**
4511          * @event click
4512          * The raw click event for the entire grid.
4513          * @param {Roo.EventObject} e
4514          */
4515         "click" : true,
4516          /**
4517             * @event changed
4518             * Fires when the active item active state changes
4519             * @param {Roo.bootstrap.NavSidebarItem} this
4520             * @param {boolean} state the new state
4521              
4522          */
4523         'changed': true
4524     });
4525    
4526 };
4527
4528 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4529     
4530     badgeWeight : 'default',
4531     
4532     open: false,
4533     
4534     getAutoCreate : function(){
4535         
4536         
4537         var a = {
4538                 tag: 'a',
4539                 href : this.href || '#',
4540                 cls: '',
4541                 html : '',
4542                 cn : []
4543         };
4544         var cfg = {
4545             tag: 'li',
4546             cls: '',
4547             cn: [ a ]
4548         };
4549         var span = {
4550             tag: 'span',
4551             html : this.html || ''
4552         };
4553         
4554         
4555         if (this.active) {
4556             cfg.cls += ' active';
4557         }
4558         
4559         if (this.disabled) {
4560             cfg.cls += ' disabled';
4561         }
4562         if (this.open) {
4563             cfg.cls += ' open x-open';
4564         }
4565         // left icon..
4566         if (this.glyphicon || this.icon) {
4567             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4568             a.cn.push({ tag : 'i', cls : c }) ;
4569         }
4570         // html..
4571         a.cn.push(span);
4572         // then badge..
4573         if (this.badge !== '') {
4574             
4575             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4576         }
4577         // fi
4578         if (this.menu) {
4579             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4580             a.cls += 'dropdown-toggle treeview' ;
4581         }
4582         
4583         return cfg;
4584          
4585            
4586     },
4587     
4588     initEvents : function()
4589     { 
4590         if (typeof (this.menu) != 'undefined') {
4591             this.menu.parentType = this.xtype;
4592             this.menu.triggerEl = this.el;
4593             this.menu = this.addxtype(Roo.apply({}, this.menu));
4594         }
4595         
4596         this.el.on('click', this.onClick, this);
4597        
4598     
4599         if(this.badge !== ''){
4600  
4601             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4602         }
4603         
4604     },
4605     
4606     onClick : function(e)
4607     {
4608         if(this.disabled){
4609             e.preventDefault();
4610             return;
4611         }
4612         
4613         if(this.preventDefault){
4614             e.preventDefault();
4615         }
4616         
4617         this.fireEvent('click', this);
4618     },
4619     
4620     disable : function()
4621     {
4622         this.setDisabled(true);
4623     },
4624     
4625     enable : function()
4626     {
4627         this.setDisabled(false);
4628     },
4629     
4630     setDisabled : function(state)
4631     {
4632         if(this.disabled == state){
4633             return;
4634         }
4635         
4636         this.disabled = state;
4637         
4638         if (state) {
4639             this.el.addClass('disabled');
4640             return;
4641         }
4642         
4643         this.el.removeClass('disabled');
4644         
4645         return;
4646     },
4647     
4648     setActive : function(state)
4649     {
4650         if(this.active == state){
4651             return;
4652         }
4653         
4654         this.active = state;
4655         
4656         if (state) {
4657             this.el.addClass('active');
4658             return;
4659         }
4660         
4661         this.el.removeClass('active');
4662         
4663         return;
4664     },
4665     
4666     isActive: function () 
4667     {
4668         return this.active;
4669     },
4670     
4671     setBadge : function(str)
4672     {
4673         if(!this.badgeEl){
4674             return;
4675         }
4676         
4677         this.badgeEl.dom.innerHTML = str;
4678     }
4679     
4680    
4681      
4682  
4683 });
4684  
4685
4686  /*
4687  * - LGPL
4688  *
4689  * row
4690  * 
4691  */
4692
4693 /**
4694  * @class Roo.bootstrap.Row
4695  * @extends Roo.bootstrap.Component
4696  * Bootstrap Row class (contains columns...)
4697  * 
4698  * @constructor
4699  * Create a new Row
4700  * @param {Object} config The config object
4701  */
4702
4703 Roo.bootstrap.Row = function(config){
4704     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4705 };
4706
4707 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4708     
4709     getAutoCreate : function(){
4710        return {
4711             cls: 'row clearfix'
4712        };
4713     }
4714     
4715     
4716 });
4717
4718  
4719
4720  /*
4721  * - LGPL
4722  *
4723  * element
4724  * 
4725  */
4726
4727 /**
4728  * @class Roo.bootstrap.Element
4729  * @extends Roo.bootstrap.Component
4730  * Bootstrap Element class
4731  * @cfg {String} html contents of the element
4732  * @cfg {String} tag tag of the element
4733  * @cfg {String} cls class of the element
4734  * @cfg {Boolean} preventDefault (true|false) default false
4735  * @cfg {Boolean} clickable (true|false) default false
4736  * 
4737  * @constructor
4738  * Create a new Element
4739  * @param {Object} config The config object
4740  */
4741
4742 Roo.bootstrap.Element = function(config){
4743     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4744     
4745     this.addEvents({
4746         // raw events
4747         /**
4748          * @event click
4749          * When a element is chick
4750          * @param {Roo.bootstrap.Element} this
4751          * @param {Roo.EventObject} e
4752          */
4753         "click" : true
4754     });
4755 };
4756
4757 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4758     
4759     tag: 'div',
4760     cls: '',
4761     html: '',
4762     preventDefault: false, 
4763     clickable: false,
4764     
4765     getAutoCreate : function(){
4766         
4767         var cfg = {
4768             tag: this.tag,
4769             cls: this.cls,
4770             html: this.html
4771         };
4772         
4773         return cfg;
4774     },
4775     
4776     initEvents: function() 
4777     {
4778         Roo.bootstrap.Element.superclass.initEvents.call(this);
4779         
4780         if(this.clickable){
4781             this.el.on('click', this.onClick, this);
4782         }
4783         
4784     },
4785     
4786     onClick : function(e)
4787     {
4788         if(this.preventDefault){
4789             e.preventDefault();
4790         }
4791         
4792         this.fireEvent('click', this, e);
4793     },
4794     
4795     getValue : function()
4796     {
4797         return this.el.dom.innerHTML;
4798     },
4799     
4800     setValue : function(value)
4801     {
4802         this.el.dom.innerHTML = value;
4803     }
4804    
4805 });
4806
4807  
4808
4809  /*
4810  * - LGPL
4811  *
4812  * pagination
4813  * 
4814  */
4815
4816 /**
4817  * @class Roo.bootstrap.Pagination
4818  * @extends Roo.bootstrap.Component
4819  * Bootstrap Pagination class
4820  * @cfg {String} size xs | sm | md | lg
4821  * @cfg {Boolean} inverse false | true
4822  * 
4823  * @constructor
4824  * Create a new Pagination
4825  * @param {Object} config The config object
4826  */
4827
4828 Roo.bootstrap.Pagination = function(config){
4829     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4830 };
4831
4832 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4833     
4834     cls: false,
4835     size: false,
4836     inverse: false,
4837     
4838     getAutoCreate : function(){
4839         var cfg = {
4840             tag: 'ul',
4841                 cls: 'pagination'
4842         };
4843         if (this.inverse) {
4844             cfg.cls += ' inverse';
4845         }
4846         if (this.html) {
4847             cfg.html=this.html;
4848         }
4849         if (this.cls) {
4850             cfg.cls += " " + this.cls;
4851         }
4852         return cfg;
4853     }
4854    
4855 });
4856
4857  
4858
4859  /*
4860  * - LGPL
4861  *
4862  * Pagination item
4863  * 
4864  */
4865
4866
4867 /**
4868  * @class Roo.bootstrap.PaginationItem
4869  * @extends Roo.bootstrap.Component
4870  * Bootstrap PaginationItem class
4871  * @cfg {String} html text
4872  * @cfg {String} href the link
4873  * @cfg {Boolean} preventDefault (true | false) default true
4874  * @cfg {Boolean} active (true | false) default false
4875  * @cfg {Boolean} disabled default false
4876  * 
4877  * 
4878  * @constructor
4879  * Create a new PaginationItem
4880  * @param {Object} config The config object
4881  */
4882
4883
4884 Roo.bootstrap.PaginationItem = function(config){
4885     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4886     this.addEvents({
4887         // raw events
4888         /**
4889          * @event click
4890          * The raw click event for the entire grid.
4891          * @param {Roo.EventObject} e
4892          */
4893         "click" : true
4894     });
4895 };
4896
4897 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4898     
4899     href : false,
4900     html : false,
4901     preventDefault: true,
4902     active : false,
4903     cls : false,
4904     disabled: false,
4905     
4906     getAutoCreate : function(){
4907         var cfg= {
4908             tag: 'li',
4909             cn: [
4910                 {
4911                     tag : 'a',
4912                     href : this.href ? this.href : '#',
4913                     html : this.html ? this.html : ''
4914                 }
4915             ]
4916         };
4917         
4918         if(this.cls){
4919             cfg.cls = this.cls;
4920         }
4921         
4922         if(this.disabled){
4923             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4924         }
4925         
4926         if(this.active){
4927             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4928         }
4929         
4930         return cfg;
4931     },
4932     
4933     initEvents: function() {
4934         
4935         this.el.on('click', this.onClick, this);
4936         
4937     },
4938     onClick : function(e)
4939     {
4940         Roo.log('PaginationItem on click ');
4941         if(this.preventDefault){
4942             e.preventDefault();
4943         }
4944         
4945         if(this.disabled){
4946             return;
4947         }
4948         
4949         this.fireEvent('click', this, e);
4950     }
4951    
4952 });
4953
4954  
4955
4956  /*
4957  * - LGPL
4958  *
4959  * slider
4960  * 
4961  */
4962
4963
4964 /**
4965  * @class Roo.bootstrap.Slider
4966  * @extends Roo.bootstrap.Component
4967  * Bootstrap Slider class
4968  *    
4969  * @constructor
4970  * Create a new Slider
4971  * @param {Object} config The config object
4972  */
4973
4974 Roo.bootstrap.Slider = function(config){
4975     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4976 };
4977
4978 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4979     
4980     getAutoCreate : function(){
4981         
4982         var cfg = {
4983             tag: 'div',
4984             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4985             cn: [
4986                 {
4987                     tag: 'a',
4988                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4989                 }
4990             ]
4991         };
4992         
4993         return cfg;
4994     }
4995    
4996 });
4997
4998  /*
4999  * Based on:
5000  * Ext JS Library 1.1.1
5001  * Copyright(c) 2006-2007, Ext JS, LLC.
5002  *
5003  * Originally Released Under LGPL - original licence link has changed is not relivant.
5004  *
5005  * Fork - LGPL
5006  * <script type="text/javascript">
5007  */
5008  
5009
5010 /**
5011  * @class Roo.grid.ColumnModel
5012  * @extends Roo.util.Observable
5013  * This is the default implementation of a ColumnModel used by the Grid. It defines
5014  * the columns in the grid.
5015  * <br>Usage:<br>
5016  <pre><code>
5017  var colModel = new Roo.grid.ColumnModel([
5018         {header: "Ticker", width: 60, sortable: true, locked: true},
5019         {header: "Company Name", width: 150, sortable: true},
5020         {header: "Market Cap.", width: 100, sortable: true},
5021         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5022         {header: "Employees", width: 100, sortable: true, resizable: false}
5023  ]);
5024  </code></pre>
5025  * <p>
5026  
5027  * The config options listed for this class are options which may appear in each
5028  * individual column definition.
5029  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5030  * @constructor
5031  * @param {Object} config An Array of column config objects. See this class's
5032  * config objects for details.
5033 */
5034 Roo.grid.ColumnModel = function(config){
5035         /**
5036      * The config passed into the constructor
5037      */
5038     this.config = config;
5039     this.lookup = {};
5040
5041     // if no id, create one
5042     // if the column does not have a dataIndex mapping,
5043     // map it to the order it is in the config
5044     for(var i = 0, len = config.length; i < len; i++){
5045         var c = config[i];
5046         if(typeof c.dataIndex == "undefined"){
5047             c.dataIndex = i;
5048         }
5049         if(typeof c.renderer == "string"){
5050             c.renderer = Roo.util.Format[c.renderer];
5051         }
5052         if(typeof c.id == "undefined"){
5053             c.id = Roo.id();
5054         }
5055         if(c.editor && c.editor.xtype){
5056             c.editor  = Roo.factory(c.editor, Roo.grid);
5057         }
5058         if(c.editor && c.editor.isFormField){
5059             c.editor = new Roo.grid.GridEditor(c.editor);
5060         }
5061         this.lookup[c.id] = c;
5062     }
5063
5064     /**
5065      * The width of columns which have no width specified (defaults to 100)
5066      * @type Number
5067      */
5068     this.defaultWidth = 100;
5069
5070     /**
5071      * Default sortable of columns which have no sortable specified (defaults to false)
5072      * @type Boolean
5073      */
5074     this.defaultSortable = false;
5075
5076     this.addEvents({
5077         /**
5078              * @event widthchange
5079              * Fires when the width of a column changes.
5080              * @param {ColumnModel} this
5081              * @param {Number} columnIndex The column index
5082              * @param {Number} newWidth The new width
5083              */
5084             "widthchange": true,
5085         /**
5086              * @event headerchange
5087              * Fires when the text of a header changes.
5088              * @param {ColumnModel} this
5089              * @param {Number} columnIndex The column index
5090              * @param {Number} newText The new header text
5091              */
5092             "headerchange": true,
5093         /**
5094              * @event hiddenchange
5095              * Fires when a column is hidden or "unhidden".
5096              * @param {ColumnModel} this
5097              * @param {Number} columnIndex The column index
5098              * @param {Boolean} hidden true if hidden, false otherwise
5099              */
5100             "hiddenchange": true,
5101             /**
5102          * @event columnmoved
5103          * Fires when a column is moved.
5104          * @param {ColumnModel} this
5105          * @param {Number} oldIndex
5106          * @param {Number} newIndex
5107          */
5108         "columnmoved" : true,
5109         /**
5110          * @event columlockchange
5111          * Fires when a column's locked state is changed
5112          * @param {ColumnModel} this
5113          * @param {Number} colIndex
5114          * @param {Boolean} locked true if locked
5115          */
5116         "columnlockchange" : true
5117     });
5118     Roo.grid.ColumnModel.superclass.constructor.call(this);
5119 };
5120 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5121     /**
5122      * @cfg {String} header The header text to display in the Grid view.
5123      */
5124     /**
5125      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5126      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5127      * specified, the column's index is used as an index into the Record's data Array.
5128      */
5129     /**
5130      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5131      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5132      */
5133     /**
5134      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5135      * Defaults to the value of the {@link #defaultSortable} property.
5136      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5137      */
5138     /**
5139      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5140      */
5141     /**
5142      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5143      */
5144     /**
5145      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5146      */
5147     /**
5148      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5149      */
5150     /**
5151      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5152      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5153      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5154      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5155      */
5156        /**
5157      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5158      */
5159     /**
5160      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5161      */
5162     /**
5163      * @cfg {String} cursor (Optional)
5164      */
5165     /**
5166      * @cfg {String} tooltip (Optional)
5167      */
5168     /**
5169      * @cfg {Number} xs (Optional)
5170      */
5171     /**
5172      * @cfg {Number} sm (Optional)
5173      */
5174     /**
5175      * @cfg {Number} md (Optional)
5176      */
5177     /**
5178      * @cfg {Number} lg (Optional)
5179      */
5180     /**
5181      * Returns the id of the column at the specified index.
5182      * @param {Number} index The column index
5183      * @return {String} the id
5184      */
5185     getColumnId : function(index){
5186         return this.config[index].id;
5187     },
5188
5189     /**
5190      * Returns the column for a specified id.
5191      * @param {String} id The column id
5192      * @return {Object} the column
5193      */
5194     getColumnById : function(id){
5195         return this.lookup[id];
5196     },
5197
5198     
5199     /**
5200      * Returns the column for a specified dataIndex.
5201      * @param {String} dataIndex The column dataIndex
5202      * @return {Object|Boolean} the column or false if not found
5203      */
5204     getColumnByDataIndex: function(dataIndex){
5205         var index = this.findColumnIndex(dataIndex);
5206         return index > -1 ? this.config[index] : false;
5207     },
5208     
5209     /**
5210      * Returns the index for a specified column id.
5211      * @param {String} id The column id
5212      * @return {Number} the index, or -1 if not found
5213      */
5214     getIndexById : function(id){
5215         for(var i = 0, len = this.config.length; i < len; i++){
5216             if(this.config[i].id == id){
5217                 return i;
5218             }
5219         }
5220         return -1;
5221     },
5222     
5223     /**
5224      * Returns the index for a specified column dataIndex.
5225      * @param {String} dataIndex The column dataIndex
5226      * @return {Number} the index, or -1 if not found
5227      */
5228     
5229     findColumnIndex : function(dataIndex){
5230         for(var i = 0, len = this.config.length; i < len; i++){
5231             if(this.config[i].dataIndex == dataIndex){
5232                 return i;
5233             }
5234         }
5235         return -1;
5236     },
5237     
5238     
5239     moveColumn : function(oldIndex, newIndex){
5240         var c = this.config[oldIndex];
5241         this.config.splice(oldIndex, 1);
5242         this.config.splice(newIndex, 0, c);
5243         this.dataMap = null;
5244         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5245     },
5246
5247     isLocked : function(colIndex){
5248         return this.config[colIndex].locked === true;
5249     },
5250
5251     setLocked : function(colIndex, value, suppressEvent){
5252         if(this.isLocked(colIndex) == value){
5253             return;
5254         }
5255         this.config[colIndex].locked = value;
5256         if(!suppressEvent){
5257             this.fireEvent("columnlockchange", this, colIndex, value);
5258         }
5259     },
5260
5261     getTotalLockedWidth : function(){
5262         var totalWidth = 0;
5263         for(var i = 0; i < this.config.length; i++){
5264             if(this.isLocked(i) && !this.isHidden(i)){
5265                 this.totalWidth += this.getColumnWidth(i);
5266             }
5267         }
5268         return totalWidth;
5269     },
5270
5271     getLockedCount : function(){
5272         for(var i = 0, len = this.config.length; i < len; i++){
5273             if(!this.isLocked(i)){
5274                 return i;
5275             }
5276         }
5277         
5278         return this.config.length;
5279     },
5280
5281     /**
5282      * Returns the number of columns.
5283      * @return {Number}
5284      */
5285     getColumnCount : function(visibleOnly){
5286         if(visibleOnly === true){
5287             var c = 0;
5288             for(var i = 0, len = this.config.length; i < len; i++){
5289                 if(!this.isHidden(i)){
5290                     c++;
5291                 }
5292             }
5293             return c;
5294         }
5295         return this.config.length;
5296     },
5297
5298     /**
5299      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5300      * @param {Function} fn
5301      * @param {Object} scope (optional)
5302      * @return {Array} result
5303      */
5304     getColumnsBy : function(fn, scope){
5305         var r = [];
5306         for(var i = 0, len = this.config.length; i < len; i++){
5307             var c = this.config[i];
5308             if(fn.call(scope||this, c, i) === true){
5309                 r[r.length] = c;
5310             }
5311         }
5312         return r;
5313     },
5314
5315     /**
5316      * Returns true if the specified column is sortable.
5317      * @param {Number} col The column index
5318      * @return {Boolean}
5319      */
5320     isSortable : function(col){
5321         if(typeof this.config[col].sortable == "undefined"){
5322             return this.defaultSortable;
5323         }
5324         return this.config[col].sortable;
5325     },
5326
5327     /**
5328      * Returns the rendering (formatting) function defined for the column.
5329      * @param {Number} col The column index.
5330      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5331      */
5332     getRenderer : function(col){
5333         if(!this.config[col].renderer){
5334             return Roo.grid.ColumnModel.defaultRenderer;
5335         }
5336         return this.config[col].renderer;
5337     },
5338
5339     /**
5340      * Sets the rendering (formatting) function for a column.
5341      * @param {Number} col The column index
5342      * @param {Function} fn The function to use to process the cell's raw data
5343      * to return HTML markup for the grid view. The render function is called with
5344      * the following parameters:<ul>
5345      * <li>Data value.</li>
5346      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5347      * <li>css A CSS style string to apply to the table cell.</li>
5348      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5349      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5350      * <li>Row index</li>
5351      * <li>Column index</li>
5352      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5353      */
5354     setRenderer : function(col, fn){
5355         this.config[col].renderer = fn;
5356     },
5357
5358     /**
5359      * Returns the width for the specified column.
5360      * @param {Number} col The column index
5361      * @return {Number}
5362      */
5363     getColumnWidth : function(col){
5364         return this.config[col].width * 1 || this.defaultWidth;
5365     },
5366
5367     /**
5368      * Sets the width for a column.
5369      * @param {Number} col The column index
5370      * @param {Number} width The new width
5371      */
5372     setColumnWidth : function(col, width, suppressEvent){
5373         this.config[col].width = width;
5374         this.totalWidth = null;
5375         if(!suppressEvent){
5376              this.fireEvent("widthchange", this, col, width);
5377         }
5378     },
5379
5380     /**
5381      * Returns the total width of all columns.
5382      * @param {Boolean} includeHidden True to include hidden column widths
5383      * @return {Number}
5384      */
5385     getTotalWidth : function(includeHidden){
5386         if(!this.totalWidth){
5387             this.totalWidth = 0;
5388             for(var i = 0, len = this.config.length; i < len; i++){
5389                 if(includeHidden || !this.isHidden(i)){
5390                     this.totalWidth += this.getColumnWidth(i);
5391                 }
5392             }
5393         }
5394         return this.totalWidth;
5395     },
5396
5397     /**
5398      * Returns the header for the specified column.
5399      * @param {Number} col The column index
5400      * @return {String}
5401      */
5402     getColumnHeader : function(col){
5403         return this.config[col].header;
5404     },
5405
5406     /**
5407      * Sets the header for a column.
5408      * @param {Number} col The column index
5409      * @param {String} header The new header
5410      */
5411     setColumnHeader : function(col, header){
5412         this.config[col].header = header;
5413         this.fireEvent("headerchange", this, col, header);
5414     },
5415
5416     /**
5417      * Returns the tooltip for the specified column.
5418      * @param {Number} col The column index
5419      * @return {String}
5420      */
5421     getColumnTooltip : function(col){
5422             return this.config[col].tooltip;
5423     },
5424     /**
5425      * Sets the tooltip for a column.
5426      * @param {Number} col The column index
5427      * @param {String} tooltip The new tooltip
5428      */
5429     setColumnTooltip : function(col, tooltip){
5430             this.config[col].tooltip = tooltip;
5431     },
5432
5433     /**
5434      * Returns the dataIndex for the specified column.
5435      * @param {Number} col The column index
5436      * @return {Number}
5437      */
5438     getDataIndex : function(col){
5439         return this.config[col].dataIndex;
5440     },
5441
5442     /**
5443      * Sets the dataIndex for a column.
5444      * @param {Number} col The column index
5445      * @param {Number} dataIndex The new dataIndex
5446      */
5447     setDataIndex : function(col, dataIndex){
5448         this.config[col].dataIndex = dataIndex;
5449     },
5450
5451     
5452     
5453     /**
5454      * Returns true if the cell is editable.
5455      * @param {Number} colIndex The column index
5456      * @param {Number} rowIndex The row index - this is nto actually used..?
5457      * @return {Boolean}
5458      */
5459     isCellEditable : function(colIndex, rowIndex){
5460         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5461     },
5462
5463     /**
5464      * Returns the editor defined for the cell/column.
5465      * return false or null to disable editing.
5466      * @param {Number} colIndex The column index
5467      * @param {Number} rowIndex The row index
5468      * @return {Object}
5469      */
5470     getCellEditor : function(colIndex, rowIndex){
5471         return this.config[colIndex].editor;
5472     },
5473
5474     /**
5475      * Sets if a column is editable.
5476      * @param {Number} col The column index
5477      * @param {Boolean} editable True if the column is editable
5478      */
5479     setEditable : function(col, editable){
5480         this.config[col].editable = editable;
5481     },
5482
5483
5484     /**
5485      * Returns true if the column is hidden.
5486      * @param {Number} colIndex The column index
5487      * @return {Boolean}
5488      */
5489     isHidden : function(colIndex){
5490         return this.config[colIndex].hidden;
5491     },
5492
5493
5494     /**
5495      * Returns true if the column width cannot be changed
5496      */
5497     isFixed : function(colIndex){
5498         return this.config[colIndex].fixed;
5499     },
5500
5501     /**
5502      * Returns true if the column can be resized
5503      * @return {Boolean}
5504      */
5505     isResizable : function(colIndex){
5506         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5507     },
5508     /**
5509      * Sets if a column is hidden.
5510      * @param {Number} colIndex The column index
5511      * @param {Boolean} hidden True if the column is hidden
5512      */
5513     setHidden : function(colIndex, hidden){
5514         this.config[colIndex].hidden = hidden;
5515         this.totalWidth = null;
5516         this.fireEvent("hiddenchange", this, colIndex, hidden);
5517     },
5518
5519     /**
5520      * Sets the editor for a column.
5521      * @param {Number} col The column index
5522      * @param {Object} editor The editor object
5523      */
5524     setEditor : function(col, editor){
5525         this.config[col].editor = editor;
5526     }
5527 });
5528
5529 Roo.grid.ColumnModel.defaultRenderer = function(value){
5530         if(typeof value == "string" && value.length < 1){
5531             return "&#160;";
5532         }
5533         return value;
5534 };
5535
5536 // Alias for backwards compatibility
5537 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5538 /*
5539  * Based on:
5540  * Ext JS Library 1.1.1
5541  * Copyright(c) 2006-2007, Ext JS, LLC.
5542  *
5543  * Originally Released Under LGPL - original licence link has changed is not relivant.
5544  *
5545  * Fork - LGPL
5546  * <script type="text/javascript">
5547  */
5548  
5549 /**
5550  * @class Roo.LoadMask
5551  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5552  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5553  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5554  * element's UpdateManager load indicator and will be destroyed after the initial load.
5555  * @constructor
5556  * Create a new LoadMask
5557  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5558  * @param {Object} config The config object
5559  */
5560 Roo.LoadMask = function(el, config){
5561     this.el = Roo.get(el);
5562     Roo.apply(this, config);
5563     if(this.store){
5564         this.store.on('beforeload', this.onBeforeLoad, this);
5565         this.store.on('load', this.onLoad, this);
5566         this.store.on('loadexception', this.onLoadException, this);
5567         this.removeMask = false;
5568     }else{
5569         var um = this.el.getUpdateManager();
5570         um.showLoadIndicator = false; // disable the default indicator
5571         um.on('beforeupdate', this.onBeforeLoad, this);
5572         um.on('update', this.onLoad, this);
5573         um.on('failure', this.onLoad, this);
5574         this.removeMask = true;
5575     }
5576 };
5577
5578 Roo.LoadMask.prototype = {
5579     /**
5580      * @cfg {Boolean} removeMask
5581      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5582      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5583      */
5584     /**
5585      * @cfg {String} msg
5586      * The text to display in a centered loading message box (defaults to 'Loading...')
5587      */
5588     msg : 'Loading...',
5589     /**
5590      * @cfg {String} msgCls
5591      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5592      */
5593     msgCls : 'x-mask-loading',
5594
5595     /**
5596      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5597      * @type Boolean
5598      */
5599     disabled: false,
5600
5601     /**
5602      * Disables the mask to prevent it from being displayed
5603      */
5604     disable : function(){
5605        this.disabled = true;
5606     },
5607
5608     /**
5609      * Enables the mask so that it can be displayed
5610      */
5611     enable : function(){
5612         this.disabled = false;
5613     },
5614     
5615     onLoadException : function()
5616     {
5617         Roo.log(arguments);
5618         
5619         if (typeof(arguments[3]) != 'undefined') {
5620             Roo.MessageBox.alert("Error loading",arguments[3]);
5621         } 
5622         /*
5623         try {
5624             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5625                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5626             }   
5627         } catch(e) {
5628             
5629         }
5630         */
5631     
5632         
5633         
5634         this.el.unmask(this.removeMask);
5635     },
5636     // private
5637     onLoad : function()
5638     {
5639         this.el.unmask(this.removeMask);
5640     },
5641
5642     // private
5643     onBeforeLoad : function(){
5644         if(!this.disabled){
5645             this.el.mask(this.msg, this.msgCls);
5646         }
5647     },
5648
5649     // private
5650     destroy : function(){
5651         if(this.store){
5652             this.store.un('beforeload', this.onBeforeLoad, this);
5653             this.store.un('load', this.onLoad, this);
5654             this.store.un('loadexception', this.onLoadException, this);
5655         }else{
5656             var um = this.el.getUpdateManager();
5657             um.un('beforeupdate', this.onBeforeLoad, this);
5658             um.un('update', this.onLoad, this);
5659             um.un('failure', this.onLoad, this);
5660         }
5661     }
5662 };/*
5663  * - LGPL
5664  *
5665  * table
5666  * 
5667  */
5668
5669 /**
5670  * @class Roo.bootstrap.Table
5671  * @extends Roo.bootstrap.Component
5672  * Bootstrap Table class
5673  * @cfg {String} cls table class
5674  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5675  * @cfg {String} bgcolor Specifies the background color for a table
5676  * @cfg {Number} border Specifies whether the table cells should have borders or not
5677  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5678  * @cfg {Number} cellspacing Specifies the space between cells
5679  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5680  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5681  * @cfg {String} sortable Specifies that the table should be sortable
5682  * @cfg {String} summary Specifies a summary of the content of a table
5683  * @cfg {Number} width Specifies the width of a table
5684  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5685  * 
5686  * @cfg {boolean} striped Should the rows be alternative striped
5687  * @cfg {boolean} bordered Add borders to the table
5688  * @cfg {boolean} hover Add hover highlighting
5689  * @cfg {boolean} condensed Format condensed
5690  * @cfg {boolean} responsive Format condensed
5691  * @cfg {Boolean} loadMask (true|false) default false
5692  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5693  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5694  * @cfg {Boolean} rowSelection (true|false) default false
5695  * @cfg {Boolean} cellSelection (true|false) default false
5696  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5697  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5698  
5699  * 
5700  * @constructor
5701  * Create a new Table
5702  * @param {Object} config The config object
5703  */
5704
5705 Roo.bootstrap.Table = function(config){
5706     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5707     
5708   
5709     
5710     // BC...
5711     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5712     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5713     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5714     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5715     
5716     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5717     if (this.sm) {
5718         this.sm.grid = this;
5719         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5720         this.sm = this.selModel;
5721         this.sm.xmodule = this.xmodule || false;
5722     }
5723     
5724     if (this.cm && typeof(this.cm.config) == 'undefined') {
5725         this.colModel = new Roo.grid.ColumnModel(this.cm);
5726         this.cm = this.colModel;
5727         this.cm.xmodule = this.xmodule || false;
5728     }
5729     if (this.store) {
5730         this.store= Roo.factory(this.store, Roo.data);
5731         this.ds = this.store;
5732         this.ds.xmodule = this.xmodule || false;
5733          
5734     }
5735     if (this.footer && this.store) {
5736         this.footer.dataSource = this.ds;
5737         this.footer = Roo.factory(this.footer);
5738     }
5739     
5740     /** @private */
5741     this.addEvents({
5742         /**
5743          * @event cellclick
5744          * Fires when a cell is clicked
5745          * @param {Roo.bootstrap.Table} this
5746          * @param {Roo.Element} el
5747          * @param {Number} rowIndex
5748          * @param {Number} columnIndex
5749          * @param {Roo.EventObject} e
5750          */
5751         "cellclick" : true,
5752         /**
5753          * @event celldblclick
5754          * Fires when a cell is double clicked
5755          * @param {Roo.bootstrap.Table} this
5756          * @param {Roo.Element} el
5757          * @param {Number} rowIndex
5758          * @param {Number} columnIndex
5759          * @param {Roo.EventObject} e
5760          */
5761         "celldblclick" : true,
5762         /**
5763          * @event rowclick
5764          * Fires when a row is clicked
5765          * @param {Roo.bootstrap.Table} this
5766          * @param {Roo.Element} el
5767          * @param {Number} rowIndex
5768          * @param {Roo.EventObject} e
5769          */
5770         "rowclick" : true,
5771         /**
5772          * @event rowdblclick
5773          * Fires when a row is double clicked
5774          * @param {Roo.bootstrap.Table} this
5775          * @param {Roo.Element} el
5776          * @param {Number} rowIndex
5777          * @param {Roo.EventObject} e
5778          */
5779         "rowdblclick" : true,
5780         /**
5781          * @event mouseover
5782          * Fires when a mouseover occur
5783          * @param {Roo.bootstrap.Table} this
5784          * @param {Roo.Element} el
5785          * @param {Number} rowIndex
5786          * @param {Number} columnIndex
5787          * @param {Roo.EventObject} e
5788          */
5789         "mouseover" : true,
5790         /**
5791          * @event mouseout
5792          * Fires when a mouseout occur
5793          * @param {Roo.bootstrap.Table} this
5794          * @param {Roo.Element} el
5795          * @param {Number} rowIndex
5796          * @param {Number} columnIndex
5797          * @param {Roo.EventObject} e
5798          */
5799         "mouseout" : true,
5800         /**
5801          * @event rowclass
5802          * Fires when a row is rendered, so you can change add a style to it.
5803          * @param {Roo.bootstrap.Table} this
5804          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5805          */
5806         'rowclass' : true,
5807           /**
5808          * @event rowsrendered
5809          * Fires when all the  rows have been rendered
5810          * @param {Roo.bootstrap.Table} this
5811          */
5812         'rowsrendered' : true,
5813         /**
5814          * @event contextmenu
5815          * The raw contextmenu event for the entire grid.
5816          * @param {Roo.EventObject} e
5817          */
5818         "contextmenu" : true,
5819         /**
5820          * @event rowcontextmenu
5821          * Fires when a row is right clicked
5822          * @param {Roo.bootstrap.Table} this
5823          * @param {Number} rowIndex
5824          * @param {Roo.EventObject} e
5825          */
5826         "rowcontextmenu" : true,
5827         /**
5828          * @event cellcontextmenu
5829          * Fires when a cell is right clicked
5830          * @param {Roo.bootstrap.Table} this
5831          * @param {Number} rowIndex
5832          * @param {Number} cellIndex
5833          * @param {Roo.EventObject} e
5834          */
5835          "cellcontextmenu" : true,
5836          /**
5837          * @event headercontextmenu
5838          * Fires when a header is right clicked
5839          * @param {Roo.bootstrap.Table} this
5840          * @param {Number} columnIndex
5841          * @param {Roo.EventObject} e
5842          */
5843         "headercontextmenu" : true
5844     });
5845 };
5846
5847 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5848     
5849     cls: false,
5850     align: false,
5851     bgcolor: false,
5852     border: false,
5853     cellpadding: false,
5854     cellspacing: false,
5855     frame: false,
5856     rules: false,
5857     sortable: false,
5858     summary: false,
5859     width: false,
5860     striped : false,
5861     scrollBody : false,
5862     bordered: false,
5863     hover:  false,
5864     condensed : false,
5865     responsive : false,
5866     sm : false,
5867     cm : false,
5868     store : false,
5869     loadMask : false,
5870     footerShow : true,
5871     headerShow : true,
5872   
5873     rowSelection : false,
5874     cellSelection : false,
5875     layout : false,
5876     
5877     // Roo.Element - the tbody
5878     mainBody: false,
5879     // Roo.Element - thead element
5880     mainHead: false,
5881     
5882     container: false, // used by gridpanel...
5883     
5884     getAutoCreate : function()
5885     {
5886         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5887         
5888         cfg = {
5889             tag: 'table',
5890             cls : 'table',
5891             cn : []
5892         };
5893         if (this.scrollBody) {
5894             cfg.cls += ' table-body-fixed';
5895         }    
5896         if (this.striped) {
5897             cfg.cls += ' table-striped';
5898         }
5899         
5900         if (this.hover) {
5901             cfg.cls += ' table-hover';
5902         }
5903         if (this.bordered) {
5904             cfg.cls += ' table-bordered';
5905         }
5906         if (this.condensed) {
5907             cfg.cls += ' table-condensed';
5908         }
5909         if (this.responsive) {
5910             cfg.cls += ' table-responsive';
5911         }
5912         
5913         if (this.cls) {
5914             cfg.cls+=  ' ' +this.cls;
5915         }
5916         
5917         // this lot should be simplifed...
5918         
5919         if (this.align) {
5920             cfg.align=this.align;
5921         }
5922         if (this.bgcolor) {
5923             cfg.bgcolor=this.bgcolor;
5924         }
5925         if (this.border) {
5926             cfg.border=this.border;
5927         }
5928         if (this.cellpadding) {
5929             cfg.cellpadding=this.cellpadding;
5930         }
5931         if (this.cellspacing) {
5932             cfg.cellspacing=this.cellspacing;
5933         }
5934         if (this.frame) {
5935             cfg.frame=this.frame;
5936         }
5937         if (this.rules) {
5938             cfg.rules=this.rules;
5939         }
5940         if (this.sortable) {
5941             cfg.sortable=this.sortable;
5942         }
5943         if (this.summary) {
5944             cfg.summary=this.summary;
5945         }
5946         if (this.width) {
5947             cfg.width=this.width;
5948         }
5949         if (this.layout) {
5950             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5951         }
5952         
5953         if(this.store || this.cm){
5954             if(this.headerShow){
5955                 cfg.cn.push(this.renderHeader());
5956             }
5957             
5958             cfg.cn.push(this.renderBody());
5959             
5960             if(this.footerShow){
5961                 cfg.cn.push(this.renderFooter());
5962             }
5963             // where does this come from?
5964             //cfg.cls+=  ' TableGrid';
5965         }
5966         
5967         return { cn : [ cfg ] };
5968     },
5969     
5970     initEvents : function()
5971     {   
5972         if(!this.store || !this.cm){
5973             return;
5974         }
5975         
5976         this.selModel.initEvents();
5977         
5978         //Roo.log('initEvents with ds!!!!');
5979         
5980         this.mainBody = this.el.select('tbody', true).first();
5981         this.mainHead = this.el.select('thead', true).first();
5982         
5983         
5984         
5985         
5986         var _this = this;
5987         
5988         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5989             e.on('click', _this.sort, _this);
5990         });
5991         
5992         this.el.on("click", this.onClick, this);
5993         this.el.on("dblclick", this.onDblClick, this);
5994         
5995         // why is this done????? = it breaks dialogs??
5996         //this.parent().el.setStyle('position', 'relative');
5997         
5998         
5999         if (this.footer) {
6000             this.footer.parentId = this.id;
6001             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6002         }
6003         
6004         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6005         
6006         this.store.on('load', this.onLoad, this);
6007         this.store.on('beforeload', this.onBeforeLoad, this);
6008         this.store.on('update', this.onUpdate, this);
6009         this.store.on('add', this.onAdd, this);
6010         this.store.on("clear", this.clear, this);
6011         
6012         this.el.on("contextmenu", this.onContextMenu, this);
6013         
6014         this.mainBody.on('scroll', this.onBodyScroll, this);
6015         
6016         
6017     },
6018     
6019     onContextMenu : function(e, t)
6020     {
6021         this.processEvent("contextmenu", e);
6022     },
6023     
6024     processEvent : function(name, e)
6025     {
6026         if (name != 'touchstart' ) {
6027             this.fireEvent(name, e);    
6028         }
6029         
6030         var t = e.getTarget();
6031         
6032         var cell = Roo.get(t);
6033         
6034         if(!cell){
6035             return;
6036         }
6037         
6038         if(cell.findParent('tfoot', false, true)){
6039             return;
6040         }
6041         
6042         if(cell.findParent('thead', false, true)){
6043             
6044             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6045                 cell = Roo.get(t).findParent('th', false, true);
6046                 if (!cell) {
6047                     Roo.log("failed to find th in thead?");
6048                     Roo.log(e.getTarget());
6049                     return;
6050                 }
6051             }
6052             
6053             var cellIndex = cell.dom.cellIndex;
6054             
6055             var ename = name == 'touchstart' ? 'click' : name;
6056             this.fireEvent("header" + ename, this, cellIndex, e);
6057             
6058             return;
6059         }
6060         
6061         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6062             cell = Roo.get(t).findParent('td', false, true);
6063             if (!cell) {
6064                 Roo.log("failed to find th in tbody?");
6065                 Roo.log(e.getTarget());
6066                 return;
6067             }
6068         }
6069         
6070         var row = cell.findParent('tr', false, true);
6071         var cellIndex = cell.dom.cellIndex;
6072         var rowIndex = row.dom.rowIndex - 1;
6073         
6074         if(row !== false){
6075             
6076             this.fireEvent("row" + name, this, rowIndex, e);
6077             
6078             if(cell !== false){
6079             
6080                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6081             }
6082         }
6083         
6084     },
6085     
6086     onMouseover : function(e, el)
6087     {
6088         var cell = Roo.get(el);
6089         
6090         if(!cell){
6091             return;
6092         }
6093         
6094         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6095             cell = cell.findParent('td', false, true);
6096         }
6097         
6098         var row = cell.findParent('tr', false, true);
6099         var cellIndex = cell.dom.cellIndex;
6100         var rowIndex = row.dom.rowIndex - 1; // start from 0
6101         
6102         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6103         
6104     },
6105     
6106     onMouseout : function(e, el)
6107     {
6108         var cell = Roo.get(el);
6109         
6110         if(!cell){
6111             return;
6112         }
6113         
6114         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6115             cell = cell.findParent('td', false, true);
6116         }
6117         
6118         var row = cell.findParent('tr', false, true);
6119         var cellIndex = cell.dom.cellIndex;
6120         var rowIndex = row.dom.rowIndex - 1; // start from 0
6121         
6122         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6123         
6124     },
6125     
6126     onClick : function(e, el)
6127     {
6128         var cell = Roo.get(el);
6129         
6130         if(!cell || (!this.cellSelection && !this.rowSelection)){
6131             return;
6132         }
6133         
6134         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6135             cell = cell.findParent('td', false, true);
6136         }
6137         
6138         if(!cell || typeof(cell) == 'undefined'){
6139             return;
6140         }
6141         
6142         var row = cell.findParent('tr', false, true);
6143         
6144         if(!row || typeof(row) == 'undefined'){
6145             return;
6146         }
6147         
6148         var cellIndex = cell.dom.cellIndex;
6149         var rowIndex = this.getRowIndex(row);
6150         
6151         // why??? - should these not be based on SelectionModel?
6152         if(this.cellSelection){
6153             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6154         }
6155         
6156         if(this.rowSelection){
6157             this.fireEvent('rowclick', this, row, rowIndex, e);
6158         }
6159         
6160         
6161     },
6162     
6163     onDblClick : function(e,el)
6164     {
6165         var cell = Roo.get(el);
6166         
6167         if(!cell || (!this.CellSelection && !this.RowSelection)){
6168             return;
6169         }
6170         
6171         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6172             cell = cell.findParent('td', false, true);
6173         }
6174         
6175         if(!cell || typeof(cell) == 'undefined'){
6176             return;
6177         }
6178         
6179         var row = cell.findParent('tr', false, true);
6180         
6181         if(!row || typeof(row) == 'undefined'){
6182             return;
6183         }
6184         
6185         var cellIndex = cell.dom.cellIndex;
6186         var rowIndex = this.getRowIndex(row);
6187         
6188         if(this.CellSelection){
6189             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6190         }
6191         
6192         if(this.RowSelection){
6193             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6194         }
6195     },
6196     
6197     sort : function(e,el)
6198     {
6199         var col = Roo.get(el);
6200         
6201         if(!col.hasClass('sortable')){
6202             return;
6203         }
6204         
6205         var sort = col.attr('sort');
6206         var dir = 'ASC';
6207         
6208         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6209             dir = 'DESC';
6210         }
6211         
6212         this.store.sortInfo = {field : sort, direction : dir};
6213         
6214         if (this.footer) {
6215             Roo.log("calling footer first");
6216             this.footer.onClick('first');
6217         } else {
6218         
6219             this.store.load({ params : { start : 0 } });
6220         }
6221     },
6222     
6223     renderHeader : function()
6224     {
6225         var header = {
6226             tag: 'thead',
6227             cn : []
6228         };
6229         
6230         var cm = this.cm;
6231         this.totalWidth = 0;
6232         
6233         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6234             
6235             var config = cm.config[i];
6236             
6237             var c = {
6238                 tag: 'th',
6239                 style : '',
6240                 html: cm.getColumnHeader(i)
6241             };
6242             
6243             var hh = '';
6244             
6245             if(typeof(config.sortable) != 'undefined' && config.sortable){
6246                 c.cls = 'sortable';
6247                 c.html = '<i class="glyphicon"></i>' + c.html;
6248             }
6249             
6250             if(typeof(config.lgHeader) != 'undefined'){
6251                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6252             }
6253             
6254             if(typeof(config.mdHeader) != 'undefined'){
6255                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6256             }
6257             
6258             if(typeof(config.smHeader) != 'undefined'){
6259                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6260             }
6261             
6262             if(typeof(config.xsHeader) != 'undefined'){
6263                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6264             }
6265             
6266             if(hh.length){
6267                 c.html = hh;
6268             }
6269             
6270             if(typeof(config.tooltip) != 'undefined'){
6271                 c.tooltip = config.tooltip;
6272             }
6273             
6274             if(typeof(config.colspan) != 'undefined'){
6275                 c.colspan = config.colspan;
6276             }
6277             
6278             if(typeof(config.hidden) != 'undefined' && config.hidden){
6279                 c.style += ' display:none;';
6280             }
6281             
6282             if(typeof(config.dataIndex) != 'undefined'){
6283                 c.sort = config.dataIndex;
6284             }
6285             
6286            
6287             
6288             if(typeof(config.align) != 'undefined' && config.align.length){
6289                 c.style += ' text-align:' + config.align + ';';
6290             }
6291             
6292             if(typeof(config.width) != 'undefined'){
6293                 c.style += ' width:' + config.width + 'px;';
6294                 this.totalWidth += config.width;
6295             } else {
6296                 this.totalWidth += 100; // assume minimum of 100 per column?
6297             }
6298             
6299             if(typeof(config.cls) != 'undefined'){
6300                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6301             }
6302             
6303             ['xs','sm','md','lg'].map(function(size){
6304                 
6305                 if(typeof(config[size]) == 'undefined'){
6306                     return;
6307                 }
6308                 
6309                 if (!config[size]) { // 0 = hidden
6310                     c.cls += ' hidden-' + size;
6311                     return;
6312                 }
6313                 
6314                 c.cls += ' col-' + size + '-' + config[size];
6315
6316             });
6317             
6318             header.cn.push(c)
6319         }
6320         
6321         return header;
6322     },
6323     
6324     renderBody : function()
6325     {
6326         var body = {
6327             tag: 'tbody',
6328             cn : [
6329                 {
6330                     tag: 'tr',
6331                     cn : [
6332                         {
6333                             tag : 'td',
6334                             colspan :  this.cm.getColumnCount()
6335                         }
6336                     ]
6337                 }
6338             ]
6339         };
6340         
6341         return body;
6342     },
6343     
6344     renderFooter : function()
6345     {
6346         var footer = {
6347             tag: 'tfoot',
6348             cn : [
6349                 {
6350                     tag: 'tr',
6351                     cn : [
6352                         {
6353                             tag : 'td',
6354                             colspan :  this.cm.getColumnCount()
6355                         }
6356                     ]
6357                 }
6358             ]
6359         };
6360         
6361         return footer;
6362     },
6363     
6364     
6365     
6366     onLoad : function()
6367     {
6368 //        Roo.log('ds onload');
6369         this.clear();
6370         
6371         var _this = this;
6372         var cm = this.cm;
6373         var ds = this.store;
6374         
6375         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6376             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6377             if (_this.store.sortInfo) {
6378                     
6379                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6380                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6381                 }
6382                 
6383                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6384                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6385                 }
6386             }
6387         });
6388         
6389         var tbody =  this.mainBody;
6390               
6391         if(ds.getCount() > 0){
6392             ds.data.each(function(d,rowIndex){
6393                 var row =  this.renderRow(cm, ds, rowIndex);
6394                 
6395                 tbody.createChild(row);
6396                 
6397                 var _this = this;
6398                 
6399                 if(row.cellObjects.length){
6400                     Roo.each(row.cellObjects, function(r){
6401                         _this.renderCellObject(r);
6402                     })
6403                 }
6404                 
6405             }, this);
6406         }
6407         
6408         Roo.each(this.el.select('tbody td', true).elements, function(e){
6409             e.on('mouseover', _this.onMouseover, _this);
6410         });
6411         
6412         Roo.each(this.el.select('tbody td', true).elements, function(e){
6413             e.on('mouseout', _this.onMouseout, _this);
6414         });
6415         this.fireEvent('rowsrendered', this);
6416         //if(this.loadMask){
6417         //    this.maskEl.hide();
6418         //}
6419         
6420         this.autoSize();
6421     },
6422     
6423     
6424     onUpdate : function(ds,record)
6425     {
6426         this.refreshRow(record);
6427     },
6428     
6429     onRemove : function(ds, record, index, isUpdate){
6430         if(isUpdate !== true){
6431             this.fireEvent("beforerowremoved", this, index, record);
6432         }
6433         var bt = this.mainBody.dom;
6434         
6435         var rows = this.el.select('tbody > tr', true).elements;
6436         
6437         if(typeof(rows[index]) != 'undefined'){
6438             bt.removeChild(rows[index].dom);
6439         }
6440         
6441 //        if(bt.rows[index]){
6442 //            bt.removeChild(bt.rows[index]);
6443 //        }
6444         
6445         if(isUpdate !== true){
6446             //this.stripeRows(index);
6447             //this.syncRowHeights(index, index);
6448             //this.layout();
6449             this.fireEvent("rowremoved", this, index, record);
6450         }
6451     },
6452     
6453     onAdd : function(ds, records, rowIndex)
6454     {
6455         //Roo.log('on Add called');
6456         // - note this does not handle multiple adding very well..
6457         var bt = this.mainBody.dom;
6458         for (var i =0 ; i < records.length;i++) {
6459             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6460             //Roo.log(records[i]);
6461             //Roo.log(this.store.getAt(rowIndex+i));
6462             this.insertRow(this.store, rowIndex + i, false);
6463             return;
6464         }
6465         
6466     },
6467     
6468     
6469     refreshRow : function(record){
6470         var ds = this.store, index;
6471         if(typeof record == 'number'){
6472             index = record;
6473             record = ds.getAt(index);
6474         }else{
6475             index = ds.indexOf(record);
6476         }
6477         this.insertRow(ds, index, true);
6478         this.onRemove(ds, record, index+1, true);
6479         //this.syncRowHeights(index, index);
6480         //this.layout();
6481         this.fireEvent("rowupdated", this, index, record);
6482     },
6483     
6484     insertRow : function(dm, rowIndex, isUpdate){
6485         
6486         if(!isUpdate){
6487             this.fireEvent("beforerowsinserted", this, rowIndex);
6488         }
6489             //var s = this.getScrollState();
6490         var row = this.renderRow(this.cm, this.store, rowIndex);
6491         // insert before rowIndex..
6492         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6493         
6494         var _this = this;
6495                 
6496         if(row.cellObjects.length){
6497             Roo.each(row.cellObjects, function(r){
6498                 _this.renderCellObject(r);
6499             })
6500         }
6501             
6502         if(!isUpdate){
6503             this.fireEvent("rowsinserted", this, rowIndex);
6504             //this.syncRowHeights(firstRow, lastRow);
6505             //this.stripeRows(firstRow);
6506             //this.layout();
6507         }
6508         
6509     },
6510     
6511     
6512     getRowDom : function(rowIndex)
6513     {
6514         var rows = this.el.select('tbody > tr', true).elements;
6515         
6516         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6517         
6518     },
6519     // returns the object tree for a tr..
6520   
6521     
6522     renderRow : function(cm, ds, rowIndex) 
6523     {
6524         
6525         var d = ds.getAt(rowIndex);
6526         
6527         var row = {
6528             tag : 'tr',
6529             cn : []
6530         };
6531             
6532         var cellObjects = [];
6533         
6534         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6535             var config = cm.config[i];
6536             
6537             var renderer = cm.getRenderer(i);
6538             var value = '';
6539             var id = false;
6540             
6541             if(typeof(renderer) !== 'undefined'){
6542                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6543             }
6544             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6545             // and are rendered into the cells after the row is rendered - using the id for the element.
6546             
6547             if(typeof(value) === 'object'){
6548                 id = Roo.id();
6549                 cellObjects.push({
6550                     container : id,
6551                     cfg : value 
6552                 })
6553             }
6554             
6555             var rowcfg = {
6556                 record: d,
6557                 rowIndex : rowIndex,
6558                 colIndex : i,
6559                 rowClass : ''
6560             };
6561
6562             this.fireEvent('rowclass', this, rowcfg);
6563             
6564             var td = {
6565                 tag: 'td',
6566                 cls : rowcfg.rowClass,
6567                 style: '',
6568                 html: (typeof(value) === 'object') ? '' : value
6569             };
6570             
6571             if (id) {
6572                 td.id = id;
6573             }
6574             
6575             if(typeof(config.colspan) != 'undefined'){
6576                 td.colspan = config.colspan;
6577             }
6578             
6579             if(typeof(config.hidden) != 'undefined' && config.hidden){
6580                 td.style += ' display:none;';
6581             }
6582             
6583             if(typeof(config.align) != 'undefined' && config.align.length){
6584                 td.style += ' text-align:' + config.align + ';';
6585             }
6586             
6587             if(typeof(config.width) != 'undefined'){
6588                 td.style += ' width:' +  config.width + 'px;';
6589             }
6590             
6591             if(typeof(config.cursor) != 'undefined'){
6592                 td.style += ' cursor:' +  config.cursor + ';';
6593             }
6594             
6595             if(typeof(config.cls) != 'undefined'){
6596                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6597             }
6598             
6599             ['xs','sm','md','lg'].map(function(size){
6600                 
6601                 if(typeof(config[size]) == 'undefined'){
6602                     return;
6603                 }
6604                 
6605                 if (!config[size]) { // 0 = hidden
6606                     td.cls += ' hidden-' + size;
6607                     return;
6608                 }
6609                 
6610                 td.cls += ' col-' + size + '-' + config[size];
6611
6612             });
6613              
6614             row.cn.push(td);
6615            
6616         }
6617         
6618         row.cellObjects = cellObjects;
6619         
6620         return row;
6621           
6622     },
6623     
6624     
6625     
6626     onBeforeLoad : function()
6627     {
6628         //Roo.log('ds onBeforeLoad');
6629         
6630         //this.clear();
6631         
6632         //if(this.loadMask){
6633         //    this.maskEl.show();
6634         //}
6635     },
6636      /**
6637      * Remove all rows
6638      */
6639     clear : function()
6640     {
6641         this.el.select('tbody', true).first().dom.innerHTML = '';
6642     },
6643     /**
6644      * Show or hide a row.
6645      * @param {Number} rowIndex to show or hide
6646      * @param {Boolean} state hide
6647      */
6648     setRowVisibility : function(rowIndex, state)
6649     {
6650         var bt = this.mainBody.dom;
6651         
6652         var rows = this.el.select('tbody > tr', true).elements;
6653         
6654         if(typeof(rows[rowIndex]) == 'undefined'){
6655             return;
6656         }
6657         rows[rowIndex].dom.style.display = state ? '' : 'none';
6658     },
6659     
6660     
6661     getSelectionModel : function(){
6662         if(!this.selModel){
6663             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6664         }
6665         return this.selModel;
6666     },
6667     /*
6668      * Render the Roo.bootstrap object from renderder
6669      */
6670     renderCellObject : function(r)
6671     {
6672         var _this = this;
6673         
6674         var t = r.cfg.render(r.container);
6675         
6676         if(r.cfg.cn){
6677             Roo.each(r.cfg.cn, function(c){
6678                 var child = {
6679                     container: t.getChildContainer(),
6680                     cfg: c
6681                 };
6682                 _this.renderCellObject(child);
6683             })
6684         }
6685     },
6686     
6687     getRowIndex : function(row)
6688     {
6689         var rowIndex = -1;
6690         
6691         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6692             if(el != row){
6693                 return;
6694             }
6695             
6696             rowIndex = index;
6697         });
6698         
6699         return rowIndex;
6700     },
6701      /**
6702      * Returns the grid's underlying element = used by panel.Grid
6703      * @return {Element} The element
6704      */
6705     getGridEl : function(){
6706         return this.el;
6707     },
6708      /**
6709      * Forces a resize - used by panel.Grid
6710      * @return {Element} The element
6711      */
6712     autoSize : function()
6713     {
6714         //var ctr = Roo.get(this.container.dom.parentElement);
6715         var ctr = Roo.get(this.el.dom);
6716         
6717         var thd = this.getGridEl().select('thead',true).first();
6718         var tbd = this.getGridEl().select('tbody', true).first();
6719         
6720         
6721         var cw = ctr.getWidth();
6722         
6723         if (tbd) {
6724             
6725             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6726             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6727             cw -= barsize;
6728         }
6729         cw = Math.max(cw, this.totalWidth);
6730         this.getGridEl().select('tr',true).setWidth(cw);
6731         // resize 'expandable coloumn?
6732         
6733         return; // we doe not have a view in this design..
6734         
6735     },
6736     onBodyScroll: function()
6737     {
6738         
6739         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6740         this.mainHead.setStyle({
6741                     'position' : 'relative',
6742                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6743         });
6744         
6745         
6746     }
6747 });
6748
6749  
6750
6751  /*
6752  * - LGPL
6753  *
6754  * table cell
6755  * 
6756  */
6757
6758 /**
6759  * @class Roo.bootstrap.TableCell
6760  * @extends Roo.bootstrap.Component
6761  * Bootstrap TableCell class
6762  * @cfg {String} html cell contain text
6763  * @cfg {String} cls cell class
6764  * @cfg {String} tag cell tag (td|th) default td
6765  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6766  * @cfg {String} align Aligns the content in a cell
6767  * @cfg {String} axis Categorizes cells
6768  * @cfg {String} bgcolor Specifies the background color of a cell
6769  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6770  * @cfg {Number} colspan Specifies the number of columns a cell should span
6771  * @cfg {String} headers Specifies one or more header cells a cell is related to
6772  * @cfg {Number} height Sets the height of a cell
6773  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6774  * @cfg {Number} rowspan Sets the number of rows a cell should span
6775  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6776  * @cfg {String} valign Vertical aligns the content in a cell
6777  * @cfg {Number} width Specifies the width of a cell
6778  * 
6779  * @constructor
6780  * Create a new TableCell
6781  * @param {Object} config The config object
6782  */
6783
6784 Roo.bootstrap.TableCell = function(config){
6785     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6786 };
6787
6788 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6789     
6790     html: false,
6791     cls: false,
6792     tag: false,
6793     abbr: false,
6794     align: false,
6795     axis: false,
6796     bgcolor: false,
6797     charoff: false,
6798     colspan: false,
6799     headers: false,
6800     height: false,
6801     nowrap: false,
6802     rowspan: false,
6803     scope: false,
6804     valign: false,
6805     width: false,
6806     
6807     
6808     getAutoCreate : function(){
6809         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6810         
6811         cfg = {
6812             tag: 'td'
6813         };
6814         
6815         if(this.tag){
6816             cfg.tag = this.tag;
6817         }
6818         
6819         if (this.html) {
6820             cfg.html=this.html
6821         }
6822         if (this.cls) {
6823             cfg.cls=this.cls
6824         }
6825         if (this.abbr) {
6826             cfg.abbr=this.abbr
6827         }
6828         if (this.align) {
6829             cfg.align=this.align
6830         }
6831         if (this.axis) {
6832             cfg.axis=this.axis
6833         }
6834         if (this.bgcolor) {
6835             cfg.bgcolor=this.bgcolor
6836         }
6837         if (this.charoff) {
6838             cfg.charoff=this.charoff
6839         }
6840         if (this.colspan) {
6841             cfg.colspan=this.colspan
6842         }
6843         if (this.headers) {
6844             cfg.headers=this.headers
6845         }
6846         if (this.height) {
6847             cfg.height=this.height
6848         }
6849         if (this.nowrap) {
6850             cfg.nowrap=this.nowrap
6851         }
6852         if (this.rowspan) {
6853             cfg.rowspan=this.rowspan
6854         }
6855         if (this.scope) {
6856             cfg.scope=this.scope
6857         }
6858         if (this.valign) {
6859             cfg.valign=this.valign
6860         }
6861         if (this.width) {
6862             cfg.width=this.width
6863         }
6864         
6865         
6866         return cfg;
6867     }
6868    
6869 });
6870
6871  
6872
6873  /*
6874  * - LGPL
6875  *
6876  * table row
6877  * 
6878  */
6879
6880 /**
6881  * @class Roo.bootstrap.TableRow
6882  * @extends Roo.bootstrap.Component
6883  * Bootstrap TableRow class
6884  * @cfg {String} cls row class
6885  * @cfg {String} align Aligns the content in a table row
6886  * @cfg {String} bgcolor Specifies a background color for a table row
6887  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6888  * @cfg {String} valign Vertical aligns the content in a table row
6889  * 
6890  * @constructor
6891  * Create a new TableRow
6892  * @param {Object} config The config object
6893  */
6894
6895 Roo.bootstrap.TableRow = function(config){
6896     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6897 };
6898
6899 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6900     
6901     cls: false,
6902     align: false,
6903     bgcolor: false,
6904     charoff: false,
6905     valign: false,
6906     
6907     getAutoCreate : function(){
6908         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6909         
6910         cfg = {
6911             tag: 'tr'
6912         };
6913             
6914         if(this.cls){
6915             cfg.cls = this.cls;
6916         }
6917         if(this.align){
6918             cfg.align = this.align;
6919         }
6920         if(this.bgcolor){
6921             cfg.bgcolor = this.bgcolor;
6922         }
6923         if(this.charoff){
6924             cfg.charoff = this.charoff;
6925         }
6926         if(this.valign){
6927             cfg.valign = this.valign;
6928         }
6929         
6930         return cfg;
6931     }
6932    
6933 });
6934
6935  
6936
6937  /*
6938  * - LGPL
6939  *
6940  * table body
6941  * 
6942  */
6943
6944 /**
6945  * @class Roo.bootstrap.TableBody
6946  * @extends Roo.bootstrap.Component
6947  * Bootstrap TableBody class
6948  * @cfg {String} cls element class
6949  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6950  * @cfg {String} align Aligns the content inside the element
6951  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6952  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6953  * 
6954  * @constructor
6955  * Create a new TableBody
6956  * @param {Object} config The config object
6957  */
6958
6959 Roo.bootstrap.TableBody = function(config){
6960     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6961 };
6962
6963 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6964     
6965     cls: false,
6966     tag: false,
6967     align: false,
6968     charoff: false,
6969     valign: false,
6970     
6971     getAutoCreate : function(){
6972         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6973         
6974         cfg = {
6975             tag: 'tbody'
6976         };
6977             
6978         if (this.cls) {
6979             cfg.cls=this.cls
6980         }
6981         if(this.tag){
6982             cfg.tag = this.tag;
6983         }
6984         
6985         if(this.align){
6986             cfg.align = this.align;
6987         }
6988         if(this.charoff){
6989             cfg.charoff = this.charoff;
6990         }
6991         if(this.valign){
6992             cfg.valign = this.valign;
6993         }
6994         
6995         return cfg;
6996     }
6997     
6998     
6999 //    initEvents : function()
7000 //    {
7001 //        
7002 //        if(!this.store){
7003 //            return;
7004 //        }
7005 //        
7006 //        this.store = Roo.factory(this.store, Roo.data);
7007 //        this.store.on('load', this.onLoad, this);
7008 //        
7009 //        this.store.load();
7010 //        
7011 //    },
7012 //    
7013 //    onLoad: function () 
7014 //    {   
7015 //        this.fireEvent('load', this);
7016 //    }
7017 //    
7018 //   
7019 });
7020
7021  
7022
7023  /*
7024  * Based on:
7025  * Ext JS Library 1.1.1
7026  * Copyright(c) 2006-2007, Ext JS, LLC.
7027  *
7028  * Originally Released Under LGPL - original licence link has changed is not relivant.
7029  *
7030  * Fork - LGPL
7031  * <script type="text/javascript">
7032  */
7033
7034 // as we use this in bootstrap.
7035 Roo.namespace('Roo.form');
7036  /**
7037  * @class Roo.form.Action
7038  * Internal Class used to handle form actions
7039  * @constructor
7040  * @param {Roo.form.BasicForm} el The form element or its id
7041  * @param {Object} config Configuration options
7042  */
7043
7044  
7045  
7046 // define the action interface
7047 Roo.form.Action = function(form, options){
7048     this.form = form;
7049     this.options = options || {};
7050 };
7051 /**
7052  * Client Validation Failed
7053  * @const 
7054  */
7055 Roo.form.Action.CLIENT_INVALID = 'client';
7056 /**
7057  * Server Validation Failed
7058  * @const 
7059  */
7060 Roo.form.Action.SERVER_INVALID = 'server';
7061  /**
7062  * Connect to Server Failed
7063  * @const 
7064  */
7065 Roo.form.Action.CONNECT_FAILURE = 'connect';
7066 /**
7067  * Reading Data from Server Failed
7068  * @const 
7069  */
7070 Roo.form.Action.LOAD_FAILURE = 'load';
7071
7072 Roo.form.Action.prototype = {
7073     type : 'default',
7074     failureType : undefined,
7075     response : undefined,
7076     result : undefined,
7077
7078     // interface method
7079     run : function(options){
7080
7081     },
7082
7083     // interface method
7084     success : function(response){
7085
7086     },
7087
7088     // interface method
7089     handleResponse : function(response){
7090
7091     },
7092
7093     // default connection failure
7094     failure : function(response){
7095         
7096         this.response = response;
7097         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7098         this.form.afterAction(this, false);
7099     },
7100
7101     processResponse : function(response){
7102         this.response = response;
7103         if(!response.responseText){
7104             return true;
7105         }
7106         this.result = this.handleResponse(response);
7107         return this.result;
7108     },
7109
7110     // utility functions used internally
7111     getUrl : function(appendParams){
7112         var url = this.options.url || this.form.url || this.form.el.dom.action;
7113         if(appendParams){
7114             var p = this.getParams();
7115             if(p){
7116                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7117             }
7118         }
7119         return url;
7120     },
7121
7122     getMethod : function(){
7123         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7124     },
7125
7126     getParams : function(){
7127         var bp = this.form.baseParams;
7128         var p = this.options.params;
7129         if(p){
7130             if(typeof p == "object"){
7131                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7132             }else if(typeof p == 'string' && bp){
7133                 p += '&' + Roo.urlEncode(bp);
7134             }
7135         }else if(bp){
7136             p = Roo.urlEncode(bp);
7137         }
7138         return p;
7139     },
7140
7141     createCallback : function(){
7142         return {
7143             success: this.success,
7144             failure: this.failure,
7145             scope: this,
7146             timeout: (this.form.timeout*1000),
7147             upload: this.form.fileUpload ? this.success : undefined
7148         };
7149     }
7150 };
7151
7152 Roo.form.Action.Submit = function(form, options){
7153     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7154 };
7155
7156 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7157     type : 'submit',
7158
7159     haveProgress : false,
7160     uploadComplete : false,
7161     
7162     // uploadProgress indicator.
7163     uploadProgress : function()
7164     {
7165         if (!this.form.progressUrl) {
7166             return;
7167         }
7168         
7169         if (!this.haveProgress) {
7170             Roo.MessageBox.progress("Uploading", "Uploading");
7171         }
7172         if (this.uploadComplete) {
7173            Roo.MessageBox.hide();
7174            return;
7175         }
7176         
7177         this.haveProgress = true;
7178    
7179         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7180         
7181         var c = new Roo.data.Connection();
7182         c.request({
7183             url : this.form.progressUrl,
7184             params: {
7185                 id : uid
7186             },
7187             method: 'GET',
7188             success : function(req){
7189                //console.log(data);
7190                 var rdata = false;
7191                 var edata;
7192                 try  {
7193                    rdata = Roo.decode(req.responseText)
7194                 } catch (e) {
7195                     Roo.log("Invalid data from server..");
7196                     Roo.log(edata);
7197                     return;
7198                 }
7199                 if (!rdata || !rdata.success) {
7200                     Roo.log(rdata);
7201                     Roo.MessageBox.alert(Roo.encode(rdata));
7202                     return;
7203                 }
7204                 var data = rdata.data;
7205                 
7206                 if (this.uploadComplete) {
7207                    Roo.MessageBox.hide();
7208                    return;
7209                 }
7210                    
7211                 if (data){
7212                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7213                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7214                     );
7215                 }
7216                 this.uploadProgress.defer(2000,this);
7217             },
7218        
7219             failure: function(data) {
7220                 Roo.log('progress url failed ');
7221                 Roo.log(data);
7222             },
7223             scope : this
7224         });
7225            
7226     },
7227     
7228     
7229     run : function()
7230     {
7231         // run get Values on the form, so it syncs any secondary forms.
7232         this.form.getValues();
7233         
7234         var o = this.options;
7235         var method = this.getMethod();
7236         var isPost = method == 'POST';
7237         if(o.clientValidation === false || this.form.isValid()){
7238             
7239             if (this.form.progressUrl) {
7240                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7241                     (new Date() * 1) + '' + Math.random());
7242                     
7243             } 
7244             
7245             
7246             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7247                 form:this.form.el.dom,
7248                 url:this.getUrl(!isPost),
7249                 method: method,
7250                 params:isPost ? this.getParams() : null,
7251                 isUpload: this.form.fileUpload
7252             }));
7253             
7254             this.uploadProgress();
7255
7256         }else if (o.clientValidation !== false){ // client validation failed
7257             this.failureType = Roo.form.Action.CLIENT_INVALID;
7258             this.form.afterAction(this, false);
7259         }
7260     },
7261
7262     success : function(response)
7263     {
7264         this.uploadComplete= true;
7265         if (this.haveProgress) {
7266             Roo.MessageBox.hide();
7267         }
7268         
7269         
7270         var result = this.processResponse(response);
7271         if(result === true || result.success){
7272             this.form.afterAction(this, true);
7273             return;
7274         }
7275         if(result.errors){
7276             this.form.markInvalid(result.errors);
7277             this.failureType = Roo.form.Action.SERVER_INVALID;
7278         }
7279         this.form.afterAction(this, false);
7280     },
7281     failure : function(response)
7282     {
7283         this.uploadComplete= true;
7284         if (this.haveProgress) {
7285             Roo.MessageBox.hide();
7286         }
7287         
7288         this.response = response;
7289         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7290         this.form.afterAction(this, false);
7291     },
7292     
7293     handleResponse : function(response){
7294         if(this.form.errorReader){
7295             var rs = this.form.errorReader.read(response);
7296             var errors = [];
7297             if(rs.records){
7298                 for(var i = 0, len = rs.records.length; i < len; i++) {
7299                     var r = rs.records[i];
7300                     errors[i] = r.data;
7301                 }
7302             }
7303             if(errors.length < 1){
7304                 errors = null;
7305             }
7306             return {
7307                 success : rs.success,
7308                 errors : errors
7309             };
7310         }
7311         var ret = false;
7312         try {
7313             ret = Roo.decode(response.responseText);
7314         } catch (e) {
7315             ret = {
7316                 success: false,
7317                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7318                 errors : []
7319             };
7320         }
7321         return ret;
7322         
7323     }
7324 });
7325
7326
7327 Roo.form.Action.Load = function(form, options){
7328     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7329     this.reader = this.form.reader;
7330 };
7331
7332 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7333     type : 'load',
7334
7335     run : function(){
7336         
7337         Roo.Ajax.request(Roo.apply(
7338                 this.createCallback(), {
7339                     method:this.getMethod(),
7340                     url:this.getUrl(false),
7341                     params:this.getParams()
7342         }));
7343     },
7344
7345     success : function(response){
7346         
7347         var result = this.processResponse(response);
7348         if(result === true || !result.success || !result.data){
7349             this.failureType = Roo.form.Action.LOAD_FAILURE;
7350             this.form.afterAction(this, false);
7351             return;
7352         }
7353         this.form.clearInvalid();
7354         this.form.setValues(result.data);
7355         this.form.afterAction(this, true);
7356     },
7357
7358     handleResponse : function(response){
7359         if(this.form.reader){
7360             var rs = this.form.reader.read(response);
7361             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7362             return {
7363                 success : rs.success,
7364                 data : data
7365             };
7366         }
7367         return Roo.decode(response.responseText);
7368     }
7369 });
7370
7371 Roo.form.Action.ACTION_TYPES = {
7372     'load' : Roo.form.Action.Load,
7373     'submit' : Roo.form.Action.Submit
7374 };/*
7375  * - LGPL
7376  *
7377  * form
7378  * 
7379  */
7380
7381 /**
7382  * @class Roo.bootstrap.Form
7383  * @extends Roo.bootstrap.Component
7384  * Bootstrap Form class
7385  * @cfg {String} method  GET | POST (default POST)
7386  * @cfg {String} labelAlign top | left (default top)
7387  * @cfg {String} align left  | right - for navbars
7388  * @cfg {Boolean} loadMask load mask when submit (default true)
7389
7390  * 
7391  * @constructor
7392  * Create a new Form
7393  * @param {Object} config The config object
7394  */
7395
7396
7397 Roo.bootstrap.Form = function(config){
7398     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7399     this.addEvents({
7400         /**
7401          * @event clientvalidation
7402          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7403          * @param {Form} this
7404          * @param {Boolean} valid true if the form has passed client-side validation
7405          */
7406         clientvalidation: true,
7407         /**
7408          * @event beforeaction
7409          * Fires before any action is performed. Return false to cancel the action.
7410          * @param {Form} this
7411          * @param {Action} action The action to be performed
7412          */
7413         beforeaction: true,
7414         /**
7415          * @event actionfailed
7416          * Fires when an action fails.
7417          * @param {Form} this
7418          * @param {Action} action The action that failed
7419          */
7420         actionfailed : true,
7421         /**
7422          * @event actioncomplete
7423          * Fires when an action is completed.
7424          * @param {Form} this
7425          * @param {Action} action The action that completed
7426          */
7427         actioncomplete : true
7428     });
7429     
7430 };
7431
7432 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7433       
7434      /**
7435      * @cfg {String} method
7436      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7437      */
7438     method : 'POST',
7439     /**
7440      * @cfg {String} url
7441      * The URL to use for form actions if one isn't supplied in the action options.
7442      */
7443     /**
7444      * @cfg {Boolean} fileUpload
7445      * Set to true if this form is a file upload.
7446      */
7447      
7448     /**
7449      * @cfg {Object} baseParams
7450      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7451      */
7452       
7453     /**
7454      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7455      */
7456     timeout: 30,
7457     /**
7458      * @cfg {Sting} align (left|right) for navbar forms
7459      */
7460     align : 'left',
7461
7462     // private
7463     activeAction : null,
7464  
7465     /**
7466      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7467      * element by passing it or its id or mask the form itself by passing in true.
7468      * @type Mixed
7469      */
7470     waitMsgTarget : false,
7471     
7472     loadMask : true,
7473     
7474     getAutoCreate : function(){
7475         
7476         var cfg = {
7477             tag: 'form',
7478             method : this.method || 'POST',
7479             id : this.id || Roo.id(),
7480             cls : ''
7481         };
7482         if (this.parent().xtype.match(/^Nav/)) {
7483             cfg.cls = 'navbar-form navbar-' + this.align;
7484             
7485         }
7486         
7487         if (this.labelAlign == 'left' ) {
7488             cfg.cls += ' form-horizontal';
7489         }
7490         
7491         
7492         return cfg;
7493     },
7494     initEvents : function()
7495     {
7496         this.el.on('submit', this.onSubmit, this);
7497         // this was added as random key presses on the form where triggering form submit.
7498         this.el.on('keypress', function(e) {
7499             if (e.getCharCode() != 13) {
7500                 return true;
7501             }
7502             // we might need to allow it for textareas.. and some other items.
7503             // check e.getTarget().
7504             
7505             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7506                 return true;
7507             }
7508         
7509             Roo.log("keypress blocked");
7510             
7511             e.preventDefault();
7512             return false;
7513         });
7514         
7515     },
7516     // private
7517     onSubmit : function(e){
7518         e.stopEvent();
7519     },
7520     
7521      /**
7522      * Returns true if client-side validation on the form is successful.
7523      * @return Boolean
7524      */
7525     isValid : function(){
7526         var items = this.getItems();
7527         var valid = true;
7528         items.each(function(f){
7529            if(!f.validate()){
7530                valid = false;
7531                
7532            }
7533         });
7534         return valid;
7535     },
7536     /**
7537      * Returns true if any fields in this form have changed since their original load.
7538      * @return Boolean
7539      */
7540     isDirty : function(){
7541         var dirty = false;
7542         var items = this.getItems();
7543         items.each(function(f){
7544            if(f.isDirty()){
7545                dirty = true;
7546                return false;
7547            }
7548            return true;
7549         });
7550         return dirty;
7551     },
7552      /**
7553      * Performs a predefined action (submit or load) or custom actions you define on this form.
7554      * @param {String} actionName The name of the action type
7555      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7556      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7557      * accept other config options):
7558      * <pre>
7559 Property          Type             Description
7560 ----------------  ---------------  ----------------------------------------------------------------------------------
7561 url               String           The url for the action (defaults to the form's url)
7562 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7563 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7564 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7565                                    validate the form on the client (defaults to false)
7566      * </pre>
7567      * @return {BasicForm} this
7568      */
7569     doAction : function(action, options){
7570         if(typeof action == 'string'){
7571             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7572         }
7573         if(this.fireEvent('beforeaction', this, action) !== false){
7574             this.beforeAction(action);
7575             action.run.defer(100, action);
7576         }
7577         return this;
7578     },
7579     
7580     // private
7581     beforeAction : function(action){
7582         var o = action.options;
7583         
7584         if(this.loadMask){
7585             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7586         }
7587         // not really supported yet.. ??
7588         
7589         //if(this.waitMsgTarget === true){
7590         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7591         //}else if(this.waitMsgTarget){
7592         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7593         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7594         //}else {
7595         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7596        // }
7597          
7598     },
7599
7600     // private
7601     afterAction : function(action, success){
7602         this.activeAction = null;
7603         var o = action.options;
7604         
7605         //if(this.waitMsgTarget === true){
7606             this.el.unmask();
7607         //}else if(this.waitMsgTarget){
7608         //    this.waitMsgTarget.unmask();
7609         //}else{
7610         //    Roo.MessageBox.updateProgress(1);
7611         //    Roo.MessageBox.hide();
7612        // }
7613         // 
7614         if(success){
7615             if(o.reset){
7616                 this.reset();
7617             }
7618             Roo.callback(o.success, o.scope, [this, action]);
7619             this.fireEvent('actioncomplete', this, action);
7620             
7621         }else{
7622             
7623             // failure condition..
7624             // we have a scenario where updates need confirming.
7625             // eg. if a locking scenario exists..
7626             // we look for { errors : { needs_confirm : true }} in the response.
7627             if (
7628                 (typeof(action.result) != 'undefined')  &&
7629                 (typeof(action.result.errors) != 'undefined')  &&
7630                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7631            ){
7632                 var _t = this;
7633                 Roo.log("not supported yet");
7634                  /*
7635                 
7636                 Roo.MessageBox.confirm(
7637                     "Change requires confirmation",
7638                     action.result.errorMsg,
7639                     function(r) {
7640                         if (r != 'yes') {
7641                             return;
7642                         }
7643                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7644                     }
7645                     
7646                 );
7647                 */
7648                 
7649                 
7650                 return;
7651             }
7652             
7653             Roo.callback(o.failure, o.scope, [this, action]);
7654             // show an error message if no failed handler is set..
7655             if (!this.hasListener('actionfailed')) {
7656                 Roo.log("need to add dialog support");
7657                 /*
7658                 Roo.MessageBox.alert("Error",
7659                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7660                         action.result.errorMsg :
7661                         "Saving Failed, please check your entries or try again"
7662                 );
7663                 */
7664             }
7665             
7666             this.fireEvent('actionfailed', this, action);
7667         }
7668         
7669     },
7670     /**
7671      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7672      * @param {String} id The value to search for
7673      * @return Field
7674      */
7675     findField : function(id){
7676         var items = this.getItems();
7677         var field = items.get(id);
7678         if(!field){
7679              items.each(function(f){
7680                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7681                     field = f;
7682                     return false;
7683                 }
7684                 return true;
7685             });
7686         }
7687         return field || null;
7688     },
7689      /**
7690      * Mark fields in this form invalid in bulk.
7691      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7692      * @return {BasicForm} this
7693      */
7694     markInvalid : function(errors){
7695         if(errors instanceof Array){
7696             for(var i = 0, len = errors.length; i < len; i++){
7697                 var fieldError = errors[i];
7698                 var f = this.findField(fieldError.id);
7699                 if(f){
7700                     f.markInvalid(fieldError.msg);
7701                 }
7702             }
7703         }else{
7704             var field, id;
7705             for(id in errors){
7706                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7707                     field.markInvalid(errors[id]);
7708                 }
7709             }
7710         }
7711         //Roo.each(this.childForms || [], function (f) {
7712         //    f.markInvalid(errors);
7713         //});
7714         
7715         return this;
7716     },
7717
7718     /**
7719      * Set values for fields in this form in bulk.
7720      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7721      * @return {BasicForm} this
7722      */
7723     setValues : function(values){
7724         if(values instanceof Array){ // array of objects
7725             for(var i = 0, len = values.length; i < len; i++){
7726                 var v = values[i];
7727                 var f = this.findField(v.id);
7728                 if(f){
7729                     f.setValue(v.value);
7730                     if(this.trackResetOnLoad){
7731                         f.originalValue = f.getValue();
7732                     }
7733                 }
7734             }
7735         }else{ // object hash
7736             var field, id;
7737             for(id in values){
7738                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7739                     
7740                     if (field.setFromData && 
7741                         field.valueField && 
7742                         field.displayField &&
7743                         // combos' with local stores can 
7744                         // be queried via setValue()
7745                         // to set their value..
7746                         (field.store && !field.store.isLocal)
7747                         ) {
7748                         // it's a combo
7749                         var sd = { };
7750                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7751                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7752                         field.setFromData(sd);
7753                         
7754                     } else {
7755                         field.setValue(values[id]);
7756                     }
7757                     
7758                     
7759                     if(this.trackResetOnLoad){
7760                         field.originalValue = field.getValue();
7761                     }
7762                 }
7763             }
7764         }
7765          
7766         //Roo.each(this.childForms || [], function (f) {
7767         //    f.setValues(values);
7768         //});
7769                 
7770         return this;
7771     },
7772
7773     /**
7774      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7775      * they are returned as an array.
7776      * @param {Boolean} asString
7777      * @return {Object}
7778      */
7779     getValues : function(asString){
7780         //if (this.childForms) {
7781             // copy values from the child forms
7782         //    Roo.each(this.childForms, function (f) {
7783         //        this.setValues(f.getValues());
7784         //    }, this);
7785         //}
7786         
7787         
7788         
7789         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7790         if(asString === true){
7791             return fs;
7792         }
7793         return Roo.urlDecode(fs);
7794     },
7795     
7796     /**
7797      * Returns the fields in this form as an object with key/value pairs. 
7798      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7799      * @return {Object}
7800      */
7801     getFieldValues : function(with_hidden)
7802     {
7803         var items = this.getItems();
7804         var ret = {};
7805         items.each(function(f){
7806             if (!f.getName()) {
7807                 return;
7808             }
7809             var v = f.getValue();
7810             if (f.inputType =='radio') {
7811                 if (typeof(ret[f.getName()]) == 'undefined') {
7812                     ret[f.getName()] = ''; // empty..
7813                 }
7814                 
7815                 if (!f.el.dom.checked) {
7816                     return;
7817                     
7818                 }
7819                 v = f.el.dom.value;
7820                 
7821             }
7822             
7823             // not sure if this supported any more..
7824             if ((typeof(v) == 'object') && f.getRawValue) {
7825                 v = f.getRawValue() ; // dates..
7826             }
7827             // combo boxes where name != hiddenName...
7828             if (f.name != f.getName()) {
7829                 ret[f.name] = f.getRawValue();
7830             }
7831             ret[f.getName()] = v;
7832         });
7833         
7834         return ret;
7835     },
7836
7837     /**
7838      * Clears all invalid messages in this form.
7839      * @return {BasicForm} this
7840      */
7841     clearInvalid : function(){
7842         var items = this.getItems();
7843         
7844         items.each(function(f){
7845            f.clearInvalid();
7846         });
7847         
7848         
7849         
7850         return this;
7851     },
7852
7853     /**
7854      * Resets this form.
7855      * @return {BasicForm} this
7856      */
7857     reset : function(){
7858         var items = this.getItems();
7859         items.each(function(f){
7860             f.reset();
7861         });
7862         
7863         Roo.each(this.childForms || [], function (f) {
7864             f.reset();
7865         });
7866        
7867         
7868         return this;
7869     },
7870     getItems : function()
7871     {
7872         var r=new Roo.util.MixedCollection(false, function(o){
7873             return o.id || (o.id = Roo.id());
7874         });
7875         var iter = function(el) {
7876             if (el.inputEl) {
7877                 r.add(el);
7878             }
7879             if (!el.items) {
7880                 return;
7881             }
7882             Roo.each(el.items,function(e) {
7883                 iter(e);
7884             });
7885             
7886             
7887         };
7888         
7889         iter(this);
7890         return r;
7891         
7892         
7893         
7894         
7895     }
7896     
7897 });
7898
7899  
7900 /*
7901  * Based on:
7902  * Ext JS Library 1.1.1
7903  * Copyright(c) 2006-2007, Ext JS, LLC.
7904  *
7905  * Originally Released Under LGPL - original licence link has changed is not relivant.
7906  *
7907  * Fork - LGPL
7908  * <script type="text/javascript">
7909  */
7910 /**
7911  * @class Roo.form.VTypes
7912  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7913  * @singleton
7914  */
7915 Roo.form.VTypes = function(){
7916     // closure these in so they are only created once.
7917     var alpha = /^[a-zA-Z_]+$/;
7918     var alphanum = /^[a-zA-Z0-9_]+$/;
7919     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7920     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7921
7922     // All these messages and functions are configurable
7923     return {
7924         /**
7925          * The function used to validate email addresses
7926          * @param {String} value The email address
7927          */
7928         'email' : function(v){
7929             return email.test(v);
7930         },
7931         /**
7932          * The error text to display when the email validation function returns false
7933          * @type String
7934          */
7935         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7936         /**
7937          * The keystroke filter mask to be applied on email input
7938          * @type RegExp
7939          */
7940         'emailMask' : /[a-z0-9_\.\-@]/i,
7941
7942         /**
7943          * The function used to validate URLs
7944          * @param {String} value The URL
7945          */
7946         'url' : function(v){
7947             return url.test(v);
7948         },
7949         /**
7950          * The error text to display when the url validation function returns false
7951          * @type String
7952          */
7953         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7954         
7955         /**
7956          * The function used to validate alpha values
7957          * @param {String} value The value
7958          */
7959         'alpha' : function(v){
7960             return alpha.test(v);
7961         },
7962         /**
7963          * The error text to display when the alpha validation function returns false
7964          * @type String
7965          */
7966         'alphaText' : 'This field should only contain letters and _',
7967         /**
7968          * The keystroke filter mask to be applied on alpha input
7969          * @type RegExp
7970          */
7971         'alphaMask' : /[a-z_]/i,
7972
7973         /**
7974          * The function used to validate alphanumeric values
7975          * @param {String} value The value
7976          */
7977         'alphanum' : function(v){
7978             return alphanum.test(v);
7979         },
7980         /**
7981          * The error text to display when the alphanumeric validation function returns false
7982          * @type String
7983          */
7984         'alphanumText' : 'This field should only contain letters, numbers and _',
7985         /**
7986          * The keystroke filter mask to be applied on alphanumeric input
7987          * @type RegExp
7988          */
7989         'alphanumMask' : /[a-z0-9_]/i
7990     };
7991 }();/*
7992  * - LGPL
7993  *
7994  * Input
7995  * 
7996  */
7997
7998 /**
7999  * @class Roo.bootstrap.Input
8000  * @extends Roo.bootstrap.Component
8001  * Bootstrap Input class
8002  * @cfg {Boolean} disabled is it disabled
8003  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8004  * @cfg {String} name name of the input
8005  * @cfg {string} fieldLabel - the label associated
8006  * @cfg {string} placeholder - placeholder to put in text.
8007  * @cfg {string}  before - input group add on before
8008  * @cfg {string} after - input group add on after
8009  * @cfg {string} size - (lg|sm) or leave empty..
8010  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8011  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8012  * @cfg {Number} md colspan out of 12 for computer-sized screens
8013  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8014  * @cfg {string} value default value of the input
8015  * @cfg {Number} labelWidth set the width of label (0-12)
8016  * @cfg {String} labelAlign (top|left)
8017  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8018  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8019  * @cfg {String} indicatorpos (left|right) default left
8020
8021  * @cfg {String} align (left|center|right) Default left
8022  * @cfg {Boolean} forceFeedback (true|false) Default false
8023  * 
8024  * 
8025  * 
8026  * 
8027  * @constructor
8028  * Create a new Input
8029  * @param {Object} config The config object
8030  */
8031
8032 Roo.bootstrap.Input = function(config){
8033     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8034    
8035         this.addEvents({
8036             /**
8037              * @event focus
8038              * Fires when this field receives input focus.
8039              * @param {Roo.form.Field} this
8040              */
8041             focus : true,
8042             /**
8043              * @event blur
8044              * Fires when this field loses input focus.
8045              * @param {Roo.form.Field} this
8046              */
8047             blur : true,
8048             /**
8049              * @event specialkey
8050              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8051              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8052              * @param {Roo.form.Field} this
8053              * @param {Roo.EventObject} e The event object
8054              */
8055             specialkey : true,
8056             /**
8057              * @event change
8058              * Fires just before the field blurs if the field value has changed.
8059              * @param {Roo.form.Field} this
8060              * @param {Mixed} newValue The new value
8061              * @param {Mixed} oldValue The original value
8062              */
8063             change : true,
8064             /**
8065              * @event invalid
8066              * Fires after the field has been marked as invalid.
8067              * @param {Roo.form.Field} this
8068              * @param {String} msg The validation message
8069              */
8070             invalid : true,
8071             /**
8072              * @event valid
8073              * Fires after the field has been validated with no errors.
8074              * @param {Roo.form.Field} this
8075              */
8076             valid : true,
8077              /**
8078              * @event keyup
8079              * Fires after the key up
8080              * @param {Roo.form.Field} this
8081              * @param {Roo.EventObject}  e The event Object
8082              */
8083             keyup : true
8084         });
8085 };
8086
8087 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8088      /**
8089      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8090       automatic validation (defaults to "keyup").
8091      */
8092     validationEvent : "keyup",
8093      /**
8094      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8095      */
8096     validateOnBlur : true,
8097     /**
8098      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8099      */
8100     validationDelay : 250,
8101      /**
8102      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8103      */
8104     focusClass : "x-form-focus",  // not needed???
8105     
8106        
8107     /**
8108      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8109      */
8110     invalidClass : "has-warning",
8111     
8112     /**
8113      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8114      */
8115     validClass : "has-success",
8116     
8117     /**
8118      * @cfg {Boolean} hasFeedback (true|false) default true
8119      */
8120     hasFeedback : true,
8121     
8122     /**
8123      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8124      */
8125     invalidFeedbackClass : "glyphicon-warning-sign",
8126     
8127     /**
8128      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8129      */
8130     validFeedbackClass : "glyphicon-ok",
8131     
8132     /**
8133      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8134      */
8135     selectOnFocus : false,
8136     
8137      /**
8138      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8139      */
8140     maskRe : null,
8141        /**
8142      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8143      */
8144     vtype : null,
8145     
8146       /**
8147      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8148      */
8149     disableKeyFilter : false,
8150     
8151        /**
8152      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8153      */
8154     disabled : false,
8155      /**
8156      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8157      */
8158     allowBlank : true,
8159     /**
8160      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8161      */
8162     blankText : "This field is required",
8163     
8164      /**
8165      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8166      */
8167     minLength : 0,
8168     /**
8169      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8170      */
8171     maxLength : Number.MAX_VALUE,
8172     /**
8173      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8174      */
8175     minLengthText : "The minimum length for this field is {0}",
8176     /**
8177      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8178      */
8179     maxLengthText : "The maximum length for this field is {0}",
8180   
8181     
8182     /**
8183      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8184      * If available, this function will be called only after the basic validators all return true, and will be passed the
8185      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8186      */
8187     validator : null,
8188     /**
8189      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8190      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8191      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8192      */
8193     regex : null,
8194     /**
8195      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8196      */
8197     regexText : "",
8198     
8199     autocomplete: false,
8200     
8201     
8202     fieldLabel : '',
8203     inputType : 'text',
8204     
8205     name : false,
8206     placeholder: false,
8207     before : false,
8208     after : false,
8209     size : false,
8210     hasFocus : false,
8211     preventMark: false,
8212     isFormField : true,
8213     value : '',
8214     labelWidth : 2,
8215     labelAlign : false,
8216     readOnly : false,
8217     align : false,
8218     formatedValue : false,
8219     forceFeedback : false,
8220     
8221     indicatorpos : 'left',
8222     
8223     parentLabelAlign : function()
8224     {
8225         var parent = this;
8226         while (parent.parent()) {
8227             parent = parent.parent();
8228             if (typeof(parent.labelAlign) !='undefined') {
8229                 return parent.labelAlign;
8230             }
8231         }
8232         return 'left';
8233         
8234     },
8235     
8236     getAutoCreate : function()
8237     {
8238         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8239         
8240         var id = Roo.id();
8241         
8242         var cfg = {};
8243         
8244         if(this.inputType != 'hidden'){
8245             cfg.cls = 'form-group' //input-group
8246         }
8247         
8248         var input =  {
8249             tag: 'input',
8250             id : id,
8251             type : this.inputType,
8252             value : this.value,
8253             cls : 'form-control',
8254             placeholder : this.placeholder || '',
8255             autocomplete : this.autocomplete || 'new-password'
8256         };
8257         
8258         if(this.align){
8259             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8260         }
8261         
8262         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8263             input.maxLength = this.maxLength;
8264         }
8265         
8266         if (this.disabled) {
8267             input.disabled=true;
8268         }
8269         
8270         if (this.readOnly) {
8271             input.readonly=true;
8272         }
8273         
8274         if (this.name) {
8275             input.name = this.name;
8276         }
8277         
8278         if (this.size) {
8279             input.cls += ' input-' + this.size;
8280         }
8281         
8282         var settings=this;
8283         ['xs','sm','md','lg'].map(function(size){
8284             if (settings[size]) {
8285                 cfg.cls += ' col-' + size + '-' + settings[size];
8286             }
8287         });
8288         
8289         var inputblock = input;
8290         
8291         var feedback = {
8292             tag: 'span',
8293             cls: 'glyphicon form-control-feedback'
8294         };
8295             
8296         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8297             
8298             inputblock = {
8299                 cls : 'has-feedback',
8300                 cn :  [
8301                     input,
8302                     feedback
8303                 ] 
8304             };  
8305         }
8306         
8307         if (this.before || this.after) {
8308             
8309             inputblock = {
8310                 cls : 'input-group',
8311                 cn :  [] 
8312             };
8313             
8314             if (this.before && typeof(this.before) == 'string') {
8315                 
8316                 inputblock.cn.push({
8317                     tag :'span',
8318                     cls : 'roo-input-before input-group-addon',
8319                     html : this.before
8320                 });
8321             }
8322             if (this.before && typeof(this.before) == 'object') {
8323                 this.before = Roo.factory(this.before);
8324                 
8325                 inputblock.cn.push({
8326                     tag :'span',
8327                     cls : 'roo-input-before input-group-' +
8328                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8329                 });
8330             }
8331             
8332             inputblock.cn.push(input);
8333             
8334             if (this.after && typeof(this.after) == 'string') {
8335                 inputblock.cn.push({
8336                     tag :'span',
8337                     cls : 'roo-input-after input-group-addon',
8338                     html : this.after
8339                 });
8340             }
8341             if (this.after && typeof(this.after) == 'object') {
8342                 this.after = Roo.factory(this.after);
8343                 
8344                 inputblock.cn.push({
8345                     tag :'span',
8346                     cls : 'roo-input-after input-group-' +
8347                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8348                 });
8349             }
8350             
8351             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8352                 inputblock.cls += ' has-feedback';
8353                 inputblock.cn.push(feedback);
8354             }
8355         };
8356         
8357         if (align ==='left' && this.fieldLabel.length) {
8358             
8359             cfg.cn = [
8360                 {
8361                     tag : 'i',
8362                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8363                     tooltip : 'This field is required'
8364                 },
8365                 {
8366                     tag: 'label',
8367                     'for' :  id,
8368                     cls : 'control-label col-sm-' + this.labelWidth,
8369                     html : this.fieldLabel
8370
8371                 },
8372                 {
8373                     cls : "col-sm-" + (12 - this.labelWidth), 
8374                     cn: [
8375                         inputblock
8376                     ]
8377                 }
8378
8379             ];
8380             
8381             if(this.indicatorpos == 'right'){
8382                 cfg.cn = [
8383                     {
8384                         tag: 'label',
8385                         'for' :  id,
8386                         cls : 'control-label col-sm-' + this.labelWidth,
8387                         html : this.fieldLabel
8388
8389                     },
8390                     {
8391                         tag : 'i',
8392                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8393                         tooltip : 'This field is required'
8394                     },
8395                     {
8396                         cls : "col-sm-" + (12 - this.labelWidth), 
8397                         cn: [
8398                             inputblock
8399                         ]
8400                     }
8401
8402                 ];
8403             }
8404             
8405         } else if ( this.fieldLabel.length) {
8406                 
8407             cfg.cn = [
8408                 {
8409                     tag : 'i',
8410                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8411                     tooltip : 'This field is required'
8412                 },
8413                 {
8414                     tag: 'label',
8415                    //cls : 'input-group-addon',
8416                     html : this.fieldLabel
8417
8418                 },
8419
8420                inputblock
8421
8422            ];
8423            
8424            if(this.indicatorpos == 'right'){
8425                 
8426                 cfg.cn = [
8427                     {
8428                         tag: 'label',
8429                        //cls : 'input-group-addon',
8430                         html : this.fieldLabel
8431
8432                     },
8433                     {
8434                         tag : 'i',
8435                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8436                         tooltip : 'This field is required'
8437                     },
8438
8439                    inputblock
8440
8441                ];
8442
8443             }
8444
8445         } else {
8446             
8447             cfg.cn = [
8448
8449                     inputblock
8450
8451             ];
8452                 
8453                 
8454         };
8455         
8456         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8457            cfg.cls += ' navbar-form';
8458         }
8459         
8460         if (this.parentType === 'NavGroup') {
8461            cfg.cls += ' navbar-form';
8462            cfg.tag = 'li';
8463         }
8464         
8465         return cfg;
8466         
8467     },
8468     /**
8469      * return the real input element.
8470      */
8471     inputEl: function ()
8472     {
8473         return this.el.select('input.form-control',true).first();
8474     },
8475     
8476     tooltipEl : function()
8477     {
8478         return this.inputEl();
8479     },
8480     
8481     indicatorEl : function()
8482     {
8483         var indicator = this.el.select('i.roo-required-indicator',true).first();
8484         
8485         if(!indicator){
8486             return false;
8487         }
8488         
8489         return indicator;
8490         
8491     },
8492     
8493     setDisabled : function(v)
8494     {
8495         var i  = this.inputEl().dom;
8496         if (!v) {
8497             i.removeAttribute('disabled');
8498             return;
8499             
8500         }
8501         i.setAttribute('disabled','true');
8502     },
8503     initEvents : function()
8504     {
8505           
8506         this.inputEl().on("keydown" , this.fireKey,  this);
8507         this.inputEl().on("focus", this.onFocus,  this);
8508         this.inputEl().on("blur", this.onBlur,  this);
8509         
8510         this.inputEl().relayEvent('keyup', this);
8511         
8512         this.indicator = this.indicatorEl();
8513         
8514         if(this.indicator){
8515             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8516             this.indicator.hide();
8517         }
8518  
8519         // reference to original value for reset
8520         this.originalValue = this.getValue();
8521         //Roo.form.TextField.superclass.initEvents.call(this);
8522         if(this.validationEvent == 'keyup'){
8523             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8524             this.inputEl().on('keyup', this.filterValidation, this);
8525         }
8526         else if(this.validationEvent !== false){
8527             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8528         }
8529         
8530         if(this.selectOnFocus){
8531             this.on("focus", this.preFocus, this);
8532             
8533         }
8534         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8535             this.inputEl().on("keypress", this.filterKeys, this);
8536         }
8537        /* if(this.grow){
8538             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8539             this.el.on("click", this.autoSize,  this);
8540         }
8541         */
8542         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8543             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8544         }
8545         
8546         if (typeof(this.before) == 'object') {
8547             this.before.render(this.el.select('.roo-input-before',true).first());
8548         }
8549         if (typeof(this.after) == 'object') {
8550             this.after.render(this.el.select('.roo-input-after',true).first());
8551         }
8552         
8553         
8554     },
8555     filterValidation : function(e){
8556         if(!e.isNavKeyPress()){
8557             this.validationTask.delay(this.validationDelay);
8558         }
8559     },
8560      /**
8561      * Validates the field value
8562      * @return {Boolean} True if the value is valid, else false
8563      */
8564     validate : function(){
8565         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8566         if(this.disabled || this.validateValue(this.getRawValue())){
8567             this.markValid();
8568             return true;
8569         }
8570         
8571         this.markInvalid();
8572         return false;
8573     },
8574     
8575     
8576     /**
8577      * Validates a value according to the field's validation rules and marks the field as invalid
8578      * if the validation fails
8579      * @param {Mixed} value The value to validate
8580      * @return {Boolean} True if the value is valid, else false
8581      */
8582     validateValue : function(value){
8583         if(value.length < 1)  { // if it's blank
8584             if(this.allowBlank){
8585                 return true;
8586             }
8587             return false;
8588         }
8589         
8590         if(value.length < this.minLength){
8591             return false;
8592         }
8593         if(value.length > this.maxLength){
8594             return false;
8595         }
8596         if(this.vtype){
8597             var vt = Roo.form.VTypes;
8598             if(!vt[this.vtype](value, this)){
8599                 return false;
8600             }
8601         }
8602         if(typeof this.validator == "function"){
8603             var msg = this.validator(value);
8604             if(msg !== true){
8605                 return false;
8606             }
8607         }
8608         
8609         if(this.regex && !this.regex.test(value)){
8610             return false;
8611         }
8612         
8613         return true;
8614     },
8615
8616     
8617     
8618      // private
8619     fireKey : function(e){
8620         //Roo.log('field ' + e.getKey());
8621         if(e.isNavKeyPress()){
8622             this.fireEvent("specialkey", this, e);
8623         }
8624     },
8625     focus : function (selectText){
8626         if(this.rendered){
8627             this.inputEl().focus();
8628             if(selectText === true){
8629                 this.inputEl().dom.select();
8630             }
8631         }
8632         return this;
8633     } ,
8634     
8635     onFocus : function(){
8636         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8637            // this.el.addClass(this.focusClass);
8638         }
8639         if(!this.hasFocus){
8640             this.hasFocus = true;
8641             this.startValue = this.getValue();
8642             this.fireEvent("focus", this);
8643         }
8644     },
8645     
8646     beforeBlur : Roo.emptyFn,
8647
8648     
8649     // private
8650     onBlur : function(){
8651         this.beforeBlur();
8652         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8653             //this.el.removeClass(this.focusClass);
8654         }
8655         this.hasFocus = false;
8656         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8657             this.validate();
8658         }
8659         var v = this.getValue();
8660         if(String(v) !== String(this.startValue)){
8661             this.fireEvent('change', this, v, this.startValue);
8662         }
8663         this.fireEvent("blur", this);
8664     },
8665     
8666     /**
8667      * Resets the current field value to the originally loaded value and clears any validation messages
8668      */
8669     reset : function(){
8670         this.setValue(this.originalValue);
8671         this.validate();
8672     },
8673      /**
8674      * Returns the name of the field
8675      * @return {Mixed} name The name field
8676      */
8677     getName: function(){
8678         return this.name;
8679     },
8680      /**
8681      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8682      * @return {Mixed} value The field value
8683      */
8684     getValue : function(){
8685         
8686         var v = this.inputEl().getValue();
8687         
8688         return v;
8689     },
8690     /**
8691      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8692      * @return {Mixed} value The field value
8693      */
8694     getRawValue : function(){
8695         var v = this.inputEl().getValue();
8696         
8697         return v;
8698     },
8699     
8700     /**
8701      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8702      * @param {Mixed} value The value to set
8703      */
8704     setRawValue : function(v){
8705         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8706     },
8707     
8708     selectText : function(start, end){
8709         var v = this.getRawValue();
8710         if(v.length > 0){
8711             start = start === undefined ? 0 : start;
8712             end = end === undefined ? v.length : end;
8713             var d = this.inputEl().dom;
8714             if(d.setSelectionRange){
8715                 d.setSelectionRange(start, end);
8716             }else if(d.createTextRange){
8717                 var range = d.createTextRange();
8718                 range.moveStart("character", start);
8719                 range.moveEnd("character", v.length-end);
8720                 range.select();
8721             }
8722         }
8723     },
8724     
8725     /**
8726      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8727      * @param {Mixed} value The value to set
8728      */
8729     setValue : function(v){
8730         this.value = v;
8731         if(this.rendered){
8732             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8733             this.validate();
8734         }
8735     },
8736     
8737     /*
8738     processValue : function(value){
8739         if(this.stripCharsRe){
8740             var newValue = value.replace(this.stripCharsRe, '');
8741             if(newValue !== value){
8742                 this.setRawValue(newValue);
8743                 return newValue;
8744             }
8745         }
8746         return value;
8747     },
8748   */
8749     preFocus : function(){
8750         
8751         if(this.selectOnFocus){
8752             this.inputEl().dom.select();
8753         }
8754     },
8755     filterKeys : function(e){
8756         var k = e.getKey();
8757         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8758             return;
8759         }
8760         var c = e.getCharCode(), cc = String.fromCharCode(c);
8761         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8762             return;
8763         }
8764         if(!this.maskRe.test(cc)){
8765             e.stopEvent();
8766         }
8767     },
8768      /**
8769      * Clear any invalid styles/messages for this field
8770      */
8771     clearInvalid : function(){
8772         
8773         if(!this.el || this.preventMark){ // not rendered
8774             return;
8775         }
8776         
8777         if(this.indicator){
8778             this.indicator.hide();
8779         }
8780         
8781         this.el.removeClass(this.invalidClass);
8782         
8783         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8784             
8785             var feedback = this.el.select('.form-control-feedback', true).first();
8786             
8787             if(feedback){
8788                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8789             }
8790             
8791         }
8792         
8793         this.fireEvent('valid', this);
8794     },
8795     
8796      /**
8797      * Mark this field as valid
8798      */
8799     markValid : function()
8800     {
8801         if(!this.el  || this.preventMark){ // not rendered
8802             return;
8803         }
8804         
8805         this.el.removeClass([this.invalidClass, this.validClass]);
8806         
8807         var feedback = this.el.select('.form-control-feedback', true).first();
8808             
8809         if(feedback){
8810             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8811         }
8812
8813         if(this.disabled || this.allowBlank){
8814             return;
8815         }
8816         
8817         if(this.indicator){
8818             this.indicator.hide();
8819         }
8820         
8821         this.el.addClass(this.validClass);
8822         
8823         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8824             
8825             var feedback = this.el.select('.form-control-feedback', true).first();
8826             
8827             if(feedback){
8828                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8829                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8830             }
8831             
8832         }
8833         
8834         this.fireEvent('valid', this);
8835     },
8836     
8837      /**
8838      * Mark this field as invalid
8839      * @param {String} msg The validation message
8840      */
8841     markInvalid : function(msg)
8842     {
8843         if(!this.el  || this.preventMark){ // not rendered
8844             return;
8845         }
8846         
8847         this.el.removeClass([this.invalidClass, this.validClass]);
8848         
8849         var feedback = this.el.select('.form-control-feedback', true).first();
8850             
8851         if(feedback){
8852             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8853         }
8854
8855         if(this.disabled || this.allowBlank){
8856             return;
8857         }
8858         
8859         if(this.indicator){
8860             this.indicator.show();
8861         }
8862         
8863         this.el.addClass(this.invalidClass);
8864         
8865         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8866             
8867             var feedback = this.el.select('.form-control-feedback', true).first();
8868             
8869             if(feedback){
8870                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8871                 
8872                 if(this.getValue().length || this.forceFeedback){
8873                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8874                 }
8875                 
8876             }
8877             
8878         }
8879         
8880         this.fireEvent('invalid', this, msg);
8881     },
8882     // private
8883     SafariOnKeyDown : function(event)
8884     {
8885         // this is a workaround for a password hang bug on chrome/ webkit.
8886         
8887         var isSelectAll = false;
8888         
8889         if(this.inputEl().dom.selectionEnd > 0){
8890             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8891         }
8892         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8893             event.preventDefault();
8894             this.setValue('');
8895             return;
8896         }
8897         
8898         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8899             
8900             event.preventDefault();
8901             // this is very hacky as keydown always get's upper case.
8902             //
8903             var cc = String.fromCharCode(event.getCharCode());
8904             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8905             
8906         }
8907     },
8908     adjustWidth : function(tag, w){
8909         tag = tag.toLowerCase();
8910         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8911             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8912                 if(tag == 'input'){
8913                     return w + 2;
8914                 }
8915                 if(tag == 'textarea'){
8916                     return w-2;
8917                 }
8918             }else if(Roo.isOpera){
8919                 if(tag == 'input'){
8920                     return w + 2;
8921                 }
8922                 if(tag == 'textarea'){
8923                     return w-2;
8924                 }
8925             }
8926         }
8927         return w;
8928     }
8929     
8930 });
8931
8932  
8933 /*
8934  * - LGPL
8935  *
8936  * Input
8937  * 
8938  */
8939
8940 /**
8941  * @class Roo.bootstrap.TextArea
8942  * @extends Roo.bootstrap.Input
8943  * Bootstrap TextArea class
8944  * @cfg {Number} cols Specifies the visible width of a text area
8945  * @cfg {Number} rows Specifies the visible number of lines in a text area
8946  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8947  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8948  * @cfg {string} html text
8949  * 
8950  * @constructor
8951  * Create a new TextArea
8952  * @param {Object} config The config object
8953  */
8954
8955 Roo.bootstrap.TextArea = function(config){
8956     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8957    
8958 };
8959
8960 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8961      
8962     cols : false,
8963     rows : 5,
8964     readOnly : false,
8965     warp : 'soft',
8966     resize : false,
8967     value: false,
8968     html: false,
8969     
8970     getAutoCreate : function(){
8971         
8972         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8973         
8974         var id = Roo.id();
8975         
8976         var cfg = {};
8977         
8978         var input =  {
8979             tag: 'textarea',
8980             id : id,
8981             warp : this.warp,
8982             rows : this.rows,
8983             value : this.value || '',
8984             html: this.html || '',
8985             cls : 'form-control',
8986             placeholder : this.placeholder || '' 
8987             
8988         };
8989         
8990         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8991             input.maxLength = this.maxLength;
8992         }
8993         
8994         if(this.resize){
8995             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8996         }
8997         
8998         if(this.cols){
8999             input.cols = this.cols;
9000         }
9001         
9002         if (this.readOnly) {
9003             input.readonly = true;
9004         }
9005         
9006         if (this.name) {
9007             input.name = this.name;
9008         }
9009         
9010         if (this.size) {
9011             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9012         }
9013         
9014         var settings=this;
9015         ['xs','sm','md','lg'].map(function(size){
9016             if (settings[size]) {
9017                 cfg.cls += ' col-' + size + '-' + settings[size];
9018             }
9019         });
9020         
9021         var inputblock = input;
9022         
9023         if(this.hasFeedback && !this.allowBlank){
9024             
9025             var feedback = {
9026                 tag: 'span',
9027                 cls: 'glyphicon form-control-feedback'
9028             };
9029
9030             inputblock = {
9031                 cls : 'has-feedback',
9032                 cn :  [
9033                     input,
9034                     feedback
9035                 ] 
9036             };  
9037         }
9038         
9039         
9040         if (this.before || this.after) {
9041             
9042             inputblock = {
9043                 cls : 'input-group',
9044                 cn :  [] 
9045             };
9046             if (this.before) {
9047                 inputblock.cn.push({
9048                     tag :'span',
9049                     cls : 'input-group-addon',
9050                     html : this.before
9051                 });
9052             }
9053             
9054             inputblock.cn.push(input);
9055             
9056             if(this.hasFeedback && !this.allowBlank){
9057                 inputblock.cls += ' has-feedback';
9058                 inputblock.cn.push(feedback);
9059             }
9060             
9061             if (this.after) {
9062                 inputblock.cn.push({
9063                     tag :'span',
9064                     cls : 'input-group-addon',
9065                     html : this.after
9066                 });
9067             }
9068             
9069         }
9070         
9071         if (align ==='left' && this.fieldLabel.length) {
9072 //                Roo.log("left and has label");
9073                 cfg.cn = [
9074                     
9075                     {
9076                         tag: 'label',
9077                         'for' :  id,
9078                         cls : 'control-label col-sm-' + this.labelWidth,
9079                         html : this.fieldLabel
9080                         
9081                     },
9082                     {
9083                         cls : "col-sm-" + (12 - this.labelWidth), 
9084                         cn: [
9085                             inputblock
9086                         ]
9087                     }
9088                     
9089                 ];
9090         } else if ( this.fieldLabel.length) {
9091 //                Roo.log(" label");
9092                  cfg.cn = [
9093                    
9094                     {
9095                         tag: 'label',
9096                         //cls : 'input-group-addon',
9097                         html : this.fieldLabel
9098                         
9099                     },
9100                     
9101                     inputblock
9102                     
9103                 ];
9104
9105         } else {
9106             
9107 //                   Roo.log(" no label && no align");
9108                 cfg.cn = [
9109                     
9110                         inputblock
9111                     
9112                 ];
9113                 
9114                 
9115         }
9116         
9117         if (this.disabled) {
9118             input.disabled=true;
9119         }
9120         
9121         return cfg;
9122         
9123     },
9124     /**
9125      * return the real textarea element.
9126      */
9127     inputEl: function ()
9128     {
9129         return this.el.select('textarea.form-control',true).first();
9130     },
9131     
9132     /**
9133      * Clear any invalid styles/messages for this field
9134      */
9135     clearInvalid : function()
9136     {
9137         
9138         if(!this.el || this.preventMark){ // not rendered
9139             return;
9140         }
9141         
9142         var label = this.el.select('label', true).first();
9143         var icon = this.el.select('i.fa-star', true).first();
9144         
9145         if(label && icon){
9146             icon.remove();
9147         }
9148         
9149         this.el.removeClass(this.invalidClass);
9150         
9151         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9152             
9153             var feedback = this.el.select('.form-control-feedback', true).first();
9154             
9155             if(feedback){
9156                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9157             }
9158             
9159         }
9160         
9161         this.fireEvent('valid', this);
9162     },
9163     
9164      /**
9165      * Mark this field as valid
9166      */
9167     markValid : function()
9168     {
9169         if(!this.el  || this.preventMark){ // not rendered
9170             return;
9171         }
9172         
9173         this.el.removeClass([this.invalidClass, this.validClass]);
9174         
9175         var feedback = this.el.select('.form-control-feedback', true).first();
9176             
9177         if(feedback){
9178             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9179         }
9180
9181         if(this.disabled || this.allowBlank){
9182             return;
9183         }
9184         
9185         var label = this.el.select('label', true).first();
9186         var icon = this.el.select('i.fa-star', true).first();
9187         
9188         if(label && icon){
9189             icon.remove();
9190         }
9191         
9192         this.el.addClass(this.validClass);
9193         
9194         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9195             
9196             var feedback = this.el.select('.form-control-feedback', true).first();
9197             
9198             if(feedback){
9199                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9200                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9201             }
9202             
9203         }
9204         
9205         this.fireEvent('valid', this);
9206     },
9207     
9208      /**
9209      * Mark this field as invalid
9210      * @param {String} msg The validation message
9211      */
9212     markInvalid : function(msg)
9213     {
9214         if(!this.el  || this.preventMark){ // not rendered
9215             return;
9216         }
9217         
9218         this.el.removeClass([this.invalidClass, this.validClass]);
9219         
9220         var feedback = this.el.select('.form-control-feedback', true).first();
9221             
9222         if(feedback){
9223             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9224         }
9225
9226         if(this.disabled || this.allowBlank){
9227             return;
9228         }
9229         
9230         var label = this.el.select('label', true).first();
9231         var icon = this.el.select('i.fa-star', true).first();
9232         
9233         if(!this.getValue().length && label && !icon){
9234             this.el.createChild({
9235                 tag : 'i',
9236                 cls : 'text-danger fa fa-lg fa-star',
9237                 tooltip : 'This field is required',
9238                 style : 'margin-right:5px;'
9239             }, label, true);
9240         }
9241
9242         this.el.addClass(this.invalidClass);
9243         
9244         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9245             
9246             var feedback = this.el.select('.form-control-feedback', true).first();
9247             
9248             if(feedback){
9249                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9250                 
9251                 if(this.getValue().length || this.forceFeedback){
9252                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9253                 }
9254                 
9255             }
9256             
9257         }
9258         
9259         this.fireEvent('invalid', this, msg);
9260     }
9261 });
9262
9263  
9264 /*
9265  * - LGPL
9266  *
9267  * trigger field - base class for combo..
9268  * 
9269  */
9270  
9271 /**
9272  * @class Roo.bootstrap.TriggerField
9273  * @extends Roo.bootstrap.Input
9274  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9275  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9276  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9277  * for which you can provide a custom implementation.  For example:
9278  * <pre><code>
9279 var trigger = new Roo.bootstrap.TriggerField();
9280 trigger.onTriggerClick = myTriggerFn;
9281 trigger.applyTo('my-field');
9282 </code></pre>
9283  *
9284  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9285  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9286  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9287  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9288  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9289
9290  * @constructor
9291  * Create a new TriggerField.
9292  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9293  * to the base TextField)
9294  */
9295 Roo.bootstrap.TriggerField = function(config){
9296     this.mimicing = false;
9297     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9298 };
9299
9300 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9301     /**
9302      * @cfg {String} triggerClass A CSS class to apply to the trigger
9303      */
9304      /**
9305      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9306      */
9307     hideTrigger:false,
9308
9309     /**
9310      * @cfg {Boolean} removable (true|false) special filter default false
9311      */
9312     removable : false,
9313     
9314     /** @cfg {Boolean} grow @hide */
9315     /** @cfg {Number} growMin @hide */
9316     /** @cfg {Number} growMax @hide */
9317
9318     /**
9319      * @hide 
9320      * @method
9321      */
9322     autoSize: Roo.emptyFn,
9323     // private
9324     monitorTab : true,
9325     // private
9326     deferHeight : true,
9327
9328     
9329     actionMode : 'wrap',
9330     
9331     caret : false,
9332     
9333     
9334     getAutoCreate : function(){
9335        
9336         var align = this.labelAlign || this.parentLabelAlign();
9337         
9338         var id = Roo.id();
9339         
9340         var cfg = {
9341             cls: 'form-group' //input-group
9342         };
9343         
9344         
9345         var input =  {
9346             tag: 'input',
9347             id : id,
9348             type : this.inputType,
9349             cls : 'form-control',
9350             autocomplete: 'new-password',
9351             placeholder : this.placeholder || '' 
9352             
9353         };
9354         if (this.name) {
9355             input.name = this.name;
9356         }
9357         if (this.size) {
9358             input.cls += ' input-' + this.size;
9359         }
9360         
9361         if (this.disabled) {
9362             input.disabled=true;
9363         }
9364         
9365         var inputblock = input;
9366         
9367         if(this.hasFeedback && !this.allowBlank){
9368             
9369             var feedback = {
9370                 tag: 'span',
9371                 cls: 'glyphicon form-control-feedback'
9372             };
9373             
9374             if(this.removable && !this.editable && !this.tickable){
9375                 inputblock = {
9376                     cls : 'has-feedback',
9377                     cn :  [
9378                         inputblock,
9379                         {
9380                             tag: 'button',
9381                             html : 'x',
9382                             cls : 'roo-combo-removable-btn close'
9383                         },
9384                         feedback
9385                     ] 
9386                 };
9387             } else {
9388                 inputblock = {
9389                     cls : 'has-feedback',
9390                     cn :  [
9391                         inputblock,
9392                         feedback
9393                     ] 
9394                 };
9395             }
9396
9397         } else {
9398             if(this.removable && !this.editable && !this.tickable){
9399                 inputblock = {
9400                     cls : 'roo-removable',
9401                     cn :  [
9402                         inputblock,
9403                         {
9404                             tag: 'button',
9405                             html : 'x',
9406                             cls : 'roo-combo-removable-btn close'
9407                         }
9408                     ] 
9409                 };
9410             }
9411         }
9412         
9413         if (this.before || this.after) {
9414             
9415             inputblock = {
9416                 cls : 'input-group',
9417                 cn :  [] 
9418             };
9419             if (this.before) {
9420                 inputblock.cn.push({
9421                     tag :'span',
9422                     cls : 'input-group-addon',
9423                     html : this.before
9424                 });
9425             }
9426             
9427             inputblock.cn.push(input);
9428             
9429             if(this.hasFeedback && !this.allowBlank){
9430                 inputblock.cls += ' has-feedback';
9431                 inputblock.cn.push(feedback);
9432             }
9433             
9434             if (this.after) {
9435                 inputblock.cn.push({
9436                     tag :'span',
9437                     cls : 'input-group-addon',
9438                     html : this.after
9439                 });
9440             }
9441             
9442         };
9443         
9444         var box = {
9445             tag: 'div',
9446             cn: [
9447                 {
9448                     tag: 'input',
9449                     type : 'hidden',
9450                     cls: 'form-hidden-field'
9451                 },
9452                 inputblock
9453             ]
9454             
9455         };
9456         
9457         if(this.multiple){
9458             box = {
9459                 tag: 'div',
9460                 cn: [
9461                     {
9462                         tag: 'input',
9463                         type : 'hidden',
9464                         cls: 'form-hidden-field'
9465                     },
9466                     {
9467                         tag: 'ul',
9468                         cls: 'roo-select2-choices',
9469                         cn:[
9470                             {
9471                                 tag: 'li',
9472                                 cls: 'roo-select2-search-field',
9473                                 cn: [
9474
9475                                     inputblock
9476                                 ]
9477                             }
9478                         ]
9479                     }
9480                 ]
9481             }
9482         };
9483         
9484         var combobox = {
9485             cls: 'roo-select2-container input-group',
9486             cn: [
9487                 box
9488 //                {
9489 //                    tag: 'ul',
9490 //                    cls: 'typeahead typeahead-long dropdown-menu',
9491 //                    style: 'display:none'
9492 //                }
9493             ]
9494         };
9495         
9496         if(!this.multiple && this.showToggleBtn){
9497             
9498             var caret = {
9499                         tag: 'span',
9500                         cls: 'caret'
9501              };
9502             if (this.caret != false) {
9503                 caret = {
9504                      tag: 'i',
9505                      cls: 'fa fa-' + this.caret
9506                 };
9507                 
9508             }
9509             
9510             combobox.cn.push({
9511                 tag :'span',
9512                 cls : 'input-group-addon btn dropdown-toggle',
9513                 cn : [
9514                     caret,
9515                     {
9516                         tag: 'span',
9517                         cls: 'combobox-clear',
9518                         cn  : [
9519                             {
9520                                 tag : 'i',
9521                                 cls: 'icon-remove'
9522                             }
9523                         ]
9524                     }
9525                 ]
9526
9527             })
9528         }
9529         
9530         if(this.multiple){
9531             combobox.cls += ' roo-select2-container-multi';
9532         }
9533         
9534         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9535             
9536 //                Roo.log("left and has label");
9537             cfg.cn = [
9538                 {
9539                     tag : 'i',
9540                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9541                     tooltip : 'This field is required'
9542                 },
9543                 {
9544                     tag: 'label',
9545                     'for' :  id,
9546                     cls : 'control-label col-sm-' + this.labelWidth,
9547                     html : this.fieldLabel
9548
9549                 },
9550                 {
9551                     cls : "col-sm-" + (12 - this.labelWidth), 
9552                     cn: [
9553                         combobox
9554                     ]
9555                 }
9556
9557             ];
9558             
9559             if(this.indicatorpos == 'right'){
9560                 cfg.cn = [
9561                     {
9562                         tag: 'label',
9563                         'for' :  id,
9564                         cls : 'control-label col-sm-' + this.labelWidth,
9565                         html : this.fieldLabel
9566
9567                     },
9568                     {
9569                         tag : 'i',
9570                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9571                         tooltip : 'This field is required'
9572                     },
9573                     {
9574                         cls : "col-sm-" + (12 - this.labelWidth), 
9575                         cn: [
9576                             combobox
9577                         ]
9578                     }
9579
9580                 ];
9581             }
9582             
9583         } else if ( this.fieldLabel.length) {
9584 //                Roo.log(" label");
9585             cfg.cn = [
9586                 {
9587                    tag : 'i',
9588                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9589                    tooltip : 'This field is required'
9590                },
9591                {
9592                    tag: 'label',
9593                    //cls : 'input-group-addon',
9594                    html : this.fieldLabel
9595
9596                },
9597
9598                combobox
9599
9600             ];
9601             
9602             if(this.indicatorpos == 'right'){
9603                 
9604                 cfg.cn = [
9605                     {
9606                        tag: 'label',
9607                        //cls : 'input-group-addon',
9608                        html : this.fieldLabel
9609
9610                     },
9611                     {
9612                        tag : 'i',
9613                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9614                        tooltip : 'This field is required'
9615                     },
9616                     
9617                     combobox
9618
9619                 ];
9620
9621             }
9622
9623         } else {
9624             
9625 //                Roo.log(" no label && no align");
9626                 cfg = combobox
9627                      
9628                 
9629         }
9630          
9631         var settings=this;
9632         ['xs','sm','md','lg'].map(function(size){
9633             if (settings[size]) {
9634                 cfg.cls += ' col-' + size + '-' + settings[size];
9635             }
9636         });
9637         
9638         return cfg;
9639         
9640     },
9641     
9642     
9643     
9644     // private
9645     onResize : function(w, h){
9646 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9647 //        if(typeof w == 'number'){
9648 //            var x = w - this.trigger.getWidth();
9649 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9650 //            this.trigger.setStyle('left', x+'px');
9651 //        }
9652     },
9653
9654     // private
9655     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9656
9657     // private
9658     getResizeEl : function(){
9659         return this.inputEl();
9660     },
9661
9662     // private
9663     getPositionEl : function(){
9664         return this.inputEl();
9665     },
9666
9667     // private
9668     alignErrorIcon : function(){
9669         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9670     },
9671
9672     // private
9673     initEvents : function(){
9674         
9675         this.createList();
9676         
9677         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9678         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9679         if(!this.multiple && this.showToggleBtn){
9680             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9681             if(this.hideTrigger){
9682                 this.trigger.setDisplayed(false);
9683             }
9684             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9685         }
9686         
9687         if(this.multiple){
9688             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9689         }
9690         
9691         if(this.removable && !this.editable && !this.tickable){
9692             var close = this.closeTriggerEl();
9693             
9694             if(close){
9695                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9696                 close.on('click', this.removeBtnClick, this, close);
9697             }
9698         }
9699         
9700         //this.trigger.addClassOnOver('x-form-trigger-over');
9701         //this.trigger.addClassOnClick('x-form-trigger-click');
9702         
9703         //if(!this.width){
9704         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9705         //}
9706     },
9707     
9708     closeTriggerEl : function()
9709     {
9710         var close = this.el.select('.roo-combo-removable-btn', true).first();
9711         return close ? close : false;
9712     },
9713     
9714     removeBtnClick : function(e, h, el)
9715     {
9716         e.preventDefault();
9717         
9718         if(this.fireEvent("remove", this) !== false){
9719             this.reset();
9720             this.fireEvent("afterremove", this)
9721         }
9722     },
9723     
9724     createList : function()
9725     {
9726         this.list = Roo.get(document.body).createChild({
9727             tag: 'ul',
9728             cls: 'typeahead typeahead-long dropdown-menu',
9729             style: 'display:none'
9730         });
9731         
9732         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9733         
9734     },
9735
9736     // private
9737     initTrigger : function(){
9738        
9739     },
9740
9741     // private
9742     onDestroy : function(){
9743         if(this.trigger){
9744             this.trigger.removeAllListeners();
9745           //  this.trigger.remove();
9746         }
9747         //if(this.wrap){
9748         //    this.wrap.remove();
9749         //}
9750         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9751     },
9752
9753     // private
9754     onFocus : function(){
9755         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9756         /*
9757         if(!this.mimicing){
9758             this.wrap.addClass('x-trigger-wrap-focus');
9759             this.mimicing = true;
9760             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9761             if(this.monitorTab){
9762                 this.el.on("keydown", this.checkTab, this);
9763             }
9764         }
9765         */
9766     },
9767
9768     // private
9769     checkTab : function(e){
9770         if(e.getKey() == e.TAB){
9771             this.triggerBlur();
9772         }
9773     },
9774
9775     // private
9776     onBlur : function(){
9777         // do nothing
9778     },
9779
9780     // private
9781     mimicBlur : function(e, t){
9782         /*
9783         if(!this.wrap.contains(t) && this.validateBlur()){
9784             this.triggerBlur();
9785         }
9786         */
9787     },
9788
9789     // private
9790     triggerBlur : function(){
9791         this.mimicing = false;
9792         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9793         if(this.monitorTab){
9794             this.el.un("keydown", this.checkTab, this);
9795         }
9796         //this.wrap.removeClass('x-trigger-wrap-focus');
9797         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9798     },
9799
9800     // private
9801     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9802     validateBlur : function(e, t){
9803         return true;
9804     },
9805
9806     // private
9807     onDisable : function(){
9808         this.inputEl().dom.disabled = true;
9809         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9810         //if(this.wrap){
9811         //    this.wrap.addClass('x-item-disabled');
9812         //}
9813     },
9814
9815     // private
9816     onEnable : function(){
9817         this.inputEl().dom.disabled = false;
9818         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9819         //if(this.wrap){
9820         //    this.el.removeClass('x-item-disabled');
9821         //}
9822     },
9823
9824     // private
9825     onShow : function(){
9826         var ae = this.getActionEl();
9827         
9828         if(ae){
9829             ae.dom.style.display = '';
9830             ae.dom.style.visibility = 'visible';
9831         }
9832     },
9833
9834     // private
9835     
9836     onHide : function(){
9837         var ae = this.getActionEl();
9838         ae.dom.style.display = 'none';
9839     },
9840
9841     /**
9842      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9843      * by an implementing function.
9844      * @method
9845      * @param {EventObject} e
9846      */
9847     onTriggerClick : Roo.emptyFn
9848 });
9849  /*
9850  * Based on:
9851  * Ext JS Library 1.1.1
9852  * Copyright(c) 2006-2007, Ext JS, LLC.
9853  *
9854  * Originally Released Under LGPL - original licence link has changed is not relivant.
9855  *
9856  * Fork - LGPL
9857  * <script type="text/javascript">
9858  */
9859
9860
9861 /**
9862  * @class Roo.data.SortTypes
9863  * @singleton
9864  * Defines the default sorting (casting?) comparison functions used when sorting data.
9865  */
9866 Roo.data.SortTypes = {
9867     /**
9868      * Default sort that does nothing
9869      * @param {Mixed} s The value being converted
9870      * @return {Mixed} The comparison value
9871      */
9872     none : function(s){
9873         return s;
9874     },
9875     
9876     /**
9877      * The regular expression used to strip tags
9878      * @type {RegExp}
9879      * @property
9880      */
9881     stripTagsRE : /<\/?[^>]+>/gi,
9882     
9883     /**
9884      * Strips all HTML tags to sort on text only
9885      * @param {Mixed} s The value being converted
9886      * @return {String} The comparison value
9887      */
9888     asText : function(s){
9889         return String(s).replace(this.stripTagsRE, "");
9890     },
9891     
9892     /**
9893      * Strips all HTML tags to sort on text only - Case insensitive
9894      * @param {Mixed} s The value being converted
9895      * @return {String} The comparison value
9896      */
9897     asUCText : function(s){
9898         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9899     },
9900     
9901     /**
9902      * Case insensitive string
9903      * @param {Mixed} s The value being converted
9904      * @return {String} The comparison value
9905      */
9906     asUCString : function(s) {
9907         return String(s).toUpperCase();
9908     },
9909     
9910     /**
9911      * Date sorting
9912      * @param {Mixed} s The value being converted
9913      * @return {Number} The comparison value
9914      */
9915     asDate : function(s) {
9916         if(!s){
9917             return 0;
9918         }
9919         if(s instanceof Date){
9920             return s.getTime();
9921         }
9922         return Date.parse(String(s));
9923     },
9924     
9925     /**
9926      * Float sorting
9927      * @param {Mixed} s The value being converted
9928      * @return {Float} The comparison value
9929      */
9930     asFloat : function(s) {
9931         var val = parseFloat(String(s).replace(/,/g, ""));
9932         if(isNaN(val)) {
9933             val = 0;
9934         }
9935         return val;
9936     },
9937     
9938     /**
9939      * Integer sorting
9940      * @param {Mixed} s The value being converted
9941      * @return {Number} The comparison value
9942      */
9943     asInt : function(s) {
9944         var val = parseInt(String(s).replace(/,/g, ""));
9945         if(isNaN(val)) {
9946             val = 0;
9947         }
9948         return val;
9949     }
9950 };/*
9951  * Based on:
9952  * Ext JS Library 1.1.1
9953  * Copyright(c) 2006-2007, Ext JS, LLC.
9954  *
9955  * Originally Released Under LGPL - original licence link has changed is not relivant.
9956  *
9957  * Fork - LGPL
9958  * <script type="text/javascript">
9959  */
9960
9961 /**
9962 * @class Roo.data.Record
9963  * Instances of this class encapsulate both record <em>definition</em> information, and record
9964  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9965  * to access Records cached in an {@link Roo.data.Store} object.<br>
9966  * <p>
9967  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9968  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9969  * objects.<br>
9970  * <p>
9971  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9972  * @constructor
9973  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9974  * {@link #create}. The parameters are the same.
9975  * @param {Array} data An associative Array of data values keyed by the field name.
9976  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9977  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9978  * not specified an integer id is generated.
9979  */
9980 Roo.data.Record = function(data, id){
9981     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9982     this.data = data;
9983 };
9984
9985 /**
9986  * Generate a constructor for a specific record layout.
9987  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9988  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9989  * Each field definition object may contain the following properties: <ul>
9990  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
9991  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9992  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9993  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9994  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9995  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9996  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9997  * this may be omitted.</p></li>
9998  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9999  * <ul><li>auto (Default, implies no conversion)</li>
10000  * <li>string</li>
10001  * <li>int</li>
10002  * <li>float</li>
10003  * <li>boolean</li>
10004  * <li>date</li></ul></p></li>
10005  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10006  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10007  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10008  * by the Reader into an object that will be stored in the Record. It is passed the
10009  * following parameters:<ul>
10010  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10011  * </ul></p></li>
10012  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10013  * </ul>
10014  * <br>usage:<br><pre><code>
10015 var TopicRecord = Roo.data.Record.create(
10016     {name: 'title', mapping: 'topic_title'},
10017     {name: 'author', mapping: 'username'},
10018     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10019     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10020     {name: 'lastPoster', mapping: 'user2'},
10021     {name: 'excerpt', mapping: 'post_text'}
10022 );
10023
10024 var myNewRecord = new TopicRecord({
10025     title: 'Do my job please',
10026     author: 'noobie',
10027     totalPosts: 1,
10028     lastPost: new Date(),
10029     lastPoster: 'Animal',
10030     excerpt: 'No way dude!'
10031 });
10032 myStore.add(myNewRecord);
10033 </code></pre>
10034  * @method create
10035  * @static
10036  */
10037 Roo.data.Record.create = function(o){
10038     var f = function(){
10039         f.superclass.constructor.apply(this, arguments);
10040     };
10041     Roo.extend(f, Roo.data.Record);
10042     var p = f.prototype;
10043     p.fields = new Roo.util.MixedCollection(false, function(field){
10044         return field.name;
10045     });
10046     for(var i = 0, len = o.length; i < len; i++){
10047         p.fields.add(new Roo.data.Field(o[i]));
10048     }
10049     f.getField = function(name){
10050         return p.fields.get(name);  
10051     };
10052     return f;
10053 };
10054
10055 Roo.data.Record.AUTO_ID = 1000;
10056 Roo.data.Record.EDIT = 'edit';
10057 Roo.data.Record.REJECT = 'reject';
10058 Roo.data.Record.COMMIT = 'commit';
10059
10060 Roo.data.Record.prototype = {
10061     /**
10062      * Readonly flag - true if this record has been modified.
10063      * @type Boolean
10064      */
10065     dirty : false,
10066     editing : false,
10067     error: null,
10068     modified: null,
10069
10070     // private
10071     join : function(store){
10072         this.store = store;
10073     },
10074
10075     /**
10076      * Set the named field to the specified value.
10077      * @param {String} name The name of the field to set.
10078      * @param {Object} value The value to set the field to.
10079      */
10080     set : function(name, value){
10081         if(this.data[name] == value){
10082             return;
10083         }
10084         this.dirty = true;
10085         if(!this.modified){
10086             this.modified = {};
10087         }
10088         if(typeof this.modified[name] == 'undefined'){
10089             this.modified[name] = this.data[name];
10090         }
10091         this.data[name] = value;
10092         if(!this.editing && this.store){
10093             this.store.afterEdit(this);
10094         }       
10095     },
10096
10097     /**
10098      * Get the value of the named field.
10099      * @param {String} name The name of the field to get the value of.
10100      * @return {Object} The value of the field.
10101      */
10102     get : function(name){
10103         return this.data[name]; 
10104     },
10105
10106     // private
10107     beginEdit : function(){
10108         this.editing = true;
10109         this.modified = {}; 
10110     },
10111
10112     // private
10113     cancelEdit : function(){
10114         this.editing = false;
10115         delete this.modified;
10116     },
10117
10118     // private
10119     endEdit : function(){
10120         this.editing = false;
10121         if(this.dirty && this.store){
10122             this.store.afterEdit(this);
10123         }
10124     },
10125
10126     /**
10127      * Usually called by the {@link Roo.data.Store} which owns the Record.
10128      * Rejects all changes made to the Record since either creation, or the last commit operation.
10129      * Modified fields are reverted to their original values.
10130      * <p>
10131      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10132      * of reject operations.
10133      */
10134     reject : function(){
10135         var m = this.modified;
10136         for(var n in m){
10137             if(typeof m[n] != "function"){
10138                 this.data[n] = m[n];
10139             }
10140         }
10141         this.dirty = false;
10142         delete this.modified;
10143         this.editing = false;
10144         if(this.store){
10145             this.store.afterReject(this);
10146         }
10147     },
10148
10149     /**
10150      * Usually called by the {@link Roo.data.Store} which owns the Record.
10151      * Commits all changes made to the Record since either creation, or the last commit operation.
10152      * <p>
10153      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10154      * of commit operations.
10155      */
10156     commit : function(){
10157         this.dirty = false;
10158         delete this.modified;
10159         this.editing = false;
10160         if(this.store){
10161             this.store.afterCommit(this);
10162         }
10163     },
10164
10165     // private
10166     hasError : function(){
10167         return this.error != null;
10168     },
10169
10170     // private
10171     clearError : function(){
10172         this.error = null;
10173     },
10174
10175     /**
10176      * Creates a copy of this record.
10177      * @param {String} id (optional) A new record id if you don't want to use this record's id
10178      * @return {Record}
10179      */
10180     copy : function(newId) {
10181         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10182     }
10183 };/*
10184  * Based on:
10185  * Ext JS Library 1.1.1
10186  * Copyright(c) 2006-2007, Ext JS, LLC.
10187  *
10188  * Originally Released Under LGPL - original licence link has changed is not relivant.
10189  *
10190  * Fork - LGPL
10191  * <script type="text/javascript">
10192  */
10193
10194
10195
10196 /**
10197  * @class Roo.data.Store
10198  * @extends Roo.util.Observable
10199  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10200  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10201  * <p>
10202  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10203  * has no knowledge of the format of the data returned by the Proxy.<br>
10204  * <p>
10205  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10206  * instances from the data object. These records are cached and made available through accessor functions.
10207  * @constructor
10208  * Creates a new Store.
10209  * @param {Object} config A config object containing the objects needed for the Store to access data,
10210  * and read the data into Records.
10211  */
10212 Roo.data.Store = function(config){
10213     this.data = new Roo.util.MixedCollection(false);
10214     this.data.getKey = function(o){
10215         return o.id;
10216     };
10217     this.baseParams = {};
10218     // private
10219     this.paramNames = {
10220         "start" : "start",
10221         "limit" : "limit",
10222         "sort" : "sort",
10223         "dir" : "dir",
10224         "multisort" : "_multisort"
10225     };
10226
10227     if(config && config.data){
10228         this.inlineData = config.data;
10229         delete config.data;
10230     }
10231
10232     Roo.apply(this, config);
10233     
10234     if(this.reader){ // reader passed
10235         this.reader = Roo.factory(this.reader, Roo.data);
10236         this.reader.xmodule = this.xmodule || false;
10237         if(!this.recordType){
10238             this.recordType = this.reader.recordType;
10239         }
10240         if(this.reader.onMetaChange){
10241             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10242         }
10243     }
10244
10245     if(this.recordType){
10246         this.fields = this.recordType.prototype.fields;
10247     }
10248     this.modified = [];
10249
10250     this.addEvents({
10251         /**
10252          * @event datachanged
10253          * Fires when the data cache has changed, and a widget which is using this Store
10254          * as a Record cache should refresh its view.
10255          * @param {Store} this
10256          */
10257         datachanged : true,
10258         /**
10259          * @event metachange
10260          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10261          * @param {Store} this
10262          * @param {Object} meta The JSON metadata
10263          */
10264         metachange : true,
10265         /**
10266          * @event add
10267          * Fires when Records have been added to the Store
10268          * @param {Store} this
10269          * @param {Roo.data.Record[]} records The array of Records added
10270          * @param {Number} index The index at which the record(s) were added
10271          */
10272         add : true,
10273         /**
10274          * @event remove
10275          * Fires when a Record has been removed from the Store
10276          * @param {Store} this
10277          * @param {Roo.data.Record} record The Record that was removed
10278          * @param {Number} index The index at which the record was removed
10279          */
10280         remove : true,
10281         /**
10282          * @event update
10283          * Fires when a Record has been updated
10284          * @param {Store} this
10285          * @param {Roo.data.Record} record The Record that was updated
10286          * @param {String} operation The update operation being performed.  Value may be one of:
10287          * <pre><code>
10288  Roo.data.Record.EDIT
10289  Roo.data.Record.REJECT
10290  Roo.data.Record.COMMIT
10291          * </code></pre>
10292          */
10293         update : true,
10294         /**
10295          * @event clear
10296          * Fires when the data cache has been cleared.
10297          * @param {Store} this
10298          */
10299         clear : true,
10300         /**
10301          * @event beforeload
10302          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10303          * the load action will be canceled.
10304          * @param {Store} this
10305          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10306          */
10307         beforeload : true,
10308         /**
10309          * @event beforeloadadd
10310          * Fires after a new set of Records has been loaded.
10311          * @param {Store} this
10312          * @param {Roo.data.Record[]} records The Records that were loaded
10313          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10314          */
10315         beforeloadadd : true,
10316         /**
10317          * @event load
10318          * Fires after a new set of Records has been loaded, before they are added to the store.
10319          * @param {Store} this
10320          * @param {Roo.data.Record[]} records The Records that were loaded
10321          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10322          * @params {Object} return from reader
10323          */
10324         load : true,
10325         /**
10326          * @event loadexception
10327          * Fires if an exception occurs in the Proxy during loading.
10328          * Called with the signature of the Proxy's "loadexception" event.
10329          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10330          * 
10331          * @param {Proxy} 
10332          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10333          * @param {Object} load options 
10334          * @param {Object} jsonData from your request (normally this contains the Exception)
10335          */
10336         loadexception : true
10337     });
10338     
10339     if(this.proxy){
10340         this.proxy = Roo.factory(this.proxy, Roo.data);
10341         this.proxy.xmodule = this.xmodule || false;
10342         this.relayEvents(this.proxy,  ["loadexception"]);
10343     }
10344     this.sortToggle = {};
10345     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10346
10347     Roo.data.Store.superclass.constructor.call(this);
10348
10349     if(this.inlineData){
10350         this.loadData(this.inlineData);
10351         delete this.inlineData;
10352     }
10353 };
10354
10355 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10356      /**
10357     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10358     * without a remote query - used by combo/forms at present.
10359     */
10360     
10361     /**
10362     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10363     */
10364     /**
10365     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10366     */
10367     /**
10368     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10369     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10370     */
10371     /**
10372     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10373     * on any HTTP request
10374     */
10375     /**
10376     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10377     */
10378     /**
10379     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10380     */
10381     multiSort: false,
10382     /**
10383     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10384     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10385     */
10386     remoteSort : false,
10387
10388     /**
10389     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10390      * loaded or when a record is removed. (defaults to false).
10391     */
10392     pruneModifiedRecords : false,
10393
10394     // private
10395     lastOptions : null,
10396
10397     /**
10398      * Add Records to the Store and fires the add event.
10399      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10400      */
10401     add : function(records){
10402         records = [].concat(records);
10403         for(var i = 0, len = records.length; i < len; i++){
10404             records[i].join(this);
10405         }
10406         var index = this.data.length;
10407         this.data.addAll(records);
10408         this.fireEvent("add", this, records, index);
10409     },
10410
10411     /**
10412      * Remove a Record from the Store and fires the remove event.
10413      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10414      */
10415     remove : function(record){
10416         var index = this.data.indexOf(record);
10417         this.data.removeAt(index);
10418         if(this.pruneModifiedRecords){
10419             this.modified.remove(record);
10420         }
10421         this.fireEvent("remove", this, record, index);
10422     },
10423
10424     /**
10425      * Remove all Records from the Store and fires the clear event.
10426      */
10427     removeAll : function(){
10428         this.data.clear();
10429         if(this.pruneModifiedRecords){
10430             this.modified = [];
10431         }
10432         this.fireEvent("clear", this);
10433     },
10434
10435     /**
10436      * Inserts Records to the Store at the given index and fires the add event.
10437      * @param {Number} index The start index at which to insert the passed Records.
10438      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10439      */
10440     insert : function(index, records){
10441         records = [].concat(records);
10442         for(var i = 0, len = records.length; i < len; i++){
10443             this.data.insert(index, records[i]);
10444             records[i].join(this);
10445         }
10446         this.fireEvent("add", this, records, index);
10447     },
10448
10449     /**
10450      * Get the index within the cache of the passed Record.
10451      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10452      * @return {Number} The index of the passed Record. Returns -1 if not found.
10453      */
10454     indexOf : function(record){
10455         return this.data.indexOf(record);
10456     },
10457
10458     /**
10459      * Get the index within the cache of the Record with the passed id.
10460      * @param {String} id The id of the Record to find.
10461      * @return {Number} The index of the Record. Returns -1 if not found.
10462      */
10463     indexOfId : function(id){
10464         return this.data.indexOfKey(id);
10465     },
10466
10467     /**
10468      * Get the Record with the specified id.
10469      * @param {String} id The id of the Record to find.
10470      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10471      */
10472     getById : function(id){
10473         return this.data.key(id);
10474     },
10475
10476     /**
10477      * Get the Record at the specified index.
10478      * @param {Number} index The index of the Record to find.
10479      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10480      */
10481     getAt : function(index){
10482         return this.data.itemAt(index);
10483     },
10484
10485     /**
10486      * Returns a range of Records between specified indices.
10487      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10488      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10489      * @return {Roo.data.Record[]} An array of Records
10490      */
10491     getRange : function(start, end){
10492         return this.data.getRange(start, end);
10493     },
10494
10495     // private
10496     storeOptions : function(o){
10497         o = Roo.apply({}, o);
10498         delete o.callback;
10499         delete o.scope;
10500         this.lastOptions = o;
10501     },
10502
10503     /**
10504      * Loads the Record cache from the configured Proxy using the configured Reader.
10505      * <p>
10506      * If using remote paging, then the first load call must specify the <em>start</em>
10507      * and <em>limit</em> properties in the options.params property to establish the initial
10508      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10509      * <p>
10510      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10511      * and this call will return before the new data has been loaded. Perform any post-processing
10512      * in a callback function, or in a "load" event handler.</strong>
10513      * <p>
10514      * @param {Object} options An object containing properties which control loading options:<ul>
10515      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10516      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10517      * passed the following arguments:<ul>
10518      * <li>r : Roo.data.Record[]</li>
10519      * <li>options: Options object from the load call</li>
10520      * <li>success: Boolean success indicator</li></ul></li>
10521      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10522      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10523      * </ul>
10524      */
10525     load : function(options){
10526         options = options || {};
10527         if(this.fireEvent("beforeload", this, options) !== false){
10528             this.storeOptions(options);
10529             var p = Roo.apply(options.params || {}, this.baseParams);
10530             // if meta was not loaded from remote source.. try requesting it.
10531             if (!this.reader.metaFromRemote) {
10532                 p._requestMeta = 1;
10533             }
10534             if(this.sortInfo && this.remoteSort){
10535                 var pn = this.paramNames;
10536                 p[pn["sort"]] = this.sortInfo.field;
10537                 p[pn["dir"]] = this.sortInfo.direction;
10538             }
10539             if (this.multiSort) {
10540                 var pn = this.paramNames;
10541                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10542             }
10543             
10544             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10545         }
10546     },
10547
10548     /**
10549      * Reloads the Record cache from the configured Proxy using the configured Reader and
10550      * the options from the last load operation performed.
10551      * @param {Object} options (optional) An object containing properties which may override the options
10552      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10553      * the most recently used options are reused).
10554      */
10555     reload : function(options){
10556         this.load(Roo.applyIf(options||{}, this.lastOptions));
10557     },
10558
10559     // private
10560     // Called as a callback by the Reader during a load operation.
10561     loadRecords : function(o, options, success){
10562         if(!o || success === false){
10563             if(success !== false){
10564                 this.fireEvent("load", this, [], options, o);
10565             }
10566             if(options.callback){
10567                 options.callback.call(options.scope || this, [], options, false);
10568             }
10569             return;
10570         }
10571         // if data returned failure - throw an exception.
10572         if (o.success === false) {
10573             // show a message if no listener is registered.
10574             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10575                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10576             }
10577             // loadmask wil be hooked into this..
10578             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10579             return;
10580         }
10581         var r = o.records, t = o.totalRecords || r.length;
10582         
10583         this.fireEvent("beforeloadadd", this, r, options, o);
10584         
10585         if(!options || options.add !== true){
10586             if(this.pruneModifiedRecords){
10587                 this.modified = [];
10588             }
10589             for(var i = 0, len = r.length; i < len; i++){
10590                 r[i].join(this);
10591             }
10592             if(this.snapshot){
10593                 this.data = this.snapshot;
10594                 delete this.snapshot;
10595             }
10596             this.data.clear();
10597             this.data.addAll(r);
10598             this.totalLength = t;
10599             this.applySort();
10600             this.fireEvent("datachanged", this);
10601         }else{
10602             this.totalLength = Math.max(t, this.data.length+r.length);
10603             this.add(r);
10604         }
10605         this.fireEvent("load", this, r, options, o);
10606         if(options.callback){
10607             options.callback.call(options.scope || this, r, options, true);
10608         }
10609     },
10610
10611
10612     /**
10613      * Loads data from a passed data block. A Reader which understands the format of the data
10614      * must have been configured in the constructor.
10615      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10616      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10617      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10618      */
10619     loadData : function(o, append){
10620         var r = this.reader.readRecords(o);
10621         this.loadRecords(r, {add: append}, true);
10622     },
10623
10624     /**
10625      * Gets the number of cached records.
10626      * <p>
10627      * <em>If using paging, this may not be the total size of the dataset. If the data object
10628      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10629      * the data set size</em>
10630      */
10631     getCount : function(){
10632         return this.data.length || 0;
10633     },
10634
10635     /**
10636      * Gets the total number of records in the dataset as returned by the server.
10637      * <p>
10638      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10639      * the dataset size</em>
10640      */
10641     getTotalCount : function(){
10642         return this.totalLength || 0;
10643     },
10644
10645     /**
10646      * Returns the sort state of the Store as an object with two properties:
10647      * <pre><code>
10648  field {String} The name of the field by which the Records are sorted
10649  direction {String} The sort order, "ASC" or "DESC"
10650      * </code></pre>
10651      */
10652     getSortState : function(){
10653         return this.sortInfo;
10654     },
10655
10656     // private
10657     applySort : function(){
10658         if(this.sortInfo && !this.remoteSort){
10659             var s = this.sortInfo, f = s.field;
10660             var st = this.fields.get(f).sortType;
10661             var fn = function(r1, r2){
10662                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10663                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10664             };
10665             this.data.sort(s.direction, fn);
10666             if(this.snapshot && this.snapshot != this.data){
10667                 this.snapshot.sort(s.direction, fn);
10668             }
10669         }
10670     },
10671
10672     /**
10673      * Sets the default sort column and order to be used by the next load operation.
10674      * @param {String} fieldName The name of the field to sort by.
10675      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10676      */
10677     setDefaultSort : function(field, dir){
10678         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10679     },
10680
10681     /**
10682      * Sort the Records.
10683      * If remote sorting is used, the sort is performed on the server, and the cache is
10684      * reloaded. If local sorting is used, the cache is sorted internally.
10685      * @param {String} fieldName The name of the field to sort by.
10686      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10687      */
10688     sort : function(fieldName, dir){
10689         var f = this.fields.get(fieldName);
10690         if(!dir){
10691             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10692             
10693             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10694                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10695             }else{
10696                 dir = f.sortDir;
10697             }
10698         }
10699         this.sortToggle[f.name] = dir;
10700         this.sortInfo = {field: f.name, direction: dir};
10701         if(!this.remoteSort){
10702             this.applySort();
10703             this.fireEvent("datachanged", this);
10704         }else{
10705             this.load(this.lastOptions);
10706         }
10707     },
10708
10709     /**
10710      * Calls the specified function for each of the Records in the cache.
10711      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10712      * Returning <em>false</em> aborts and exits the iteration.
10713      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10714      */
10715     each : function(fn, scope){
10716         this.data.each(fn, scope);
10717     },
10718
10719     /**
10720      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10721      * (e.g., during paging).
10722      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10723      */
10724     getModifiedRecords : function(){
10725         return this.modified;
10726     },
10727
10728     // private
10729     createFilterFn : function(property, value, anyMatch){
10730         if(!value.exec){ // not a regex
10731             value = String(value);
10732             if(value.length == 0){
10733                 return false;
10734             }
10735             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10736         }
10737         return function(r){
10738             return value.test(r.data[property]);
10739         };
10740     },
10741
10742     /**
10743      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10744      * @param {String} property A field on your records
10745      * @param {Number} start The record index to start at (defaults to 0)
10746      * @param {Number} end The last record index to include (defaults to length - 1)
10747      * @return {Number} The sum
10748      */
10749     sum : function(property, start, end){
10750         var rs = this.data.items, v = 0;
10751         start = start || 0;
10752         end = (end || end === 0) ? end : rs.length-1;
10753
10754         for(var i = start; i <= end; i++){
10755             v += (rs[i].data[property] || 0);
10756         }
10757         return v;
10758     },
10759
10760     /**
10761      * Filter the records by a specified property.
10762      * @param {String} field A field on your records
10763      * @param {String/RegExp} value Either a string that the field
10764      * should start with or a RegExp to test against the field
10765      * @param {Boolean} anyMatch True to match any part not just the beginning
10766      */
10767     filter : function(property, value, anyMatch){
10768         var fn = this.createFilterFn(property, value, anyMatch);
10769         return fn ? this.filterBy(fn) : this.clearFilter();
10770     },
10771
10772     /**
10773      * Filter by a function. The specified function will be called with each
10774      * record in this data source. If the function returns true the record is included,
10775      * otherwise it is filtered.
10776      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10777      * @param {Object} scope (optional) The scope of the function (defaults to this)
10778      */
10779     filterBy : function(fn, scope){
10780         this.snapshot = this.snapshot || this.data;
10781         this.data = this.queryBy(fn, scope||this);
10782         this.fireEvent("datachanged", this);
10783     },
10784
10785     /**
10786      * Query the records by a specified property.
10787      * @param {String} field A field on your records
10788      * @param {String/RegExp} value Either a string that the field
10789      * should start with or a RegExp to test against the field
10790      * @param {Boolean} anyMatch True to match any part not just the beginning
10791      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10792      */
10793     query : function(property, value, anyMatch){
10794         var fn = this.createFilterFn(property, value, anyMatch);
10795         return fn ? this.queryBy(fn) : this.data.clone();
10796     },
10797
10798     /**
10799      * Query by a function. The specified function will be called with each
10800      * record in this data source. If the function returns true the record is included
10801      * in the results.
10802      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10803      * @param {Object} scope (optional) The scope of the function (defaults to this)
10804       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10805      **/
10806     queryBy : function(fn, scope){
10807         var data = this.snapshot || this.data;
10808         return data.filterBy(fn, scope||this);
10809     },
10810
10811     /**
10812      * Collects unique values for a particular dataIndex from this store.
10813      * @param {String} dataIndex The property to collect
10814      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10815      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10816      * @return {Array} An array of the unique values
10817      **/
10818     collect : function(dataIndex, allowNull, bypassFilter){
10819         var d = (bypassFilter === true && this.snapshot) ?
10820                 this.snapshot.items : this.data.items;
10821         var v, sv, r = [], l = {};
10822         for(var i = 0, len = d.length; i < len; i++){
10823             v = d[i].data[dataIndex];
10824             sv = String(v);
10825             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10826                 l[sv] = true;
10827                 r[r.length] = v;
10828             }
10829         }
10830         return r;
10831     },
10832
10833     /**
10834      * Revert to a view of the Record cache with no filtering applied.
10835      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10836      */
10837     clearFilter : function(suppressEvent){
10838         if(this.snapshot && this.snapshot != this.data){
10839             this.data = this.snapshot;
10840             delete this.snapshot;
10841             if(suppressEvent !== true){
10842                 this.fireEvent("datachanged", this);
10843             }
10844         }
10845     },
10846
10847     // private
10848     afterEdit : function(record){
10849         if(this.modified.indexOf(record) == -1){
10850             this.modified.push(record);
10851         }
10852         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10853     },
10854     
10855     // private
10856     afterReject : function(record){
10857         this.modified.remove(record);
10858         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10859     },
10860
10861     // private
10862     afterCommit : function(record){
10863         this.modified.remove(record);
10864         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10865     },
10866
10867     /**
10868      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10869      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10870      */
10871     commitChanges : function(){
10872         var m = this.modified.slice(0);
10873         this.modified = [];
10874         for(var i = 0, len = m.length; i < len; i++){
10875             m[i].commit();
10876         }
10877     },
10878
10879     /**
10880      * Cancel outstanding changes on all changed records.
10881      */
10882     rejectChanges : function(){
10883         var m = this.modified.slice(0);
10884         this.modified = [];
10885         for(var i = 0, len = m.length; i < len; i++){
10886             m[i].reject();
10887         }
10888     },
10889
10890     onMetaChange : function(meta, rtype, o){
10891         this.recordType = rtype;
10892         this.fields = rtype.prototype.fields;
10893         delete this.snapshot;
10894         this.sortInfo = meta.sortInfo || this.sortInfo;
10895         this.modified = [];
10896         this.fireEvent('metachange', this, this.reader.meta);
10897     },
10898     
10899     moveIndex : function(data, type)
10900     {
10901         var index = this.indexOf(data);
10902         
10903         var newIndex = index + type;
10904         
10905         this.remove(data);
10906         
10907         this.insert(newIndex, data);
10908         
10909     }
10910 });/*
10911  * Based on:
10912  * Ext JS Library 1.1.1
10913  * Copyright(c) 2006-2007, Ext JS, LLC.
10914  *
10915  * Originally Released Under LGPL - original licence link has changed is not relivant.
10916  *
10917  * Fork - LGPL
10918  * <script type="text/javascript">
10919  */
10920
10921 /**
10922  * @class Roo.data.SimpleStore
10923  * @extends Roo.data.Store
10924  * Small helper class to make creating Stores from Array data easier.
10925  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10926  * @cfg {Array} fields An array of field definition objects, or field name strings.
10927  * @cfg {Array} data The multi-dimensional array of data
10928  * @constructor
10929  * @param {Object} config
10930  */
10931 Roo.data.SimpleStore = function(config){
10932     Roo.data.SimpleStore.superclass.constructor.call(this, {
10933         isLocal : true,
10934         reader: new Roo.data.ArrayReader({
10935                 id: config.id
10936             },
10937             Roo.data.Record.create(config.fields)
10938         ),
10939         proxy : new Roo.data.MemoryProxy(config.data)
10940     });
10941     this.load();
10942 };
10943 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10944  * Based on:
10945  * Ext JS Library 1.1.1
10946  * Copyright(c) 2006-2007, Ext JS, LLC.
10947  *
10948  * Originally Released Under LGPL - original licence link has changed is not relivant.
10949  *
10950  * Fork - LGPL
10951  * <script type="text/javascript">
10952  */
10953
10954 /**
10955 /**
10956  * @extends Roo.data.Store
10957  * @class Roo.data.JsonStore
10958  * Small helper class to make creating Stores for JSON data easier. <br/>
10959 <pre><code>
10960 var store = new Roo.data.JsonStore({
10961     url: 'get-images.php',
10962     root: 'images',
10963     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10964 });
10965 </code></pre>
10966  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10967  * JsonReader and HttpProxy (unless inline data is provided).</b>
10968  * @cfg {Array} fields An array of field definition objects, or field name strings.
10969  * @constructor
10970  * @param {Object} config
10971  */
10972 Roo.data.JsonStore = function(c){
10973     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10974         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10975         reader: new Roo.data.JsonReader(c, c.fields)
10976     }));
10977 };
10978 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10979  * Based on:
10980  * Ext JS Library 1.1.1
10981  * Copyright(c) 2006-2007, Ext JS, LLC.
10982  *
10983  * Originally Released Under LGPL - original licence link has changed is not relivant.
10984  *
10985  * Fork - LGPL
10986  * <script type="text/javascript">
10987  */
10988
10989  
10990 Roo.data.Field = function(config){
10991     if(typeof config == "string"){
10992         config = {name: config};
10993     }
10994     Roo.apply(this, config);
10995     
10996     if(!this.type){
10997         this.type = "auto";
10998     }
10999     
11000     var st = Roo.data.SortTypes;
11001     // named sortTypes are supported, here we look them up
11002     if(typeof this.sortType == "string"){
11003         this.sortType = st[this.sortType];
11004     }
11005     
11006     // set default sortType for strings and dates
11007     if(!this.sortType){
11008         switch(this.type){
11009             case "string":
11010                 this.sortType = st.asUCString;
11011                 break;
11012             case "date":
11013                 this.sortType = st.asDate;
11014                 break;
11015             default:
11016                 this.sortType = st.none;
11017         }
11018     }
11019
11020     // define once
11021     var stripRe = /[\$,%]/g;
11022
11023     // prebuilt conversion function for this field, instead of
11024     // switching every time we're reading a value
11025     if(!this.convert){
11026         var cv, dateFormat = this.dateFormat;
11027         switch(this.type){
11028             case "":
11029             case "auto":
11030             case undefined:
11031                 cv = function(v){ return v; };
11032                 break;
11033             case "string":
11034                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11035                 break;
11036             case "int":
11037                 cv = function(v){
11038                     return v !== undefined && v !== null && v !== '' ?
11039                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11040                     };
11041                 break;
11042             case "float":
11043                 cv = function(v){
11044                     return v !== undefined && v !== null && v !== '' ?
11045                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11046                     };
11047                 break;
11048             case "bool":
11049             case "boolean":
11050                 cv = function(v){ return v === true || v === "true" || v == 1; };
11051                 break;
11052             case "date":
11053                 cv = function(v){
11054                     if(!v){
11055                         return '';
11056                     }
11057                     if(v instanceof Date){
11058                         return v;
11059                     }
11060                     if(dateFormat){
11061                         if(dateFormat == "timestamp"){
11062                             return new Date(v*1000);
11063                         }
11064                         return Date.parseDate(v, dateFormat);
11065                     }
11066                     var parsed = Date.parse(v);
11067                     return parsed ? new Date(parsed) : null;
11068                 };
11069              break;
11070             
11071         }
11072         this.convert = cv;
11073     }
11074 };
11075
11076 Roo.data.Field.prototype = {
11077     dateFormat: null,
11078     defaultValue: "",
11079     mapping: null,
11080     sortType : null,
11081     sortDir : "ASC"
11082 };/*
11083  * Based on:
11084  * Ext JS Library 1.1.1
11085  * Copyright(c) 2006-2007, Ext JS, LLC.
11086  *
11087  * Originally Released Under LGPL - original licence link has changed is not relivant.
11088  *
11089  * Fork - LGPL
11090  * <script type="text/javascript">
11091  */
11092  
11093 // Base class for reading structured data from a data source.  This class is intended to be
11094 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11095
11096 /**
11097  * @class Roo.data.DataReader
11098  * Base class for reading structured data from a data source.  This class is intended to be
11099  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11100  */
11101
11102 Roo.data.DataReader = function(meta, recordType){
11103     
11104     this.meta = meta;
11105     
11106     this.recordType = recordType instanceof Array ? 
11107         Roo.data.Record.create(recordType) : recordType;
11108 };
11109
11110 Roo.data.DataReader.prototype = {
11111      /**
11112      * Create an empty record
11113      * @param {Object} data (optional) - overlay some values
11114      * @return {Roo.data.Record} record created.
11115      */
11116     newRow :  function(d) {
11117         var da =  {};
11118         this.recordType.prototype.fields.each(function(c) {
11119             switch( c.type) {
11120                 case 'int' : da[c.name] = 0; break;
11121                 case 'date' : da[c.name] = new Date(); break;
11122                 case 'float' : da[c.name] = 0.0; break;
11123                 case 'boolean' : da[c.name] = false; break;
11124                 default : da[c.name] = ""; break;
11125             }
11126             
11127         });
11128         return new this.recordType(Roo.apply(da, d));
11129     }
11130     
11131 };/*
11132  * Based on:
11133  * Ext JS Library 1.1.1
11134  * Copyright(c) 2006-2007, Ext JS, LLC.
11135  *
11136  * Originally Released Under LGPL - original licence link has changed is not relivant.
11137  *
11138  * Fork - LGPL
11139  * <script type="text/javascript">
11140  */
11141
11142 /**
11143  * @class Roo.data.DataProxy
11144  * @extends Roo.data.Observable
11145  * This class is an abstract base class for implementations which provide retrieval of
11146  * unformatted data objects.<br>
11147  * <p>
11148  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11149  * (of the appropriate type which knows how to parse the data object) to provide a block of
11150  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11151  * <p>
11152  * Custom implementations must implement the load method as described in
11153  * {@link Roo.data.HttpProxy#load}.
11154  */
11155 Roo.data.DataProxy = function(){
11156     this.addEvents({
11157         /**
11158          * @event beforeload
11159          * Fires before a network request is made to retrieve a data object.
11160          * @param {Object} This DataProxy object.
11161          * @param {Object} params The params parameter to the load function.
11162          */
11163         beforeload : true,
11164         /**
11165          * @event load
11166          * Fires before the load method's callback is called.
11167          * @param {Object} This DataProxy object.
11168          * @param {Object} o The data object.
11169          * @param {Object} arg The callback argument object passed to the load function.
11170          */
11171         load : true,
11172         /**
11173          * @event loadexception
11174          * Fires if an Exception occurs during data retrieval.
11175          * @param {Object} This DataProxy object.
11176          * @param {Object} o The data object.
11177          * @param {Object} arg The callback argument object passed to the load function.
11178          * @param {Object} e The Exception.
11179          */
11180         loadexception : true
11181     });
11182     Roo.data.DataProxy.superclass.constructor.call(this);
11183 };
11184
11185 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11186
11187     /**
11188      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11189      */
11190 /*
11191  * Based on:
11192  * Ext JS Library 1.1.1
11193  * Copyright(c) 2006-2007, Ext JS, LLC.
11194  *
11195  * Originally Released Under LGPL - original licence link has changed is not relivant.
11196  *
11197  * Fork - LGPL
11198  * <script type="text/javascript">
11199  */
11200 /**
11201  * @class Roo.data.MemoryProxy
11202  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11203  * to the Reader when its load method is called.
11204  * @constructor
11205  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11206  */
11207 Roo.data.MemoryProxy = function(data){
11208     if (data.data) {
11209         data = data.data;
11210     }
11211     Roo.data.MemoryProxy.superclass.constructor.call(this);
11212     this.data = data;
11213 };
11214
11215 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11216     
11217     /**
11218      * Load data from the requested source (in this case an in-memory
11219      * data object passed to the constructor), read the data object into
11220      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11221      * process that block using the passed callback.
11222      * @param {Object} params This parameter is not used by the MemoryProxy class.
11223      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11224      * object into a block of Roo.data.Records.
11225      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11226      * The function must be passed <ul>
11227      * <li>The Record block object</li>
11228      * <li>The "arg" argument from the load function</li>
11229      * <li>A boolean success indicator</li>
11230      * </ul>
11231      * @param {Object} scope The scope in which to call the callback
11232      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11233      */
11234     load : function(params, reader, callback, scope, arg){
11235         params = params || {};
11236         var result;
11237         try {
11238             result = reader.readRecords(this.data);
11239         }catch(e){
11240             this.fireEvent("loadexception", this, arg, null, e);
11241             callback.call(scope, null, arg, false);
11242             return;
11243         }
11244         callback.call(scope, result, arg, true);
11245     },
11246     
11247     // private
11248     update : function(params, records){
11249         
11250     }
11251 });/*
11252  * Based on:
11253  * Ext JS Library 1.1.1
11254  * Copyright(c) 2006-2007, Ext JS, LLC.
11255  *
11256  * Originally Released Under LGPL - original licence link has changed is not relivant.
11257  *
11258  * Fork - LGPL
11259  * <script type="text/javascript">
11260  */
11261 /**
11262  * @class Roo.data.HttpProxy
11263  * @extends Roo.data.DataProxy
11264  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11265  * configured to reference a certain URL.<br><br>
11266  * <p>
11267  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11268  * from which the running page was served.<br><br>
11269  * <p>
11270  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11271  * <p>
11272  * Be aware that to enable the browser to parse an XML document, the server must set
11273  * the Content-Type header in the HTTP response to "text/xml".
11274  * @constructor
11275  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11276  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11277  * will be used to make the request.
11278  */
11279 Roo.data.HttpProxy = function(conn){
11280     Roo.data.HttpProxy.superclass.constructor.call(this);
11281     // is conn a conn config or a real conn?
11282     this.conn = conn;
11283     this.useAjax = !conn || !conn.events;
11284   
11285 };
11286
11287 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11288     // thse are take from connection...
11289     
11290     /**
11291      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11292      */
11293     /**
11294      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11295      * extra parameters to each request made by this object. (defaults to undefined)
11296      */
11297     /**
11298      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11299      *  to each request made by this object. (defaults to undefined)
11300      */
11301     /**
11302      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11303      */
11304     /**
11305      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11306      */
11307      /**
11308      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11309      * @type Boolean
11310      */
11311   
11312
11313     /**
11314      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11315      * @type Boolean
11316      */
11317     /**
11318      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11319      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11320      * a finer-grained basis than the DataProxy events.
11321      */
11322     getConnection : function(){
11323         return this.useAjax ? Roo.Ajax : this.conn;
11324     },
11325
11326     /**
11327      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11328      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11329      * process that block using the passed callback.
11330      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11331      * for the request to the remote server.
11332      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11333      * object into a block of Roo.data.Records.
11334      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11335      * The function must be passed <ul>
11336      * <li>The Record block object</li>
11337      * <li>The "arg" argument from the load function</li>
11338      * <li>A boolean success indicator</li>
11339      * </ul>
11340      * @param {Object} scope The scope in which to call the callback
11341      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11342      */
11343     load : function(params, reader, callback, scope, arg){
11344         if(this.fireEvent("beforeload", this, params) !== false){
11345             var  o = {
11346                 params : params || {},
11347                 request: {
11348                     callback : callback,
11349                     scope : scope,
11350                     arg : arg
11351                 },
11352                 reader: reader,
11353                 callback : this.loadResponse,
11354                 scope: this
11355             };
11356             if(this.useAjax){
11357                 Roo.applyIf(o, this.conn);
11358                 if(this.activeRequest){
11359                     Roo.Ajax.abort(this.activeRequest);
11360                 }
11361                 this.activeRequest = Roo.Ajax.request(o);
11362             }else{
11363                 this.conn.request(o);
11364             }
11365         }else{
11366             callback.call(scope||this, null, arg, false);
11367         }
11368     },
11369
11370     // private
11371     loadResponse : function(o, success, response){
11372         delete this.activeRequest;
11373         if(!success){
11374             this.fireEvent("loadexception", this, o, response);
11375             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11376             return;
11377         }
11378         var result;
11379         try {
11380             result = o.reader.read(response);
11381         }catch(e){
11382             this.fireEvent("loadexception", this, o, response, e);
11383             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11384             return;
11385         }
11386         
11387         this.fireEvent("load", this, o, o.request.arg);
11388         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11389     },
11390
11391     // private
11392     update : function(dataSet){
11393
11394     },
11395
11396     // private
11397     updateResponse : function(dataSet){
11398
11399     }
11400 });/*
11401  * Based on:
11402  * Ext JS Library 1.1.1
11403  * Copyright(c) 2006-2007, Ext JS, LLC.
11404  *
11405  * Originally Released Under LGPL - original licence link has changed is not relivant.
11406  *
11407  * Fork - LGPL
11408  * <script type="text/javascript">
11409  */
11410
11411 /**
11412  * @class Roo.data.ScriptTagProxy
11413  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11414  * other than the originating domain of the running page.<br><br>
11415  * <p>
11416  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
11417  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11418  * <p>
11419  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11420  * source code that is used as the source inside a &lt;script> tag.<br><br>
11421  * <p>
11422  * In order for the browser to process the returned data, the server must wrap the data object
11423  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11424  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11425  * depending on whether the callback name was passed:
11426  * <p>
11427  * <pre><code>
11428 boolean scriptTag = false;
11429 String cb = request.getParameter("callback");
11430 if (cb != null) {
11431     scriptTag = true;
11432     response.setContentType("text/javascript");
11433 } else {
11434     response.setContentType("application/x-json");
11435 }
11436 Writer out = response.getWriter();
11437 if (scriptTag) {
11438     out.write(cb + "(");
11439 }
11440 out.print(dataBlock.toJsonString());
11441 if (scriptTag) {
11442     out.write(");");
11443 }
11444 </pre></code>
11445  *
11446  * @constructor
11447  * @param {Object} config A configuration object.
11448  */
11449 Roo.data.ScriptTagProxy = function(config){
11450     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11451     Roo.apply(this, config);
11452     this.head = document.getElementsByTagName("head")[0];
11453 };
11454
11455 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11456
11457 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11458     /**
11459      * @cfg {String} url The URL from which to request the data object.
11460      */
11461     /**
11462      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11463      */
11464     timeout : 30000,
11465     /**
11466      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11467      * the server the name of the callback function set up by the load call to process the returned data object.
11468      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11469      * javascript output which calls this named function passing the data object as its only parameter.
11470      */
11471     callbackParam : "callback",
11472     /**
11473      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11474      * name to the request.
11475      */
11476     nocache : true,
11477
11478     /**
11479      * Load data from the configured URL, read the data object into
11480      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11481      * process that block using the passed callback.
11482      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11483      * for the request to the remote server.
11484      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11485      * object into a block of Roo.data.Records.
11486      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11487      * The function must be passed <ul>
11488      * <li>The Record block object</li>
11489      * <li>The "arg" argument from the load function</li>
11490      * <li>A boolean success indicator</li>
11491      * </ul>
11492      * @param {Object} scope The scope in which to call the callback
11493      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11494      */
11495     load : function(params, reader, callback, scope, arg){
11496         if(this.fireEvent("beforeload", this, params) !== false){
11497
11498             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11499
11500             var url = this.url;
11501             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11502             if(this.nocache){
11503                 url += "&_dc=" + (new Date().getTime());
11504             }
11505             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11506             var trans = {
11507                 id : transId,
11508                 cb : "stcCallback"+transId,
11509                 scriptId : "stcScript"+transId,
11510                 params : params,
11511                 arg : arg,
11512                 url : url,
11513                 callback : callback,
11514                 scope : scope,
11515                 reader : reader
11516             };
11517             var conn = this;
11518
11519             window[trans.cb] = function(o){
11520                 conn.handleResponse(o, trans);
11521             };
11522
11523             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11524
11525             if(this.autoAbort !== false){
11526                 this.abort();
11527             }
11528
11529             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11530
11531             var script = document.createElement("script");
11532             script.setAttribute("src", url);
11533             script.setAttribute("type", "text/javascript");
11534             script.setAttribute("id", trans.scriptId);
11535             this.head.appendChild(script);
11536
11537             this.trans = trans;
11538         }else{
11539             callback.call(scope||this, null, arg, false);
11540         }
11541     },
11542
11543     // private
11544     isLoading : function(){
11545         return this.trans ? true : false;
11546     },
11547
11548     /**
11549      * Abort the current server request.
11550      */
11551     abort : function(){
11552         if(this.isLoading()){
11553             this.destroyTrans(this.trans);
11554         }
11555     },
11556
11557     // private
11558     destroyTrans : function(trans, isLoaded){
11559         this.head.removeChild(document.getElementById(trans.scriptId));
11560         clearTimeout(trans.timeoutId);
11561         if(isLoaded){
11562             window[trans.cb] = undefined;
11563             try{
11564                 delete window[trans.cb];
11565             }catch(e){}
11566         }else{
11567             // if hasn't been loaded, wait for load to remove it to prevent script error
11568             window[trans.cb] = function(){
11569                 window[trans.cb] = undefined;
11570                 try{
11571                     delete window[trans.cb];
11572                 }catch(e){}
11573             };
11574         }
11575     },
11576
11577     // private
11578     handleResponse : function(o, trans){
11579         this.trans = false;
11580         this.destroyTrans(trans, true);
11581         var result;
11582         try {
11583             result = trans.reader.readRecords(o);
11584         }catch(e){
11585             this.fireEvent("loadexception", this, o, trans.arg, e);
11586             trans.callback.call(trans.scope||window, null, trans.arg, false);
11587             return;
11588         }
11589         this.fireEvent("load", this, o, trans.arg);
11590         trans.callback.call(trans.scope||window, result, trans.arg, true);
11591     },
11592
11593     // private
11594     handleFailure : function(trans){
11595         this.trans = false;
11596         this.destroyTrans(trans, false);
11597         this.fireEvent("loadexception", this, null, trans.arg);
11598         trans.callback.call(trans.scope||window, null, trans.arg, false);
11599     }
11600 });/*
11601  * Based on:
11602  * Ext JS Library 1.1.1
11603  * Copyright(c) 2006-2007, Ext JS, LLC.
11604  *
11605  * Originally Released Under LGPL - original licence link has changed is not relivant.
11606  *
11607  * Fork - LGPL
11608  * <script type="text/javascript">
11609  */
11610
11611 /**
11612  * @class Roo.data.JsonReader
11613  * @extends Roo.data.DataReader
11614  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11615  * based on mappings in a provided Roo.data.Record constructor.
11616  * 
11617  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11618  * in the reply previously. 
11619  * 
11620  * <p>
11621  * Example code:
11622  * <pre><code>
11623 var RecordDef = Roo.data.Record.create([
11624     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11625     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11626 ]);
11627 var myReader = new Roo.data.JsonReader({
11628     totalProperty: "results",    // The property which contains the total dataset size (optional)
11629     root: "rows",                // The property which contains an Array of row objects
11630     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11631 }, RecordDef);
11632 </code></pre>
11633  * <p>
11634  * This would consume a JSON file like this:
11635  * <pre><code>
11636 { 'results': 2, 'rows': [
11637     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11638     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11639 }
11640 </code></pre>
11641  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11642  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11643  * paged from the remote server.
11644  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11645  * @cfg {String} root name of the property which contains the Array of row objects.
11646  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11647  * @cfg {Array} fields Array of field definition objects
11648  * @constructor
11649  * Create a new JsonReader
11650  * @param {Object} meta Metadata configuration options
11651  * @param {Object} recordType Either an Array of field definition objects,
11652  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11653  */
11654 Roo.data.JsonReader = function(meta, recordType){
11655     
11656     meta = meta || {};
11657     // set some defaults:
11658     Roo.applyIf(meta, {
11659         totalProperty: 'total',
11660         successProperty : 'success',
11661         root : 'data',
11662         id : 'id'
11663     });
11664     
11665     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11666 };
11667 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11668     
11669     /**
11670      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11671      * Used by Store query builder to append _requestMeta to params.
11672      * 
11673      */
11674     metaFromRemote : false,
11675     /**
11676      * This method is only used by a DataProxy which has retrieved data from a remote server.
11677      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11678      * @return {Object} data A data block which is used by an Roo.data.Store object as
11679      * a cache of Roo.data.Records.
11680      */
11681     read : function(response){
11682         var json = response.responseText;
11683        
11684         var o = /* eval:var:o */ eval("("+json+")");
11685         if(!o) {
11686             throw {message: "JsonReader.read: Json object not found"};
11687         }
11688         
11689         if(o.metaData){
11690             
11691             delete this.ef;
11692             this.metaFromRemote = true;
11693             this.meta = o.metaData;
11694             this.recordType = Roo.data.Record.create(o.metaData.fields);
11695             this.onMetaChange(this.meta, this.recordType, o);
11696         }
11697         return this.readRecords(o);
11698     },
11699
11700     // private function a store will implement
11701     onMetaChange : function(meta, recordType, o){
11702
11703     },
11704
11705     /**
11706          * @ignore
11707          */
11708     simpleAccess: function(obj, subsc) {
11709         return obj[subsc];
11710     },
11711
11712         /**
11713          * @ignore
11714          */
11715     getJsonAccessor: function(){
11716         var re = /[\[\.]/;
11717         return function(expr) {
11718             try {
11719                 return(re.test(expr))
11720                     ? new Function("obj", "return obj." + expr)
11721                     : function(obj){
11722                         return obj[expr];
11723                     };
11724             } catch(e){}
11725             return Roo.emptyFn;
11726         };
11727     }(),
11728
11729     /**
11730      * Create a data block containing Roo.data.Records from an XML document.
11731      * @param {Object} o An object which contains an Array of row objects in the property specified
11732      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11733      * which contains the total size of the dataset.
11734      * @return {Object} data A data block which is used by an Roo.data.Store object as
11735      * a cache of Roo.data.Records.
11736      */
11737     readRecords : function(o){
11738         /**
11739          * After any data loads, the raw JSON data is available for further custom processing.
11740          * @type Object
11741          */
11742         this.o = o;
11743         var s = this.meta, Record = this.recordType,
11744             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11745
11746 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11747         if (!this.ef) {
11748             if(s.totalProperty) {
11749                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11750                 }
11751                 if(s.successProperty) {
11752                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11753                 }
11754                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11755                 if (s.id) {
11756                         var g = this.getJsonAccessor(s.id);
11757                         this.getId = function(rec) {
11758                                 var r = g(rec);  
11759                                 return (r === undefined || r === "") ? null : r;
11760                         };
11761                 } else {
11762                         this.getId = function(){return null;};
11763                 }
11764             this.ef = [];
11765             for(var jj = 0; jj < fl; jj++){
11766                 f = fi[jj];
11767                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11768                 this.ef[jj] = this.getJsonAccessor(map);
11769             }
11770         }
11771
11772         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11773         if(s.totalProperty){
11774             var vt = parseInt(this.getTotal(o), 10);
11775             if(!isNaN(vt)){
11776                 totalRecords = vt;
11777             }
11778         }
11779         if(s.successProperty){
11780             var vs = this.getSuccess(o);
11781             if(vs === false || vs === 'false'){
11782                 success = false;
11783             }
11784         }
11785         var records = [];
11786         for(var i = 0; i < c; i++){
11787                 var n = root[i];
11788             var values = {};
11789             var id = this.getId(n);
11790             for(var j = 0; j < fl; j++){
11791                 f = fi[j];
11792             var v = this.ef[j](n);
11793             if (!f.convert) {
11794                 Roo.log('missing convert for ' + f.name);
11795                 Roo.log(f);
11796                 continue;
11797             }
11798             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11799             }
11800             var record = new Record(values, id);
11801             record.json = n;
11802             records[i] = record;
11803         }
11804         return {
11805             raw : o,
11806             success : success,
11807             records : records,
11808             totalRecords : totalRecords
11809         };
11810     }
11811 });/*
11812  * Based on:
11813  * Ext JS Library 1.1.1
11814  * Copyright(c) 2006-2007, Ext JS, LLC.
11815  *
11816  * Originally Released Under LGPL - original licence link has changed is not relivant.
11817  *
11818  * Fork - LGPL
11819  * <script type="text/javascript">
11820  */
11821
11822 /**
11823  * @class Roo.data.ArrayReader
11824  * @extends Roo.data.DataReader
11825  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11826  * Each element of that Array represents a row of data fields. The
11827  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11828  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11829  * <p>
11830  * Example code:.
11831  * <pre><code>
11832 var RecordDef = Roo.data.Record.create([
11833     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11834     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11835 ]);
11836 var myReader = new Roo.data.ArrayReader({
11837     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11838 }, RecordDef);
11839 </code></pre>
11840  * <p>
11841  * This would consume an Array like this:
11842  * <pre><code>
11843 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11844   </code></pre>
11845  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11846  * @constructor
11847  * Create a new JsonReader
11848  * @param {Object} meta Metadata configuration options.
11849  * @param {Object} recordType Either an Array of field definition objects
11850  * as specified to {@link Roo.data.Record#create},
11851  * or an {@link Roo.data.Record} object
11852  * created using {@link Roo.data.Record#create}.
11853  */
11854 Roo.data.ArrayReader = function(meta, recordType){
11855     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11856 };
11857
11858 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11859     /**
11860      * Create a data block containing Roo.data.Records from an XML document.
11861      * @param {Object} o An Array of row objects which represents the dataset.
11862      * @return {Object} data A data block which is used by an Roo.data.Store object as
11863      * a cache of Roo.data.Records.
11864      */
11865     readRecords : function(o){
11866         var sid = this.meta ? this.meta.id : null;
11867         var recordType = this.recordType, fields = recordType.prototype.fields;
11868         var records = [];
11869         var root = o;
11870             for(var i = 0; i < root.length; i++){
11871                     var n = root[i];
11872                 var values = {};
11873                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11874                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11875                 var f = fields.items[j];
11876                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11877                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11878                 v = f.convert(v);
11879                 values[f.name] = v;
11880             }
11881                 var record = new recordType(values, id);
11882                 record.json = n;
11883                 records[records.length] = record;
11884             }
11885             return {
11886                 records : records,
11887                 totalRecords : records.length
11888             };
11889     }
11890 });/*
11891  * - LGPL
11892  * * 
11893  */
11894
11895 /**
11896  * @class Roo.bootstrap.ComboBox
11897  * @extends Roo.bootstrap.TriggerField
11898  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11899  * @cfg {Boolean} append (true|false) default false
11900  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11901  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11902  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11903  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11904  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11905  * @cfg {Boolean} animate default true
11906  * @cfg {Boolean} emptyResultText only for touch device
11907  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11908  * @constructor
11909  * Create a new ComboBox.
11910  * @param {Object} config Configuration options
11911  */
11912 Roo.bootstrap.ComboBox = function(config){
11913     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11914     this.addEvents({
11915         /**
11916          * @event expand
11917          * Fires when the dropdown list is expanded
11918              * @param {Roo.bootstrap.ComboBox} combo This combo box
11919              */
11920         'expand' : true,
11921         /**
11922          * @event collapse
11923          * Fires when the dropdown list is collapsed
11924              * @param {Roo.bootstrap.ComboBox} combo This combo box
11925              */
11926         'collapse' : true,
11927         /**
11928          * @event beforeselect
11929          * Fires before a list item is selected. Return false to cancel the selection.
11930              * @param {Roo.bootstrap.ComboBox} combo This combo box
11931              * @param {Roo.data.Record} record The data record returned from the underlying store
11932              * @param {Number} index The index of the selected item in the dropdown list
11933              */
11934         'beforeselect' : true,
11935         /**
11936          * @event select
11937          * Fires when a list item is selected
11938              * @param {Roo.bootstrap.ComboBox} combo This combo box
11939              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11940              * @param {Number} index The index of the selected item in the dropdown list
11941              */
11942         'select' : true,
11943         /**
11944          * @event beforequery
11945          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11946          * The event object passed has these properties:
11947              * @param {Roo.bootstrap.ComboBox} combo This combo box
11948              * @param {String} query The query
11949              * @param {Boolean} forceAll true to force "all" query
11950              * @param {Boolean} cancel true to cancel the query
11951              * @param {Object} e The query event object
11952              */
11953         'beforequery': true,
11954          /**
11955          * @event add
11956          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11957              * @param {Roo.bootstrap.ComboBox} combo This combo box
11958              */
11959         'add' : true,
11960         /**
11961          * @event edit
11962          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11963              * @param {Roo.bootstrap.ComboBox} combo This combo box
11964              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11965              */
11966         'edit' : true,
11967         /**
11968          * @event remove
11969          * Fires when the remove value from the combobox array
11970              * @param {Roo.bootstrap.ComboBox} combo This combo box
11971              */
11972         'remove' : true,
11973         /**
11974          * @event afterremove
11975          * Fires when the remove value from the combobox array
11976              * @param {Roo.bootstrap.ComboBox} combo This combo box
11977              */
11978         'afterremove' : true,
11979         /**
11980          * @event specialfilter
11981          * Fires when specialfilter
11982             * @param {Roo.bootstrap.ComboBox} combo This combo box
11983             */
11984         'specialfilter' : true,
11985         /**
11986          * @event tick
11987          * Fires when tick the element
11988             * @param {Roo.bootstrap.ComboBox} combo This combo box
11989             */
11990         'tick' : true,
11991         /**
11992          * @event touchviewdisplay
11993          * Fires when touch view require special display (default is using displayField)
11994             * @param {Roo.bootstrap.ComboBox} combo This combo box
11995             * @param {Object} cfg set html .
11996             */
11997         'touchviewdisplay' : true
11998         
11999     });
12000     
12001     this.item = [];
12002     this.tickItems = [];
12003     
12004     this.selectedIndex = -1;
12005     if(this.mode == 'local'){
12006         if(config.queryDelay === undefined){
12007             this.queryDelay = 10;
12008         }
12009         if(config.minChars === undefined){
12010             this.minChars = 0;
12011         }
12012     }
12013 };
12014
12015 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12016      
12017     /**
12018      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12019      * rendering into an Roo.Editor, defaults to false)
12020      */
12021     /**
12022      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12023      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12024      */
12025     /**
12026      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12027      */
12028     /**
12029      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12030      * the dropdown list (defaults to undefined, with no header element)
12031      */
12032
12033      /**
12034      * @cfg {String/Roo.Template} tpl The template to use to render the output
12035      */
12036      
12037      /**
12038      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12039      */
12040     listWidth: undefined,
12041     /**
12042      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12043      * mode = 'remote' or 'text' if mode = 'local')
12044      */
12045     displayField: undefined,
12046     
12047     /**
12048      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12049      * mode = 'remote' or 'value' if mode = 'local'). 
12050      * Note: use of a valueField requires the user make a selection
12051      * in order for a value to be mapped.
12052      */
12053     valueField: undefined,
12054     /**
12055      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12056      */
12057     modalTitle : '',
12058     
12059     /**
12060      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12061      * field's data value (defaults to the underlying DOM element's name)
12062      */
12063     hiddenName: undefined,
12064     /**
12065      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12066      */
12067     listClass: '',
12068     /**
12069      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12070      */
12071     selectedClass: 'active',
12072     
12073     /**
12074      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12075      */
12076     shadow:'sides',
12077     /**
12078      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12079      * anchor positions (defaults to 'tl-bl')
12080      */
12081     listAlign: 'tl-bl?',
12082     /**
12083      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12084      */
12085     maxHeight: 300,
12086     /**
12087      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12088      * query specified by the allQuery config option (defaults to 'query')
12089      */
12090     triggerAction: 'query',
12091     /**
12092      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12093      * (defaults to 4, does not apply if editable = false)
12094      */
12095     minChars : 4,
12096     /**
12097      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12098      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12099      */
12100     typeAhead: false,
12101     /**
12102      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12103      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12104      */
12105     queryDelay: 500,
12106     /**
12107      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12108      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12109      */
12110     pageSize: 0,
12111     /**
12112      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12113      * when editable = true (defaults to false)
12114      */
12115     selectOnFocus:false,
12116     /**
12117      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12118      */
12119     queryParam: 'query',
12120     /**
12121      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12122      * when mode = 'remote' (defaults to 'Loading...')
12123      */
12124     loadingText: 'Loading...',
12125     /**
12126      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12127      */
12128     resizable: false,
12129     /**
12130      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12131      */
12132     handleHeight : 8,
12133     /**
12134      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12135      * traditional select (defaults to true)
12136      */
12137     editable: true,
12138     /**
12139      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12140      */
12141     allQuery: '',
12142     /**
12143      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12144      */
12145     mode: 'remote',
12146     /**
12147      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12148      * listWidth has a higher value)
12149      */
12150     minListWidth : 70,
12151     /**
12152      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12153      * allow the user to set arbitrary text into the field (defaults to false)
12154      */
12155     forceSelection:false,
12156     /**
12157      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12158      * if typeAhead = true (defaults to 250)
12159      */
12160     typeAheadDelay : 250,
12161     /**
12162      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12163      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12164      */
12165     valueNotFoundText : undefined,
12166     /**
12167      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12168      */
12169     blockFocus : false,
12170     
12171     /**
12172      * @cfg {Boolean} disableClear Disable showing of clear button.
12173      */
12174     disableClear : false,
12175     /**
12176      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12177      */
12178     alwaysQuery : false,
12179     
12180     /**
12181      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12182      */
12183     multiple : false,
12184     
12185     /**
12186      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12187      */
12188     invalidClass : "has-warning",
12189     
12190     /**
12191      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12192      */
12193     validClass : "has-success",
12194     
12195     /**
12196      * @cfg {Boolean} specialFilter (true|false) special filter default false
12197      */
12198     specialFilter : false,
12199     
12200     /**
12201      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12202      */
12203     mobileTouchView : true,
12204     
12205     //private
12206     addicon : false,
12207     editicon: false,
12208     
12209     page: 0,
12210     hasQuery: false,
12211     append: false,
12212     loadNext: false,
12213     autoFocus : true,
12214     tickable : false,
12215     btnPosition : 'right',
12216     triggerList : true,
12217     showToggleBtn : true,
12218     animate : true,
12219     emptyResultText: 'Empty',
12220     triggerText : 'Select',
12221     
12222     // element that contains real text value.. (when hidden is used..)
12223     
12224     getAutoCreate : function()
12225     {
12226         var cfg = false;
12227         
12228         /*
12229          * Touch Devices
12230          */
12231         
12232         if(Roo.isTouch && this.mobileTouchView){
12233             cfg = this.getAutoCreateTouchView();
12234             return cfg;;
12235         }
12236         
12237         /*
12238          *  Normal ComboBox
12239          */
12240         if(!this.tickable){
12241             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12242             return cfg;
12243         }
12244         
12245         /*
12246          *  ComboBox with tickable selections
12247          */
12248              
12249         var align = this.labelAlign || this.parentLabelAlign();
12250         
12251         cfg = {
12252             cls : 'form-group roo-combobox-tickable' //input-group
12253         };
12254         
12255         var buttons = {
12256             tag : 'div',
12257             cls : 'tickable-buttons',
12258             cn : [
12259                 {
12260                     tag : 'button',
12261                     type : 'button',
12262                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12263                     html : this.triggerText
12264                 },
12265                 {
12266                     tag : 'button',
12267                     type : 'button',
12268                     name : 'ok',
12269                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12270                     html : 'Done'
12271                 },
12272                 {
12273                     tag : 'button',
12274                     type : 'button',
12275                     name : 'cancel',
12276                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12277                     html : 'Cancel'
12278                 }
12279             ]
12280         };
12281         
12282         if(this.editable){
12283             buttons.cn.unshift({
12284                 tag: 'input',
12285                 cls: 'roo-select2-search-field-input'
12286             });
12287         }
12288         
12289         var _this = this;
12290         
12291         Roo.each(buttons.cn, function(c){
12292             if (_this.size) {
12293                 c.cls += ' btn-' + _this.size;
12294             }
12295
12296             if (_this.disabled) {
12297                 c.disabled = true;
12298             }
12299         });
12300         
12301         var box = {
12302             tag: 'div',
12303             cn: [
12304                 {
12305                     tag: 'input',
12306                     type : 'hidden',
12307                     cls: 'form-hidden-field'
12308                 },
12309                 {
12310                     tag: 'ul',
12311                     cls: 'roo-select2-choices',
12312                     cn:[
12313                         {
12314                             tag: 'li',
12315                             cls: 'roo-select2-search-field',
12316                             cn: [
12317
12318                                 buttons
12319                             ]
12320                         }
12321                     ]
12322                 }
12323             ]
12324         };
12325         
12326         var combobox = {
12327             cls: 'roo-select2-container input-group roo-select2-container-multi',
12328             cn: [
12329                 box
12330 //                {
12331 //                    tag: 'ul',
12332 //                    cls: 'typeahead typeahead-long dropdown-menu',
12333 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12334 //                }
12335             ]
12336         };
12337         
12338         if(this.hasFeedback && !this.allowBlank){
12339             
12340             var feedback = {
12341                 tag: 'span',
12342                 cls: 'glyphicon form-control-feedback'
12343             };
12344
12345             combobox.cn.push(feedback);
12346         }
12347         
12348         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12349             
12350 //                Roo.log("left and has label");
12351             cfg.cn = [
12352                 {
12353                     tag : 'i',
12354                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12355                     tooltip : 'This field is required'
12356                 },
12357                 {
12358                     tag: 'label',
12359                     'for' :  id,
12360                     cls : 'control-label col-sm-' + this.labelWidth,
12361                     html : this.fieldLabel
12362
12363                 },
12364                 {
12365                     cls : "col-sm-" + (12 - this.labelWidth), 
12366                     cn: [
12367                         combobox
12368                     ]
12369                 }
12370
12371             ];
12372
12373             if(this.indicatorpos == 'right'){
12374                 
12375                 cfg.cn = [
12376                     {
12377                         tag: 'label',
12378                         'for' :  id,
12379                         cls : 'control-label col-sm-' + this.labelWidth,
12380                         html : this.fieldLabel
12381
12382                     },
12383                     {
12384                         tag : 'i',
12385                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12386                         tooltip : 'This field is required'
12387                     },
12388                     {
12389                         cls : "col-sm-" + (12 - this.labelWidth), 
12390                         cn: [
12391                             combobox
12392                         ]
12393                     }
12394
12395                 ];
12396             
12397             }
12398                 
12399                 
12400         } else if ( this.fieldLabel.length) {
12401 //                Roo.log(" label");
12402                  cfg.cn = [
12403                     {
12404                         tag : 'i',
12405                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12406                         tooltip : 'This field is required'
12407                     },
12408                     {
12409                         tag: 'label',
12410                         //cls : 'input-group-addon',
12411                         html : this.fieldLabel
12412                         
12413                     },
12414                     
12415                     combobox
12416                     
12417                 ];
12418                 
12419                 if(this.indicatorpos == 'right'){
12420                     
12421                     cfg.cn = [
12422                         {
12423                             tag: 'label',
12424                             //cls : 'input-group-addon',
12425                             html : this.fieldLabel
12426
12427                         },
12428                         
12429                         {
12430                             tag : 'i',
12431                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12432                             tooltip : 'This field is required'
12433                         },
12434                         
12435                         combobox
12436
12437                     ];
12438                 
12439                 }
12440
12441         } else {
12442             
12443 //                Roo.log(" no label && no align");
12444                 cfg = combobox
12445                      
12446                 
12447         }
12448          
12449         var settings=this;
12450         ['xs','sm','md','lg'].map(function(size){
12451             if (settings[size]) {
12452                 cfg.cls += ' col-' + size + '-' + settings[size];
12453             }
12454         });
12455         
12456         return cfg;
12457         
12458     },
12459     
12460     _initEventsCalled : false,
12461     
12462     // private
12463     initEvents: function()
12464     {
12465         
12466         if (this._initEventsCalled) { // as we call render... prevent looping...
12467             return;
12468         }
12469         this._initEventsCalled = true;
12470         
12471         if (!this.store) {
12472             throw "can not find store for combo";
12473         }
12474         
12475         this.store = Roo.factory(this.store, Roo.data);
12476         
12477         // if we are building from html. then this element is so complex, that we can not really
12478         // use the rendered HTML.
12479         // so we have to trash and replace the previous code.
12480         if (Roo.XComponent.build_from_html) {
12481             
12482             // remove this element....
12483             var e = this.el.dom, k=0;
12484             while (e ) { e = e.previousSibling;  ++k;}
12485
12486             this.el.remove();
12487             
12488             this.el=false;
12489             this.rendered = false;
12490             
12491             this.render(this.parent().getChildContainer(true), k);
12492             
12493             
12494             
12495         }
12496         
12497         
12498         /*
12499          * Touch Devices
12500          */
12501         
12502         if(Roo.isTouch && this.mobileTouchView){
12503             this.initTouchView();
12504             return;
12505         }
12506         
12507         if(this.tickable){
12508             this.initTickableEvents();
12509             return;
12510         }
12511         
12512         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12513         
12514         if(this.hiddenName){
12515             
12516             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12517             
12518             this.hiddenField.dom.value =
12519                 this.hiddenValue !== undefined ? this.hiddenValue :
12520                 this.value !== undefined ? this.value : '';
12521
12522             // prevent input submission
12523             this.el.dom.removeAttribute('name');
12524             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12525              
12526              
12527         }
12528         //if(Roo.isGecko){
12529         //    this.el.dom.setAttribute('autocomplete', 'off');
12530         //}
12531         
12532         var cls = 'x-combo-list';
12533         
12534         //this.list = new Roo.Layer({
12535         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12536         //});
12537         
12538         var _this = this;
12539         
12540         (function(){
12541             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12542             _this.list.setWidth(lw);
12543         }).defer(100);
12544         
12545         this.list.on('mouseover', this.onViewOver, this);
12546         this.list.on('mousemove', this.onViewMove, this);
12547         
12548         this.list.on('scroll', this.onViewScroll, this);
12549         
12550         /*
12551         this.list.swallowEvent('mousewheel');
12552         this.assetHeight = 0;
12553
12554         if(this.title){
12555             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12556             this.assetHeight += this.header.getHeight();
12557         }
12558
12559         this.innerList = this.list.createChild({cls:cls+'-inner'});
12560         this.innerList.on('mouseover', this.onViewOver, this);
12561         this.innerList.on('mousemove', this.onViewMove, this);
12562         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12563         
12564         if(this.allowBlank && !this.pageSize && !this.disableClear){
12565             this.footer = this.list.createChild({cls:cls+'-ft'});
12566             this.pageTb = new Roo.Toolbar(this.footer);
12567            
12568         }
12569         if(this.pageSize){
12570             this.footer = this.list.createChild({cls:cls+'-ft'});
12571             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12572                     {pageSize: this.pageSize});
12573             
12574         }
12575         
12576         if (this.pageTb && this.allowBlank && !this.disableClear) {
12577             var _this = this;
12578             this.pageTb.add(new Roo.Toolbar.Fill(), {
12579                 cls: 'x-btn-icon x-btn-clear',
12580                 text: '&#160;',
12581                 handler: function()
12582                 {
12583                     _this.collapse();
12584                     _this.clearValue();
12585                     _this.onSelect(false, -1);
12586                 }
12587             });
12588         }
12589         if (this.footer) {
12590             this.assetHeight += this.footer.getHeight();
12591         }
12592         */
12593             
12594         if(!this.tpl){
12595             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12596         }
12597
12598         this.view = new Roo.View(this.list, this.tpl, {
12599             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12600         });
12601         //this.view.wrapEl.setDisplayed(false);
12602         this.view.on('click', this.onViewClick, this);
12603         
12604         
12605         
12606         this.store.on('beforeload', this.onBeforeLoad, this);
12607         this.store.on('load', this.onLoad, this);
12608         this.store.on('loadexception', this.onLoadException, this);
12609         /*
12610         if(this.resizable){
12611             this.resizer = new Roo.Resizable(this.list,  {
12612                pinned:true, handles:'se'
12613             });
12614             this.resizer.on('resize', function(r, w, h){
12615                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12616                 this.listWidth = w;
12617                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12618                 this.restrictHeight();
12619             }, this);
12620             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12621         }
12622         */
12623         if(!this.editable){
12624             this.editable = true;
12625             this.setEditable(false);
12626         }
12627         
12628         /*
12629         
12630         if (typeof(this.events.add.listeners) != 'undefined') {
12631             
12632             this.addicon = this.wrap.createChild(
12633                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12634        
12635             this.addicon.on('click', function(e) {
12636                 this.fireEvent('add', this);
12637             }, this);
12638         }
12639         if (typeof(this.events.edit.listeners) != 'undefined') {
12640             
12641             this.editicon = this.wrap.createChild(
12642                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12643             if (this.addicon) {
12644                 this.editicon.setStyle('margin-left', '40px');
12645             }
12646             this.editicon.on('click', function(e) {
12647                 
12648                 // we fire even  if inothing is selected..
12649                 this.fireEvent('edit', this, this.lastData );
12650                 
12651             }, this);
12652         }
12653         */
12654         
12655         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12656             "up" : function(e){
12657                 this.inKeyMode = true;
12658                 this.selectPrev();
12659             },
12660
12661             "down" : function(e){
12662                 if(!this.isExpanded()){
12663                     this.onTriggerClick();
12664                 }else{
12665                     this.inKeyMode = true;
12666                     this.selectNext();
12667                 }
12668             },
12669
12670             "enter" : function(e){
12671 //                this.onViewClick();
12672                 //return true;
12673                 this.collapse();
12674                 
12675                 if(this.fireEvent("specialkey", this, e)){
12676                     this.onViewClick(false);
12677                 }
12678                 
12679                 return true;
12680             },
12681
12682             "esc" : function(e){
12683                 this.collapse();
12684             },
12685
12686             "tab" : function(e){
12687                 this.collapse();
12688                 
12689                 if(this.fireEvent("specialkey", this, e)){
12690                     this.onViewClick(false);
12691                 }
12692                 
12693                 return true;
12694             },
12695
12696             scope : this,
12697
12698             doRelay : function(foo, bar, hname){
12699                 if(hname == 'down' || this.scope.isExpanded()){
12700                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12701                 }
12702                 return true;
12703             },
12704
12705             forceKeyDown: true
12706         });
12707         
12708         
12709         this.queryDelay = Math.max(this.queryDelay || 10,
12710                 this.mode == 'local' ? 10 : 250);
12711         
12712         
12713         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12714         
12715         if(this.typeAhead){
12716             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12717         }
12718         if(this.editable !== false){
12719             this.inputEl().on("keyup", this.onKeyUp, this);
12720         }
12721         if(this.forceSelection){
12722             this.inputEl().on('blur', this.doForce, this);
12723         }
12724         
12725         if(this.multiple){
12726             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12727             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12728         }
12729     },
12730     
12731     initTickableEvents: function()
12732     {   
12733         this.createList();
12734         
12735         if(this.hiddenName){
12736             
12737             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12738             
12739             this.hiddenField.dom.value =
12740                 this.hiddenValue !== undefined ? this.hiddenValue :
12741                 this.value !== undefined ? this.value : '';
12742
12743             // prevent input submission
12744             this.el.dom.removeAttribute('name');
12745             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12746              
12747              
12748         }
12749         
12750 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12751         
12752         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12753         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12754         if(this.triggerList){
12755             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12756         }
12757          
12758         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12759         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12760         
12761         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12762         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12763         
12764         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12765         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12766         
12767         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12768         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12769         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12770         
12771         this.okBtn.hide();
12772         this.cancelBtn.hide();
12773         
12774         var _this = this;
12775         
12776         (function(){
12777             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12778             _this.list.setWidth(lw);
12779         }).defer(100);
12780         
12781         this.list.on('mouseover', this.onViewOver, this);
12782         this.list.on('mousemove', this.onViewMove, this);
12783         
12784         this.list.on('scroll', this.onViewScroll, this);
12785         
12786         if(!this.tpl){
12787             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12788         }
12789
12790         this.view = new Roo.View(this.list, this.tpl, {
12791             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12792         });
12793         
12794         //this.view.wrapEl.setDisplayed(false);
12795         this.view.on('click', this.onViewClick, this);
12796         
12797         
12798         
12799         this.store.on('beforeload', this.onBeforeLoad, this);
12800         this.store.on('load', this.onLoad, this);
12801         this.store.on('loadexception', this.onLoadException, this);
12802         
12803         if(this.editable){
12804             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12805                 "up" : function(e){
12806                     this.inKeyMode = true;
12807                     this.selectPrev();
12808                 },
12809
12810                 "down" : function(e){
12811                     this.inKeyMode = true;
12812                     this.selectNext();
12813                 },
12814
12815                 "enter" : function(e){
12816                     if(this.fireEvent("specialkey", this, e)){
12817                         this.onViewClick(false);
12818                     }
12819                     
12820                     return true;
12821                 },
12822
12823                 "esc" : function(e){
12824                     this.onTickableFooterButtonClick(e, false, false);
12825                 },
12826
12827                 "tab" : function(e){
12828                     this.fireEvent("specialkey", this, e);
12829                     
12830                     this.onTickableFooterButtonClick(e, false, false);
12831                     
12832                     return true;
12833                 },
12834
12835                 scope : this,
12836
12837                 doRelay : function(e, fn, key){
12838                     if(this.scope.isExpanded()){
12839                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12840                     }
12841                     return true;
12842                 },
12843
12844                 forceKeyDown: true
12845             });
12846         }
12847         
12848         this.queryDelay = Math.max(this.queryDelay || 10,
12849                 this.mode == 'local' ? 10 : 250);
12850         
12851         
12852         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12853         
12854         if(this.typeAhead){
12855             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12856         }
12857         
12858         if(this.editable !== false){
12859             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12860         }
12861         
12862     },
12863
12864     onDestroy : function(){
12865         if(this.view){
12866             this.view.setStore(null);
12867             this.view.el.removeAllListeners();
12868             this.view.el.remove();
12869             this.view.purgeListeners();
12870         }
12871         if(this.list){
12872             this.list.dom.innerHTML  = '';
12873         }
12874         
12875         if(this.store){
12876             this.store.un('beforeload', this.onBeforeLoad, this);
12877             this.store.un('load', this.onLoad, this);
12878             this.store.un('loadexception', this.onLoadException, this);
12879         }
12880         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12881     },
12882
12883     // private
12884     fireKey : function(e){
12885         if(e.isNavKeyPress() && !this.list.isVisible()){
12886             this.fireEvent("specialkey", this, e);
12887         }
12888     },
12889
12890     // private
12891     onResize: function(w, h){
12892 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12893 //        
12894 //        if(typeof w != 'number'){
12895 //            // we do not handle it!?!?
12896 //            return;
12897 //        }
12898 //        var tw = this.trigger.getWidth();
12899 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12900 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12901 //        var x = w - tw;
12902 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12903 //            
12904 //        //this.trigger.setStyle('left', x+'px');
12905 //        
12906 //        if(this.list && this.listWidth === undefined){
12907 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12908 //            this.list.setWidth(lw);
12909 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12910 //        }
12911         
12912     
12913         
12914     },
12915
12916     /**
12917      * Allow or prevent the user from directly editing the field text.  If false is passed,
12918      * the user will only be able to select from the items defined in the dropdown list.  This method
12919      * is the runtime equivalent of setting the 'editable' config option at config time.
12920      * @param {Boolean} value True to allow the user to directly edit the field text
12921      */
12922     setEditable : function(value){
12923         if(value == this.editable){
12924             return;
12925         }
12926         this.editable = value;
12927         if(!value){
12928             this.inputEl().dom.setAttribute('readOnly', true);
12929             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12930             this.inputEl().addClass('x-combo-noedit');
12931         }else{
12932             this.inputEl().dom.setAttribute('readOnly', false);
12933             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12934             this.inputEl().removeClass('x-combo-noedit');
12935         }
12936     },
12937
12938     // private
12939     
12940     onBeforeLoad : function(combo,opts){
12941         if(!this.hasFocus){
12942             return;
12943         }
12944          if (!opts.add) {
12945             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12946          }
12947         this.restrictHeight();
12948         this.selectedIndex = -1;
12949     },
12950
12951     // private
12952     onLoad : function(){
12953         
12954         this.hasQuery = false;
12955         
12956         if(!this.hasFocus){
12957             return;
12958         }
12959         
12960         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12961             this.loading.hide();
12962         }
12963              
12964         if(this.store.getCount() > 0){
12965             this.expand();
12966             this.restrictHeight();
12967             if(this.lastQuery == this.allQuery){
12968                 if(this.editable && !this.tickable){
12969                     this.inputEl().dom.select();
12970                 }
12971                 
12972                 if(
12973                     !this.selectByValue(this.value, true) &&
12974                     this.autoFocus && 
12975                     (
12976                         !this.store.lastOptions ||
12977                         typeof(this.store.lastOptions.add) == 'undefined' || 
12978                         this.store.lastOptions.add != true
12979                     )
12980                 ){
12981                     this.select(0, true);
12982                 }
12983             }else{
12984                 if(this.autoFocus){
12985                     this.selectNext();
12986                 }
12987                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12988                     this.taTask.delay(this.typeAheadDelay);
12989                 }
12990             }
12991         }else{
12992             this.onEmptyResults();
12993         }
12994         
12995         //this.el.focus();
12996     },
12997     // private
12998     onLoadException : function()
12999     {
13000         this.hasQuery = false;
13001         
13002         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13003             this.loading.hide();
13004         }
13005         
13006         if(this.tickable && this.editable){
13007             return;
13008         }
13009         
13010         this.collapse();
13011         // only causes errors at present
13012         //Roo.log(this.store.reader.jsonData);
13013         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13014             // fixme
13015             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13016         //}
13017         
13018         
13019     },
13020     // private
13021     onTypeAhead : function(){
13022         if(this.store.getCount() > 0){
13023             var r = this.store.getAt(0);
13024             var newValue = r.data[this.displayField];
13025             var len = newValue.length;
13026             var selStart = this.getRawValue().length;
13027             
13028             if(selStart != len){
13029                 this.setRawValue(newValue);
13030                 this.selectText(selStart, newValue.length);
13031             }
13032         }
13033     },
13034
13035     // private
13036     onSelect : function(record, index){
13037         
13038         if(this.fireEvent('beforeselect', this, record, index) !== false){
13039         
13040             this.setFromData(index > -1 ? record.data : false);
13041             
13042             this.collapse();
13043             this.fireEvent('select', this, record, index);
13044         }
13045     },
13046
13047     /**
13048      * Returns the currently selected field value or empty string if no value is set.
13049      * @return {String} value The selected value
13050      */
13051     getValue : function(){
13052         
13053         if(this.multiple){
13054             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13055         }
13056         
13057         if(this.valueField){
13058             return typeof this.value != 'undefined' ? this.value : '';
13059         }else{
13060             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13061         }
13062     },
13063
13064     /**
13065      * Clears any text/value currently set in the field
13066      */
13067     clearValue : function(){
13068         if(this.hiddenField){
13069             this.hiddenField.dom.value = '';
13070         }
13071         this.value = '';
13072         this.setRawValue('');
13073         this.lastSelectionText = '';
13074         this.lastData = false;
13075         
13076         var close = this.closeTriggerEl();
13077         
13078         if(close){
13079             close.hide();
13080         }
13081         
13082         this.validate();
13083         
13084     },
13085
13086     /**
13087      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13088      * will be displayed in the field.  If the value does not match the data value of an existing item,
13089      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13090      * Otherwise the field will be blank (although the value will still be set).
13091      * @param {String} value The value to match
13092      */
13093     setValue : function(v){
13094         if(this.multiple){
13095             this.syncValue();
13096             return;
13097         }
13098         
13099         var text = v;
13100         if(this.valueField){
13101             var r = this.findRecord(this.valueField, v);
13102             if(r){
13103                 text = r.data[this.displayField];
13104             }else if(this.valueNotFoundText !== undefined){
13105                 text = this.valueNotFoundText;
13106             }
13107         }
13108         this.lastSelectionText = text;
13109         if(this.hiddenField){
13110             this.hiddenField.dom.value = v;
13111         }
13112         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13113         this.value = v;
13114         
13115         var close = this.closeTriggerEl();
13116         
13117         if(close){
13118             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13119         }
13120         
13121         this.validate();
13122     },
13123     /**
13124      * @property {Object} the last set data for the element
13125      */
13126     
13127     lastData : false,
13128     /**
13129      * Sets the value of the field based on a object which is related to the record format for the store.
13130      * @param {Object} value the value to set as. or false on reset?
13131      */
13132     setFromData : function(o){
13133         
13134         if(this.multiple){
13135             this.addItem(o);
13136             return;
13137         }
13138             
13139         var dv = ''; // display value
13140         var vv = ''; // value value..
13141         this.lastData = o;
13142         if (this.displayField) {
13143             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13144         } else {
13145             // this is an error condition!!!
13146             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13147         }
13148         
13149         if(this.valueField){
13150             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13151         }
13152         
13153         var close = this.closeTriggerEl();
13154         
13155         if(close){
13156             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13157         }
13158         
13159         if(this.hiddenField){
13160             this.hiddenField.dom.value = vv;
13161             
13162             this.lastSelectionText = dv;
13163             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13164             this.value = vv;
13165             return;
13166         }
13167         // no hidden field.. - we store the value in 'value', but still display
13168         // display field!!!!
13169         this.lastSelectionText = dv;
13170         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13171         this.value = vv;
13172         
13173         
13174         
13175     },
13176     // private
13177     reset : function(){
13178         // overridden so that last data is reset..
13179         
13180         if(this.multiple){
13181             this.clearItem();
13182             return;
13183         }
13184         
13185         this.setValue(this.originalValue);
13186         //this.clearInvalid();
13187         this.lastData = false;
13188         if (this.view) {
13189             this.view.clearSelections();
13190         }
13191         
13192         this.validate();
13193     },
13194     // private
13195     findRecord : function(prop, value){
13196         var record;
13197         if(this.store.getCount() > 0){
13198             this.store.each(function(r){
13199                 if(r.data[prop] == value){
13200                     record = r;
13201                     return false;
13202                 }
13203                 return true;
13204             });
13205         }
13206         return record;
13207     },
13208     
13209     getName: function()
13210     {
13211         // returns hidden if it's set..
13212         if (!this.rendered) {return ''};
13213         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13214         
13215     },
13216     // private
13217     onViewMove : function(e, t){
13218         this.inKeyMode = false;
13219     },
13220
13221     // private
13222     onViewOver : function(e, t){
13223         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13224             return;
13225         }
13226         var item = this.view.findItemFromChild(t);
13227         
13228         if(item){
13229             var index = this.view.indexOf(item);
13230             this.select(index, false);
13231         }
13232     },
13233
13234     // private
13235     onViewClick : function(view, doFocus, el, e)
13236     {
13237         var index = this.view.getSelectedIndexes()[0];
13238         
13239         var r = this.store.getAt(index);
13240         
13241         if(this.tickable){
13242             
13243             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13244                 return;
13245             }
13246             
13247             var rm = false;
13248             var _this = this;
13249             
13250             Roo.each(this.tickItems, function(v,k){
13251                 
13252                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13253                     Roo.log(v);
13254                     _this.tickItems.splice(k, 1);
13255                     
13256                     if(typeof(e) == 'undefined' && view == false){
13257                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13258                     }
13259                     
13260                     rm = true;
13261                     return;
13262                 }
13263             });
13264             
13265             if(rm){
13266                 return;
13267             }
13268             
13269             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13270                 this.tickItems.push(r.data);
13271             }
13272             
13273             if(typeof(e) == 'undefined' && view == false){
13274                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13275             }
13276                     
13277             return;
13278         }
13279         
13280         if(r){
13281             this.onSelect(r, index);
13282         }
13283         if(doFocus !== false && !this.blockFocus){
13284             this.inputEl().focus();
13285         }
13286     },
13287
13288     // private
13289     restrictHeight : function(){
13290         //this.innerList.dom.style.height = '';
13291         //var inner = this.innerList.dom;
13292         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13293         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13294         //this.list.beginUpdate();
13295         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13296         this.list.alignTo(this.inputEl(), this.listAlign);
13297         this.list.alignTo(this.inputEl(), this.listAlign);
13298         //this.list.endUpdate();
13299     },
13300
13301     // private
13302     onEmptyResults : function(){
13303         
13304         if(this.tickable && this.editable){
13305             this.restrictHeight();
13306             return;
13307         }
13308         
13309         this.collapse();
13310     },
13311
13312     /**
13313      * Returns true if the dropdown list is expanded, else false.
13314      */
13315     isExpanded : function(){
13316         return this.list.isVisible();
13317     },
13318
13319     /**
13320      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13321      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13322      * @param {String} value The data value of the item to select
13323      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13324      * selected item if it is not currently in view (defaults to true)
13325      * @return {Boolean} True if the value matched an item in the list, else false
13326      */
13327     selectByValue : function(v, scrollIntoView){
13328         if(v !== undefined && v !== null){
13329             var r = this.findRecord(this.valueField || this.displayField, v);
13330             if(r){
13331                 this.select(this.store.indexOf(r), scrollIntoView);
13332                 return true;
13333             }
13334         }
13335         return false;
13336     },
13337
13338     /**
13339      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13340      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13341      * @param {Number} index The zero-based index of the list item to select
13342      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13343      * selected item if it is not currently in view (defaults to true)
13344      */
13345     select : function(index, scrollIntoView){
13346         this.selectedIndex = index;
13347         this.view.select(index);
13348         if(scrollIntoView !== false){
13349             var el = this.view.getNode(index);
13350             /*
13351              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13352              */
13353             if(el){
13354                 this.list.scrollChildIntoView(el, false);
13355             }
13356         }
13357     },
13358
13359     // private
13360     selectNext : function(){
13361         var ct = this.store.getCount();
13362         if(ct > 0){
13363             if(this.selectedIndex == -1){
13364                 this.select(0);
13365             }else if(this.selectedIndex < ct-1){
13366                 this.select(this.selectedIndex+1);
13367             }
13368         }
13369     },
13370
13371     // private
13372     selectPrev : function(){
13373         var ct = this.store.getCount();
13374         if(ct > 0){
13375             if(this.selectedIndex == -1){
13376                 this.select(0);
13377             }else if(this.selectedIndex != 0){
13378                 this.select(this.selectedIndex-1);
13379             }
13380         }
13381     },
13382
13383     // private
13384     onKeyUp : function(e){
13385         if(this.editable !== false && !e.isSpecialKey()){
13386             this.lastKey = e.getKey();
13387             this.dqTask.delay(this.queryDelay);
13388         }
13389     },
13390
13391     // private
13392     validateBlur : function(){
13393         return !this.list || !this.list.isVisible();   
13394     },
13395
13396     // private
13397     initQuery : function(){
13398         
13399         var v = this.getRawValue();
13400         
13401         if(this.tickable && this.editable){
13402             v = this.tickableInputEl().getValue();
13403         }
13404         
13405         this.doQuery(v);
13406     },
13407
13408     // private
13409     doForce : function(){
13410         if(this.inputEl().dom.value.length > 0){
13411             this.inputEl().dom.value =
13412                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13413              
13414         }
13415     },
13416
13417     /**
13418      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13419      * query allowing the query action to be canceled if needed.
13420      * @param {String} query The SQL query to execute
13421      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13422      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13423      * saved in the current store (defaults to false)
13424      */
13425     doQuery : function(q, forceAll){
13426         
13427         if(q === undefined || q === null){
13428             q = '';
13429         }
13430         var qe = {
13431             query: q,
13432             forceAll: forceAll,
13433             combo: this,
13434             cancel:false
13435         };
13436         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13437             return false;
13438         }
13439         q = qe.query;
13440         
13441         forceAll = qe.forceAll;
13442         if(forceAll === true || (q.length >= this.minChars)){
13443             
13444             this.hasQuery = true;
13445             
13446             if(this.lastQuery != q || this.alwaysQuery){
13447                 this.lastQuery = q;
13448                 if(this.mode == 'local'){
13449                     this.selectedIndex = -1;
13450                     if(forceAll){
13451                         this.store.clearFilter();
13452                     }else{
13453                         
13454                         if(this.specialFilter){
13455                             this.fireEvent('specialfilter', this);
13456                             this.onLoad();
13457                             return;
13458                         }
13459                         
13460                         this.store.filter(this.displayField, q);
13461                     }
13462                     
13463                     this.store.fireEvent("datachanged", this.store);
13464                     
13465                     this.onLoad();
13466                     
13467                     
13468                 }else{
13469                     
13470                     this.store.baseParams[this.queryParam] = q;
13471                     
13472                     var options = {params : this.getParams(q)};
13473                     
13474                     if(this.loadNext){
13475                         options.add = true;
13476                         options.params.start = this.page * this.pageSize;
13477                     }
13478                     
13479                     this.store.load(options);
13480                     
13481                     /*
13482                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13483                      *  we should expand the list on onLoad
13484                      *  so command out it
13485                      */
13486 //                    this.expand();
13487                 }
13488             }else{
13489                 this.selectedIndex = -1;
13490                 this.onLoad();   
13491             }
13492         }
13493         
13494         this.loadNext = false;
13495     },
13496     
13497     // private
13498     getParams : function(q){
13499         var p = {};
13500         //p[this.queryParam] = q;
13501         
13502         if(this.pageSize){
13503             p.start = 0;
13504             p.limit = this.pageSize;
13505         }
13506         return p;
13507     },
13508
13509     /**
13510      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13511      */
13512     collapse : function(){
13513         if(!this.isExpanded()){
13514             return;
13515         }
13516         
13517         this.list.hide();
13518         
13519         if(this.tickable){
13520             this.hasFocus = false;
13521             this.okBtn.hide();
13522             this.cancelBtn.hide();
13523             this.trigger.show();
13524             
13525             if(this.editable){
13526                 this.tickableInputEl().dom.value = '';
13527                 this.tickableInputEl().blur();
13528             }
13529             
13530         }
13531         
13532         Roo.get(document).un('mousedown', this.collapseIf, this);
13533         Roo.get(document).un('mousewheel', this.collapseIf, this);
13534         if (!this.editable) {
13535             Roo.get(document).un('keydown', this.listKeyPress, this);
13536         }
13537         this.fireEvent('collapse', this);
13538         
13539         this.validate();
13540     },
13541
13542     // private
13543     collapseIf : function(e){
13544         var in_combo  = e.within(this.el);
13545         var in_list =  e.within(this.list);
13546         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13547         
13548         if (in_combo || in_list || is_list) {
13549             //e.stopPropagation();
13550             return;
13551         }
13552         
13553         if(this.tickable){
13554             this.onTickableFooterButtonClick(e, false, false);
13555         }
13556
13557         this.collapse();
13558         
13559     },
13560
13561     /**
13562      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13563      */
13564     expand : function(){
13565        
13566         if(this.isExpanded() || !this.hasFocus){
13567             return;
13568         }
13569         
13570         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13571         this.list.setWidth(lw);
13572         
13573         
13574          Roo.log('expand');
13575         
13576         this.list.show();
13577         
13578         this.restrictHeight();
13579         
13580         if(this.tickable){
13581             
13582             this.tickItems = Roo.apply([], this.item);
13583             
13584             this.okBtn.show();
13585             this.cancelBtn.show();
13586             this.trigger.hide();
13587             
13588             if(this.editable){
13589                 this.tickableInputEl().focus();
13590             }
13591             
13592         }
13593         
13594         Roo.get(document).on('mousedown', this.collapseIf, this);
13595         Roo.get(document).on('mousewheel', this.collapseIf, this);
13596         if (!this.editable) {
13597             Roo.get(document).on('keydown', this.listKeyPress, this);
13598         }
13599         
13600         this.fireEvent('expand', this);
13601     },
13602
13603     // private
13604     // Implements the default empty TriggerField.onTriggerClick function
13605     onTriggerClick : function(e)
13606     {
13607         Roo.log('trigger click');
13608         
13609         if(this.disabled || !this.triggerList){
13610             return;
13611         }
13612         
13613         this.page = 0;
13614         this.loadNext = false;
13615         
13616         if(this.isExpanded()){
13617             this.collapse();
13618             if (!this.blockFocus) {
13619                 this.inputEl().focus();
13620             }
13621             
13622         }else {
13623             this.hasFocus = true;
13624             if(this.triggerAction == 'all') {
13625                 this.doQuery(this.allQuery, true);
13626             } else {
13627                 this.doQuery(this.getRawValue());
13628             }
13629             if (!this.blockFocus) {
13630                 this.inputEl().focus();
13631             }
13632         }
13633     },
13634     
13635     onTickableTriggerClick : function(e)
13636     {
13637         if(this.disabled){
13638             return;
13639         }
13640         
13641         this.page = 0;
13642         this.loadNext = false;
13643         this.hasFocus = true;
13644         
13645         if(this.triggerAction == 'all') {
13646             this.doQuery(this.allQuery, true);
13647         } else {
13648             this.doQuery(this.getRawValue());
13649         }
13650     },
13651     
13652     onSearchFieldClick : function(e)
13653     {
13654         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13655             this.onTickableFooterButtonClick(e, false, false);
13656             return;
13657         }
13658         
13659         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13660             return;
13661         }
13662         
13663         this.page = 0;
13664         this.loadNext = false;
13665         this.hasFocus = true;
13666         
13667         if(this.triggerAction == 'all') {
13668             this.doQuery(this.allQuery, true);
13669         } else {
13670             this.doQuery(this.getRawValue());
13671         }
13672     },
13673     
13674     listKeyPress : function(e)
13675     {
13676         //Roo.log('listkeypress');
13677         // scroll to first matching element based on key pres..
13678         if (e.isSpecialKey()) {
13679             return false;
13680         }
13681         var k = String.fromCharCode(e.getKey()).toUpperCase();
13682         //Roo.log(k);
13683         var match  = false;
13684         var csel = this.view.getSelectedNodes();
13685         var cselitem = false;
13686         if (csel.length) {
13687             var ix = this.view.indexOf(csel[0]);
13688             cselitem  = this.store.getAt(ix);
13689             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13690                 cselitem = false;
13691             }
13692             
13693         }
13694         
13695         this.store.each(function(v) { 
13696             if (cselitem) {
13697                 // start at existing selection.
13698                 if (cselitem.id == v.id) {
13699                     cselitem = false;
13700                 }
13701                 return true;
13702             }
13703                 
13704             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13705                 match = this.store.indexOf(v);
13706                 return false;
13707             }
13708             return true;
13709         }, this);
13710         
13711         if (match === false) {
13712             return true; // no more action?
13713         }
13714         // scroll to?
13715         this.view.select(match);
13716         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13717         sn.scrollIntoView(sn.dom.parentNode, false);
13718     },
13719     
13720     onViewScroll : function(e, t){
13721         
13722         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13723             return;
13724         }
13725         
13726         this.hasQuery = true;
13727         
13728         this.loading = this.list.select('.loading', true).first();
13729         
13730         if(this.loading === null){
13731             this.list.createChild({
13732                 tag: 'div',
13733                 cls: 'loading roo-select2-more-results roo-select2-active',
13734                 html: 'Loading more results...'
13735             });
13736             
13737             this.loading = this.list.select('.loading', true).first();
13738             
13739             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13740             
13741             this.loading.hide();
13742         }
13743         
13744         this.loading.show();
13745         
13746         var _combo = this;
13747         
13748         this.page++;
13749         this.loadNext = true;
13750         
13751         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13752         
13753         return;
13754     },
13755     
13756     addItem : function(o)
13757     {   
13758         var dv = ''; // display value
13759         
13760         if (this.displayField) {
13761             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13762         } else {
13763             // this is an error condition!!!
13764             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13765         }
13766         
13767         if(!dv.length){
13768             return;
13769         }
13770         
13771         var choice = this.choices.createChild({
13772             tag: 'li',
13773             cls: 'roo-select2-search-choice',
13774             cn: [
13775                 {
13776                     tag: 'div',
13777                     html: dv
13778                 },
13779                 {
13780                     tag: 'a',
13781                     href: '#',
13782                     cls: 'roo-select2-search-choice-close',
13783                     tabindex: '-1'
13784                 }
13785             ]
13786             
13787         }, this.searchField);
13788         
13789         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13790         
13791         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13792         
13793         this.item.push(o);
13794         
13795         this.lastData = o;
13796         
13797         this.syncValue();
13798         
13799         this.inputEl().dom.value = '';
13800         
13801         this.validate();
13802     },
13803     
13804     onRemoveItem : function(e, _self, o)
13805     {
13806         e.preventDefault();
13807         
13808         this.lastItem = Roo.apply([], this.item);
13809         
13810         var index = this.item.indexOf(o.data) * 1;
13811         
13812         if( index < 0){
13813             Roo.log('not this item?!');
13814             return;
13815         }
13816         
13817         this.item.splice(index, 1);
13818         o.item.remove();
13819         
13820         this.syncValue();
13821         
13822         this.fireEvent('remove', this, e);
13823         
13824         this.validate();
13825         
13826     },
13827     
13828     syncValue : function()
13829     {
13830         if(!this.item.length){
13831             this.clearValue();
13832             return;
13833         }
13834             
13835         var value = [];
13836         var _this = this;
13837         Roo.each(this.item, function(i){
13838             if(_this.valueField){
13839                 value.push(i[_this.valueField]);
13840                 return;
13841             }
13842
13843             value.push(i);
13844         });
13845
13846         this.value = value.join(',');
13847
13848         if(this.hiddenField){
13849             this.hiddenField.dom.value = this.value;
13850         }
13851         
13852         this.store.fireEvent("datachanged", this.store);
13853         
13854         this.validate();
13855     },
13856     
13857     clearItem : function()
13858     {
13859         if(!this.multiple){
13860             return;
13861         }
13862         
13863         this.item = [];
13864         
13865         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13866            c.remove();
13867         });
13868         
13869         this.syncValue();
13870         
13871         this.validate();
13872         
13873         if(this.tickable && !Roo.isTouch){
13874             this.view.refresh();
13875         }
13876     },
13877     
13878     inputEl: function ()
13879     {
13880         if(Roo.isTouch && this.mobileTouchView){
13881             return this.el.select('input.form-control',true).first();
13882         }
13883         
13884         if(this.tickable){
13885             return this.searchField;
13886         }
13887         
13888         return this.el.select('input.form-control',true).first();
13889     },
13890     
13891     
13892     onTickableFooterButtonClick : function(e, btn, el)
13893     {
13894         e.preventDefault();
13895         
13896         this.lastItem = Roo.apply([], this.item);
13897         
13898         if(btn && btn.name == 'cancel'){
13899             this.tickItems = Roo.apply([], this.item);
13900             this.collapse();
13901             return;
13902         }
13903         
13904         this.clearItem();
13905         
13906         var _this = this;
13907         
13908         Roo.each(this.tickItems, function(o){
13909             _this.addItem(o);
13910         });
13911         
13912         this.collapse();
13913         
13914     },
13915     
13916     validate : function()
13917     {
13918         var v = this.getRawValue();
13919         
13920         if(this.multiple){
13921             v = this.getValue();
13922         }
13923         
13924         if(this.disabled || this.allowBlank || v.length){
13925             this.markValid();
13926             return true;
13927         }
13928         
13929         this.markInvalid();
13930         return false;
13931     },
13932     
13933     tickableInputEl : function()
13934     {
13935         if(!this.tickable || !this.editable){
13936             return this.inputEl();
13937         }
13938         
13939         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13940     },
13941     
13942     
13943     getAutoCreateTouchView : function()
13944     {
13945         var id = Roo.id();
13946         
13947         var cfg = {
13948             cls: 'form-group' //input-group
13949         };
13950         
13951         var input =  {
13952             tag: 'input',
13953             id : id,
13954             type : this.inputType,
13955             cls : 'form-control x-combo-noedit',
13956             autocomplete: 'new-password',
13957             placeholder : this.placeholder || '',
13958             readonly : true
13959         };
13960         
13961         if (this.name) {
13962             input.name = this.name;
13963         }
13964         
13965         if (this.size) {
13966             input.cls += ' input-' + this.size;
13967         }
13968         
13969         if (this.disabled) {
13970             input.disabled = true;
13971         }
13972         
13973         var inputblock = {
13974             cls : '',
13975             cn : [
13976                 input
13977             ]
13978         };
13979         
13980         if(this.before){
13981             inputblock.cls += ' input-group';
13982             
13983             inputblock.cn.unshift({
13984                 tag :'span',
13985                 cls : 'input-group-addon',
13986                 html : this.before
13987             });
13988         }
13989         
13990         if(this.removable && !this.multiple){
13991             inputblock.cls += ' roo-removable';
13992             
13993             inputblock.cn.push({
13994                 tag: 'button',
13995                 html : 'x',
13996                 cls : 'roo-combo-removable-btn close'
13997             });
13998         }
13999
14000         if(this.hasFeedback && !this.allowBlank){
14001             
14002             inputblock.cls += ' has-feedback';
14003             
14004             inputblock.cn.push({
14005                 tag: 'span',
14006                 cls: 'glyphicon form-control-feedback'
14007             });
14008             
14009         }
14010         
14011         if (this.after) {
14012             
14013             inputblock.cls += (this.before) ? '' : ' input-group';
14014             
14015             inputblock.cn.push({
14016                 tag :'span',
14017                 cls : 'input-group-addon',
14018                 html : this.after
14019             });
14020         }
14021
14022         var box = {
14023             tag: 'div',
14024             cn: [
14025                 {
14026                     tag: 'input',
14027                     type : 'hidden',
14028                     cls: 'form-hidden-field'
14029                 },
14030                 inputblock
14031             ]
14032             
14033         };
14034         
14035         if(this.multiple){
14036             box = {
14037                 tag: 'div',
14038                 cn: [
14039                     {
14040                         tag: 'input',
14041                         type : 'hidden',
14042                         cls: 'form-hidden-field'
14043                     },
14044                     {
14045                         tag: 'ul',
14046                         cls: 'roo-select2-choices',
14047                         cn:[
14048                             {
14049                                 tag: 'li',
14050                                 cls: 'roo-select2-search-field',
14051                                 cn: [
14052
14053                                     inputblock
14054                                 ]
14055                             }
14056                         ]
14057                     }
14058                 ]
14059             }
14060         };
14061         
14062         var combobox = {
14063             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14064             cn: [
14065                 box
14066             ]
14067         };
14068         
14069         if(!this.multiple && this.showToggleBtn){
14070             
14071             var caret = {
14072                         tag: 'span',
14073                         cls: 'caret'
14074             };
14075             
14076             if (this.caret != false) {
14077                 caret = {
14078                      tag: 'i',
14079                      cls: 'fa fa-' + this.caret
14080                 };
14081                 
14082             }
14083             
14084             combobox.cn.push({
14085                 tag :'span',
14086                 cls : 'input-group-addon btn dropdown-toggle',
14087                 cn : [
14088                     caret,
14089                     {
14090                         tag: 'span',
14091                         cls: 'combobox-clear',
14092                         cn  : [
14093                             {
14094                                 tag : 'i',
14095                                 cls: 'icon-remove'
14096                             }
14097                         ]
14098                     }
14099                 ]
14100
14101             })
14102         }
14103         
14104         if(this.multiple){
14105             combobox.cls += ' roo-select2-container-multi';
14106         }
14107         
14108         var align = this.labelAlign || this.parentLabelAlign();
14109         
14110         cfg.cn = combobox;
14111         
14112         if(this.fieldLabel.length && this.labelWidth){
14113             
14114             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14115             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14116             
14117             cfg.cn = [
14118                 {
14119                    tag : 'i',
14120                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14121                    tooltip : 'This field is required'
14122                 },
14123                 {
14124                     tag: 'label',
14125                     cls : 'control-label ' + lw,
14126                     html : this.fieldLabel
14127
14128                 },
14129                 {
14130                     cls : cw, 
14131                     cn: [
14132                         combobox
14133                     ]
14134                 }
14135             ];
14136             
14137             if(this.indicatorpos == 'right'){
14138                 cfg.cn = [
14139                     {
14140                         tag: 'label',
14141                         cls : 'control-label ' + lw,
14142                         html : this.fieldLabel
14143
14144                     },
14145                     {
14146                        tag : 'i',
14147                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14148                        tooltip : 'This field is required'
14149                     },
14150                     {
14151                         cls : cw, 
14152                         cn: [
14153                             combobox
14154                         ]
14155                     }
14156                 ];
14157             }
14158         }
14159         
14160         var settings = this;
14161         
14162         ['xs','sm','md','lg'].map(function(size){
14163             if (settings[size]) {
14164                 cfg.cls += ' col-' + size + '-' + settings[size];
14165             }
14166         });
14167         
14168         return cfg;
14169     },
14170     
14171     initTouchView : function()
14172     {
14173         this.renderTouchView();
14174         
14175         this.touchViewEl.on('scroll', function(){
14176             this.el.dom.scrollTop = 0;
14177         }, this);
14178         
14179         this.originalValue = this.getValue();
14180         
14181         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14182         
14183         this.inputEl().on("click", this.showTouchView, this);
14184         this.triggerEl.on("click", this.showTouchView, this);
14185         
14186         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14187         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14188         
14189         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14190         
14191         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14192         this.store.on('load', this.onTouchViewLoad, this);
14193         this.store.on('loadexception', this.onTouchViewLoadException, this);
14194         
14195         if(this.hiddenName){
14196             
14197             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14198             
14199             this.hiddenField.dom.value =
14200                 this.hiddenValue !== undefined ? this.hiddenValue :
14201                 this.value !== undefined ? this.value : '';
14202         
14203             this.el.dom.removeAttribute('name');
14204             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14205         }
14206         
14207         if(this.multiple){
14208             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14209             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14210         }
14211         
14212         if(this.removable && !this.multiple){
14213             var close = this.closeTriggerEl();
14214             if(close){
14215                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14216                 close.on('click', this.removeBtnClick, this, close);
14217             }
14218         }
14219         /*
14220          * fix the bug in Safari iOS8
14221          */
14222         this.inputEl().on("focus", function(e){
14223             document.activeElement.blur();
14224         }, this);
14225         
14226         return;
14227         
14228         
14229     },
14230     
14231     renderTouchView : function()
14232     {
14233         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14234         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14235         
14236         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14237         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14238         
14239         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14240         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14241         this.touchViewBodyEl.setStyle('overflow', 'auto');
14242         
14243         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14244         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14245         
14246         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14247         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14248         
14249     },
14250     
14251     showTouchView : function()
14252     {
14253         if(this.disabled){
14254             return;
14255         }
14256         
14257         this.touchViewHeaderEl.hide();
14258
14259         if(this.modalTitle.length){
14260             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14261             this.touchViewHeaderEl.show();
14262         }
14263
14264         this.touchViewEl.show();
14265
14266         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14267         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14268                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14269
14270         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14271
14272         if(this.modalTitle.length){
14273             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14274         }
14275         
14276         this.touchViewBodyEl.setHeight(bodyHeight);
14277
14278         if(this.animate){
14279             var _this = this;
14280             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14281         }else{
14282             this.touchViewEl.addClass('in');
14283         }
14284
14285         this.doTouchViewQuery();
14286         
14287     },
14288     
14289     hideTouchView : function()
14290     {
14291         this.touchViewEl.removeClass('in');
14292
14293         if(this.animate){
14294             var _this = this;
14295             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14296         }else{
14297             this.touchViewEl.setStyle('display', 'none');
14298         }
14299         
14300     },
14301     
14302     setTouchViewValue : function()
14303     {
14304         if(this.multiple){
14305             this.clearItem();
14306         
14307             var _this = this;
14308
14309             Roo.each(this.tickItems, function(o){
14310                 this.addItem(o);
14311             }, this);
14312         }
14313         
14314         this.hideTouchView();
14315     },
14316     
14317     doTouchViewQuery : function()
14318     {
14319         var qe = {
14320             query: '',
14321             forceAll: true,
14322             combo: this,
14323             cancel:false
14324         };
14325         
14326         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14327             return false;
14328         }
14329         
14330         if(!this.alwaysQuery || this.mode == 'local'){
14331             this.onTouchViewLoad();
14332             return;
14333         }
14334         
14335         this.store.load();
14336     },
14337     
14338     onTouchViewBeforeLoad : function(combo,opts)
14339     {
14340         return;
14341     },
14342
14343     // private
14344     onTouchViewLoad : function()
14345     {
14346         if(this.store.getCount() < 1){
14347             this.onTouchViewEmptyResults();
14348             return;
14349         }
14350         
14351         this.clearTouchView();
14352         
14353         var rawValue = this.getRawValue();
14354         
14355         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14356         
14357         this.tickItems = [];
14358         
14359         this.store.data.each(function(d, rowIndex){
14360             var row = this.touchViewListGroup.createChild(template);
14361             
14362             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14363                 row.addClass(d.data.cls);
14364             }
14365             
14366             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14367                 var cfg = {
14368                     data : d.data,
14369                     html : d.data[this.displayField]
14370                 };
14371                 
14372                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14373                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14374                 }
14375             }
14376             row.removeClass('selected');
14377             if(!this.multiple && this.valueField &&
14378                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14379             {
14380                 // radio buttons..
14381                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14382                 row.addClass('selected');
14383             }
14384             
14385             if(this.multiple && this.valueField &&
14386                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14387             {
14388                 
14389                 // checkboxes...
14390                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14391                 this.tickItems.push(d.data);
14392             }
14393             
14394             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14395             
14396         }, this);
14397         
14398         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14399         
14400         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14401
14402         if(this.modalTitle.length){
14403             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14404         }
14405
14406         var listHeight = this.touchViewListGroup.getHeight();
14407         
14408         var _this = this;
14409         
14410         if(firstChecked && listHeight > bodyHeight){
14411             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14412         }
14413         
14414     },
14415     
14416     onTouchViewLoadException : function()
14417     {
14418         this.hideTouchView();
14419     },
14420     
14421     onTouchViewEmptyResults : function()
14422     {
14423         this.clearTouchView();
14424         
14425         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14426         
14427         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14428         
14429     },
14430     
14431     clearTouchView : function()
14432     {
14433         this.touchViewListGroup.dom.innerHTML = '';
14434     },
14435     
14436     onTouchViewClick : function(e, el, o)
14437     {
14438         e.preventDefault();
14439         
14440         var row = o.row;
14441         var rowIndex = o.rowIndex;
14442         
14443         var r = this.store.getAt(rowIndex);
14444         
14445         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14446             
14447             if(!this.multiple){
14448                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14449                     c.dom.removeAttribute('checked');
14450                 }, this);
14451
14452                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14453
14454                 this.setFromData(r.data);
14455
14456                 var close = this.closeTriggerEl();
14457
14458                 if(close){
14459                     close.show();
14460                 }
14461
14462                 this.hideTouchView();
14463
14464                 this.fireEvent('select', this, r, rowIndex);
14465
14466                 return;
14467             }
14468
14469             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14470                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14471                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14472                 return;
14473             }
14474
14475             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14476             this.addItem(r.data);
14477             this.tickItems.push(r.data);
14478         }
14479     }
14480     
14481
14482     /** 
14483     * @cfg {Boolean} grow 
14484     * @hide 
14485     */
14486     /** 
14487     * @cfg {Number} growMin 
14488     * @hide 
14489     */
14490     /** 
14491     * @cfg {Number} growMax 
14492     * @hide 
14493     */
14494     /**
14495      * @hide
14496      * @method autoSize
14497      */
14498 });
14499
14500 Roo.apply(Roo.bootstrap.ComboBox,  {
14501     
14502     header : {
14503         tag: 'div',
14504         cls: 'modal-header',
14505         cn: [
14506             {
14507                 tag: 'h4',
14508                 cls: 'modal-title'
14509             }
14510         ]
14511     },
14512     
14513     body : {
14514         tag: 'div',
14515         cls: 'modal-body',
14516         cn: [
14517             {
14518                 tag: 'ul',
14519                 cls: 'list-group'
14520             }
14521         ]
14522     },
14523     
14524     listItemRadio : {
14525         tag: 'li',
14526         cls: 'list-group-item',
14527         cn: [
14528             {
14529                 tag: 'span',
14530                 cls: 'roo-combobox-list-group-item-value'
14531             },
14532             {
14533                 tag: 'div',
14534                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14535                 cn: [
14536                     {
14537                         tag: 'input',
14538                         type: 'radio'
14539                     },
14540                     {
14541                         tag: 'label'
14542                     }
14543                 ]
14544             }
14545         ]
14546     },
14547     
14548     listItemCheckbox : {
14549         tag: 'li',
14550         cls: 'list-group-item',
14551         cn: [
14552             {
14553                 tag: 'span',
14554                 cls: 'roo-combobox-list-group-item-value'
14555             },
14556             {
14557                 tag: 'div',
14558                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14559                 cn: [
14560                     {
14561                         tag: 'input',
14562                         type: 'checkbox'
14563                     },
14564                     {
14565                         tag: 'label'
14566                     }
14567                 ]
14568             }
14569         ]
14570     },
14571     
14572     emptyResult : {
14573         tag: 'div',
14574         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14575     },
14576     
14577     footer : {
14578         tag: 'div',
14579         cls: 'modal-footer',
14580         cn: [
14581             {
14582                 tag: 'div',
14583                 cls: 'row',
14584                 cn: [
14585                     {
14586                         tag: 'div',
14587                         cls: 'col-xs-6 text-left',
14588                         cn: {
14589                             tag: 'button',
14590                             cls: 'btn btn-danger roo-touch-view-cancel',
14591                             html: 'Cancel'
14592                         }
14593                     },
14594                     {
14595                         tag: 'div',
14596                         cls: 'col-xs-6 text-right',
14597                         cn: {
14598                             tag: 'button',
14599                             cls: 'btn btn-success roo-touch-view-ok',
14600                             html: 'OK'
14601                         }
14602                     }
14603                 ]
14604             }
14605         ]
14606         
14607     }
14608 });
14609
14610 Roo.apply(Roo.bootstrap.ComboBox,  {
14611     
14612     touchViewTemplate : {
14613         tag: 'div',
14614         cls: 'modal fade roo-combobox-touch-view',
14615         cn: [
14616             {
14617                 tag: 'div',
14618                 cls: 'modal-dialog',
14619                 style : 'position:fixed', // we have to fix position....
14620                 cn: [
14621                     {
14622                         tag: 'div',
14623                         cls: 'modal-content',
14624                         cn: [
14625                             Roo.bootstrap.ComboBox.header,
14626                             Roo.bootstrap.ComboBox.body,
14627                             Roo.bootstrap.ComboBox.footer
14628                         ]
14629                     }
14630                 ]
14631             }
14632         ]
14633     }
14634 });/*
14635  * Based on:
14636  * Ext JS Library 1.1.1
14637  * Copyright(c) 2006-2007, Ext JS, LLC.
14638  *
14639  * Originally Released Under LGPL - original licence link has changed is not relivant.
14640  *
14641  * Fork - LGPL
14642  * <script type="text/javascript">
14643  */
14644
14645 /**
14646  * @class Roo.View
14647  * @extends Roo.util.Observable
14648  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14649  * This class also supports single and multi selection modes. <br>
14650  * Create a data model bound view:
14651  <pre><code>
14652  var store = new Roo.data.Store(...);
14653
14654  var view = new Roo.View({
14655     el : "my-element",
14656     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14657  
14658     singleSelect: true,
14659     selectedClass: "ydataview-selected",
14660     store: store
14661  });
14662
14663  // listen for node click?
14664  view.on("click", function(vw, index, node, e){
14665  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14666  });
14667
14668  // load XML data
14669  dataModel.load("foobar.xml");
14670  </code></pre>
14671  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14672  * <br><br>
14673  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14674  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14675  * 
14676  * Note: old style constructor is still suported (container, template, config)
14677  * 
14678  * @constructor
14679  * Create a new View
14680  * @param {Object} config The config object
14681  * 
14682  */
14683 Roo.View = function(config, depreciated_tpl, depreciated_config){
14684     
14685     this.parent = false;
14686     
14687     if (typeof(depreciated_tpl) == 'undefined') {
14688         // new way.. - universal constructor.
14689         Roo.apply(this, config);
14690         this.el  = Roo.get(this.el);
14691     } else {
14692         // old format..
14693         this.el  = Roo.get(config);
14694         this.tpl = depreciated_tpl;
14695         Roo.apply(this, depreciated_config);
14696     }
14697     this.wrapEl  = this.el.wrap().wrap();
14698     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14699     
14700     
14701     if(typeof(this.tpl) == "string"){
14702         this.tpl = new Roo.Template(this.tpl);
14703     } else {
14704         // support xtype ctors..
14705         this.tpl = new Roo.factory(this.tpl, Roo);
14706     }
14707     
14708     
14709     this.tpl.compile();
14710     
14711     /** @private */
14712     this.addEvents({
14713         /**
14714          * @event beforeclick
14715          * Fires before a click is processed. Returns false to cancel the default action.
14716          * @param {Roo.View} this
14717          * @param {Number} index The index of the target node
14718          * @param {HTMLElement} node The target node
14719          * @param {Roo.EventObject} e The raw event object
14720          */
14721             "beforeclick" : true,
14722         /**
14723          * @event click
14724          * Fires when a template node is clicked.
14725          * @param {Roo.View} this
14726          * @param {Number} index The index of the target node
14727          * @param {HTMLElement} node The target node
14728          * @param {Roo.EventObject} e The raw event object
14729          */
14730             "click" : true,
14731         /**
14732          * @event dblclick
14733          * Fires when a template node is double clicked.
14734          * @param {Roo.View} this
14735          * @param {Number} index The index of the target node
14736          * @param {HTMLElement} node The target node
14737          * @param {Roo.EventObject} e The raw event object
14738          */
14739             "dblclick" : true,
14740         /**
14741          * @event contextmenu
14742          * Fires when a template node is right clicked.
14743          * @param {Roo.View} this
14744          * @param {Number} index The index of the target node
14745          * @param {HTMLElement} node The target node
14746          * @param {Roo.EventObject} e The raw event object
14747          */
14748             "contextmenu" : true,
14749         /**
14750          * @event selectionchange
14751          * Fires when the selected nodes change.
14752          * @param {Roo.View} this
14753          * @param {Array} selections Array of the selected nodes
14754          */
14755             "selectionchange" : true,
14756     
14757         /**
14758          * @event beforeselect
14759          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14760          * @param {Roo.View} this
14761          * @param {HTMLElement} node The node to be selected
14762          * @param {Array} selections Array of currently selected nodes
14763          */
14764             "beforeselect" : true,
14765         /**
14766          * @event preparedata
14767          * Fires on every row to render, to allow you to change the data.
14768          * @param {Roo.View} this
14769          * @param {Object} data to be rendered (change this)
14770          */
14771           "preparedata" : true
14772           
14773           
14774         });
14775
14776
14777
14778     this.el.on({
14779         "click": this.onClick,
14780         "dblclick": this.onDblClick,
14781         "contextmenu": this.onContextMenu,
14782         scope:this
14783     });
14784
14785     this.selections = [];
14786     this.nodes = [];
14787     this.cmp = new Roo.CompositeElementLite([]);
14788     if(this.store){
14789         this.store = Roo.factory(this.store, Roo.data);
14790         this.setStore(this.store, true);
14791     }
14792     
14793     if ( this.footer && this.footer.xtype) {
14794            
14795          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14796         
14797         this.footer.dataSource = this.store;
14798         this.footer.container = fctr;
14799         this.footer = Roo.factory(this.footer, Roo);
14800         fctr.insertFirst(this.el);
14801         
14802         // this is a bit insane - as the paging toolbar seems to detach the el..
14803 //        dom.parentNode.parentNode.parentNode
14804          // they get detached?
14805     }
14806     
14807     
14808     Roo.View.superclass.constructor.call(this);
14809     
14810     
14811 };
14812
14813 Roo.extend(Roo.View, Roo.util.Observable, {
14814     
14815      /**
14816      * @cfg {Roo.data.Store} store Data store to load data from.
14817      */
14818     store : false,
14819     
14820     /**
14821      * @cfg {String|Roo.Element} el The container element.
14822      */
14823     el : '',
14824     
14825     /**
14826      * @cfg {String|Roo.Template} tpl The template used by this View 
14827      */
14828     tpl : false,
14829     /**
14830      * @cfg {String} dataName the named area of the template to use as the data area
14831      *                          Works with domtemplates roo-name="name"
14832      */
14833     dataName: false,
14834     /**
14835      * @cfg {String} selectedClass The css class to add to selected nodes
14836      */
14837     selectedClass : "x-view-selected",
14838      /**
14839      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14840      */
14841     emptyText : "",
14842     
14843     /**
14844      * @cfg {String} text to display on mask (default Loading)
14845      */
14846     mask : false,
14847     /**
14848      * @cfg {Boolean} multiSelect Allow multiple selection
14849      */
14850     multiSelect : false,
14851     /**
14852      * @cfg {Boolean} singleSelect Allow single selection
14853      */
14854     singleSelect:  false,
14855     
14856     /**
14857      * @cfg {Boolean} toggleSelect - selecting 
14858      */
14859     toggleSelect : false,
14860     
14861     /**
14862      * @cfg {Boolean} tickable - selecting 
14863      */
14864     tickable : false,
14865     
14866     /**
14867      * Returns the element this view is bound to.
14868      * @return {Roo.Element}
14869      */
14870     getEl : function(){
14871         return this.wrapEl;
14872     },
14873     
14874     
14875
14876     /**
14877      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14878      */
14879     refresh : function(){
14880         //Roo.log('refresh');
14881         var t = this.tpl;
14882         
14883         // if we are using something like 'domtemplate', then
14884         // the what gets used is:
14885         // t.applySubtemplate(NAME, data, wrapping data..)
14886         // the outer template then get' applied with
14887         //     the store 'extra data'
14888         // and the body get's added to the
14889         //      roo-name="data" node?
14890         //      <span class='roo-tpl-{name}'></span> ?????
14891         
14892         
14893         
14894         this.clearSelections();
14895         this.el.update("");
14896         var html = [];
14897         var records = this.store.getRange();
14898         if(records.length < 1) {
14899             
14900             // is this valid??  = should it render a template??
14901             
14902             this.el.update(this.emptyText);
14903             return;
14904         }
14905         var el = this.el;
14906         if (this.dataName) {
14907             this.el.update(t.apply(this.store.meta)); //????
14908             el = this.el.child('.roo-tpl-' + this.dataName);
14909         }
14910         
14911         for(var i = 0, len = records.length; i < len; i++){
14912             var data = this.prepareData(records[i].data, i, records[i]);
14913             this.fireEvent("preparedata", this, data, i, records[i]);
14914             
14915             var d = Roo.apply({}, data);
14916             
14917             if(this.tickable){
14918                 Roo.apply(d, {'roo-id' : Roo.id()});
14919                 
14920                 var _this = this;
14921             
14922                 Roo.each(this.parent.item, function(item){
14923                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14924                         return;
14925                     }
14926                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14927                 });
14928             }
14929             
14930             html[html.length] = Roo.util.Format.trim(
14931                 this.dataName ?
14932                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14933                     t.apply(d)
14934             );
14935         }
14936         
14937         
14938         
14939         el.update(html.join(""));
14940         this.nodes = el.dom.childNodes;
14941         this.updateIndexes(0);
14942     },
14943     
14944
14945     /**
14946      * Function to override to reformat the data that is sent to
14947      * the template for each node.
14948      * DEPRICATED - use the preparedata event handler.
14949      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14950      * a JSON object for an UpdateManager bound view).
14951      */
14952     prepareData : function(data, index, record)
14953     {
14954         this.fireEvent("preparedata", this, data, index, record);
14955         return data;
14956     },
14957
14958     onUpdate : function(ds, record){
14959         // Roo.log('on update');   
14960         this.clearSelections();
14961         var index = this.store.indexOf(record);
14962         var n = this.nodes[index];
14963         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14964         n.parentNode.removeChild(n);
14965         this.updateIndexes(index, index);
14966     },
14967
14968     
14969     
14970 // --------- FIXME     
14971     onAdd : function(ds, records, index)
14972     {
14973         //Roo.log(['on Add', ds, records, index] );        
14974         this.clearSelections();
14975         if(this.nodes.length == 0){
14976             this.refresh();
14977             return;
14978         }
14979         var n = this.nodes[index];
14980         for(var i = 0, len = records.length; i < len; i++){
14981             var d = this.prepareData(records[i].data, i, records[i]);
14982             if(n){
14983                 this.tpl.insertBefore(n, d);
14984             }else{
14985                 
14986                 this.tpl.append(this.el, d);
14987             }
14988         }
14989         this.updateIndexes(index);
14990     },
14991
14992     onRemove : function(ds, record, index){
14993        // Roo.log('onRemove');
14994         this.clearSelections();
14995         var el = this.dataName  ?
14996             this.el.child('.roo-tpl-' + this.dataName) :
14997             this.el; 
14998         
14999         el.dom.removeChild(this.nodes[index]);
15000         this.updateIndexes(index);
15001     },
15002
15003     /**
15004      * Refresh an individual node.
15005      * @param {Number} index
15006      */
15007     refreshNode : function(index){
15008         this.onUpdate(this.store, this.store.getAt(index));
15009     },
15010
15011     updateIndexes : function(startIndex, endIndex){
15012         var ns = this.nodes;
15013         startIndex = startIndex || 0;
15014         endIndex = endIndex || ns.length - 1;
15015         for(var i = startIndex; i <= endIndex; i++){
15016             ns[i].nodeIndex = i;
15017         }
15018     },
15019
15020     /**
15021      * Changes the data store this view uses and refresh the view.
15022      * @param {Store} store
15023      */
15024     setStore : function(store, initial){
15025         if(!initial && this.store){
15026             this.store.un("datachanged", this.refresh);
15027             this.store.un("add", this.onAdd);
15028             this.store.un("remove", this.onRemove);
15029             this.store.un("update", this.onUpdate);
15030             this.store.un("clear", this.refresh);
15031             this.store.un("beforeload", this.onBeforeLoad);
15032             this.store.un("load", this.onLoad);
15033             this.store.un("loadexception", this.onLoad);
15034         }
15035         if(store){
15036           
15037             store.on("datachanged", this.refresh, this);
15038             store.on("add", this.onAdd, this);
15039             store.on("remove", this.onRemove, this);
15040             store.on("update", this.onUpdate, this);
15041             store.on("clear", this.refresh, this);
15042             store.on("beforeload", this.onBeforeLoad, this);
15043             store.on("load", this.onLoad, this);
15044             store.on("loadexception", this.onLoad, this);
15045         }
15046         
15047         if(store){
15048             this.refresh();
15049         }
15050     },
15051     /**
15052      * onbeforeLoad - masks the loading area.
15053      *
15054      */
15055     onBeforeLoad : function(store,opts)
15056     {
15057          //Roo.log('onBeforeLoad');   
15058         if (!opts.add) {
15059             this.el.update("");
15060         }
15061         this.el.mask(this.mask ? this.mask : "Loading" ); 
15062     },
15063     onLoad : function ()
15064     {
15065         this.el.unmask();
15066     },
15067     
15068
15069     /**
15070      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15071      * @param {HTMLElement} node
15072      * @return {HTMLElement} The template node
15073      */
15074     findItemFromChild : function(node){
15075         var el = this.dataName  ?
15076             this.el.child('.roo-tpl-' + this.dataName,true) :
15077             this.el.dom; 
15078         
15079         if(!node || node.parentNode == el){
15080                     return node;
15081             }
15082             var p = node.parentNode;
15083             while(p && p != el){
15084             if(p.parentNode == el){
15085                 return p;
15086             }
15087             p = p.parentNode;
15088         }
15089             return null;
15090     },
15091
15092     /** @ignore */
15093     onClick : function(e){
15094         var item = this.findItemFromChild(e.getTarget());
15095         if(item){
15096             var index = this.indexOf(item);
15097             if(this.onItemClick(item, index, e) !== false){
15098                 this.fireEvent("click", this, index, item, e);
15099             }
15100         }else{
15101             this.clearSelections();
15102         }
15103     },
15104
15105     /** @ignore */
15106     onContextMenu : function(e){
15107         var item = this.findItemFromChild(e.getTarget());
15108         if(item){
15109             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15110         }
15111     },
15112
15113     /** @ignore */
15114     onDblClick : function(e){
15115         var item = this.findItemFromChild(e.getTarget());
15116         if(item){
15117             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15118         }
15119     },
15120
15121     onItemClick : function(item, index, e)
15122     {
15123         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15124             return false;
15125         }
15126         if (this.toggleSelect) {
15127             var m = this.isSelected(item) ? 'unselect' : 'select';
15128             //Roo.log(m);
15129             var _t = this;
15130             _t[m](item, true, false);
15131             return true;
15132         }
15133         if(this.multiSelect || this.singleSelect){
15134             if(this.multiSelect && e.shiftKey && this.lastSelection){
15135                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15136             }else{
15137                 this.select(item, this.multiSelect && e.ctrlKey);
15138                 this.lastSelection = item;
15139             }
15140             
15141             if(!this.tickable){
15142                 e.preventDefault();
15143             }
15144             
15145         }
15146         return true;
15147     },
15148
15149     /**
15150      * Get the number of selected nodes.
15151      * @return {Number}
15152      */
15153     getSelectionCount : function(){
15154         return this.selections.length;
15155     },
15156
15157     /**
15158      * Get the currently selected nodes.
15159      * @return {Array} An array of HTMLElements
15160      */
15161     getSelectedNodes : function(){
15162         return this.selections;
15163     },
15164
15165     /**
15166      * Get the indexes of the selected nodes.
15167      * @return {Array}
15168      */
15169     getSelectedIndexes : function(){
15170         var indexes = [], s = this.selections;
15171         for(var i = 0, len = s.length; i < len; i++){
15172             indexes.push(s[i].nodeIndex);
15173         }
15174         return indexes;
15175     },
15176
15177     /**
15178      * Clear all selections
15179      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15180      */
15181     clearSelections : function(suppressEvent){
15182         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15183             this.cmp.elements = this.selections;
15184             this.cmp.removeClass(this.selectedClass);
15185             this.selections = [];
15186             if(!suppressEvent){
15187                 this.fireEvent("selectionchange", this, this.selections);
15188             }
15189         }
15190     },
15191
15192     /**
15193      * Returns true if the passed node is selected
15194      * @param {HTMLElement/Number} node The node or node index
15195      * @return {Boolean}
15196      */
15197     isSelected : function(node){
15198         var s = this.selections;
15199         if(s.length < 1){
15200             return false;
15201         }
15202         node = this.getNode(node);
15203         return s.indexOf(node) !== -1;
15204     },
15205
15206     /**
15207      * Selects nodes.
15208      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
15209      * @param {Boolean} keepExisting (optional) true to keep existing selections
15210      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15211      */
15212     select : function(nodeInfo, keepExisting, suppressEvent){
15213         if(nodeInfo instanceof Array){
15214             if(!keepExisting){
15215                 this.clearSelections(true);
15216             }
15217             for(var i = 0, len = nodeInfo.length; i < len; i++){
15218                 this.select(nodeInfo[i], true, true);
15219             }
15220             return;
15221         } 
15222         var node = this.getNode(nodeInfo);
15223         if(!node || this.isSelected(node)){
15224             return; // already selected.
15225         }
15226         if(!keepExisting){
15227             this.clearSelections(true);
15228         }
15229         
15230         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15231             Roo.fly(node).addClass(this.selectedClass);
15232             this.selections.push(node);
15233             if(!suppressEvent){
15234                 this.fireEvent("selectionchange", this, this.selections);
15235             }
15236         }
15237         
15238         
15239     },
15240       /**
15241      * Unselects nodes.
15242      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
15243      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15244      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15245      */
15246     unselect : function(nodeInfo, keepExisting, suppressEvent)
15247     {
15248         if(nodeInfo instanceof Array){
15249             Roo.each(this.selections, function(s) {
15250                 this.unselect(s, nodeInfo);
15251             }, this);
15252             return;
15253         }
15254         var node = this.getNode(nodeInfo);
15255         if(!node || !this.isSelected(node)){
15256             //Roo.log("not selected");
15257             return; // not selected.
15258         }
15259         // fireevent???
15260         var ns = [];
15261         Roo.each(this.selections, function(s) {
15262             if (s == node ) {
15263                 Roo.fly(node).removeClass(this.selectedClass);
15264
15265                 return;
15266             }
15267             ns.push(s);
15268         },this);
15269         
15270         this.selections= ns;
15271         this.fireEvent("selectionchange", this, this.selections);
15272     },
15273
15274     /**
15275      * Gets a template node.
15276      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15277      * @return {HTMLElement} The node or null if it wasn't found
15278      */
15279     getNode : function(nodeInfo){
15280         if(typeof nodeInfo == "string"){
15281             return document.getElementById(nodeInfo);
15282         }else if(typeof nodeInfo == "number"){
15283             return this.nodes[nodeInfo];
15284         }
15285         return nodeInfo;
15286     },
15287
15288     /**
15289      * Gets a range template nodes.
15290      * @param {Number} startIndex
15291      * @param {Number} endIndex
15292      * @return {Array} An array of nodes
15293      */
15294     getNodes : function(start, end){
15295         var ns = this.nodes;
15296         start = start || 0;
15297         end = typeof end == "undefined" ? ns.length - 1 : end;
15298         var nodes = [];
15299         if(start <= end){
15300             for(var i = start; i <= end; i++){
15301                 nodes.push(ns[i]);
15302             }
15303         } else{
15304             for(var i = start; i >= end; i--){
15305                 nodes.push(ns[i]);
15306             }
15307         }
15308         return nodes;
15309     },
15310
15311     /**
15312      * Finds the index of the passed node
15313      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15314      * @return {Number} The index of the node or -1
15315      */
15316     indexOf : function(node){
15317         node = this.getNode(node);
15318         if(typeof node.nodeIndex == "number"){
15319             return node.nodeIndex;
15320         }
15321         var ns = this.nodes;
15322         for(var i = 0, len = ns.length; i < len; i++){
15323             if(ns[i] == node){
15324                 return i;
15325             }
15326         }
15327         return -1;
15328     }
15329 });
15330 /*
15331  * - LGPL
15332  *
15333  * based on jquery fullcalendar
15334  * 
15335  */
15336
15337 Roo.bootstrap = Roo.bootstrap || {};
15338 /**
15339  * @class Roo.bootstrap.Calendar
15340  * @extends Roo.bootstrap.Component
15341  * Bootstrap Calendar class
15342  * @cfg {Boolean} loadMask (true|false) default false
15343  * @cfg {Object} header generate the user specific header of the calendar, default false
15344
15345  * @constructor
15346  * Create a new Container
15347  * @param {Object} config The config object
15348  */
15349
15350
15351
15352 Roo.bootstrap.Calendar = function(config){
15353     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15354      this.addEvents({
15355         /**
15356              * @event select
15357              * Fires when a date is selected
15358              * @param {DatePicker} this
15359              * @param {Date} date The selected date
15360              */
15361         'select': true,
15362         /**
15363              * @event monthchange
15364              * Fires when the displayed month changes 
15365              * @param {DatePicker} this
15366              * @param {Date} date The selected month
15367              */
15368         'monthchange': true,
15369         /**
15370              * @event evententer
15371              * Fires when mouse over an event
15372              * @param {Calendar} this
15373              * @param {event} Event
15374              */
15375         'evententer': true,
15376         /**
15377              * @event eventleave
15378              * Fires when the mouse leaves an
15379              * @param {Calendar} this
15380              * @param {event}
15381              */
15382         'eventleave': true,
15383         /**
15384              * @event eventclick
15385              * Fires when the mouse click an
15386              * @param {Calendar} this
15387              * @param {event}
15388              */
15389         'eventclick': true
15390         
15391     });
15392
15393 };
15394
15395 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15396     
15397      /**
15398      * @cfg {Number} startDay
15399      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15400      */
15401     startDay : 0,
15402     
15403     loadMask : false,
15404     
15405     header : false,
15406       
15407     getAutoCreate : function(){
15408         
15409         
15410         var fc_button = function(name, corner, style, content ) {
15411             return Roo.apply({},{
15412                 tag : 'span',
15413                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15414                          (corner.length ?
15415                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15416                             ''
15417                         ),
15418                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15419                 unselectable: 'on'
15420             });
15421         };
15422         
15423         var header = {};
15424         
15425         if(!this.header){
15426             header = {
15427                 tag : 'table',
15428                 cls : 'fc-header',
15429                 style : 'width:100%',
15430                 cn : [
15431                     {
15432                         tag: 'tr',
15433                         cn : [
15434                             {
15435                                 tag : 'td',
15436                                 cls : 'fc-header-left',
15437                                 cn : [
15438                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15439                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15440                                     { tag: 'span', cls: 'fc-header-space' },
15441                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15442
15443
15444                                 ]
15445                             },
15446
15447                             {
15448                                 tag : 'td',
15449                                 cls : 'fc-header-center',
15450                                 cn : [
15451                                     {
15452                                         tag: 'span',
15453                                         cls: 'fc-header-title',
15454                                         cn : {
15455                                             tag: 'H2',
15456                                             html : 'month / year'
15457                                         }
15458                                     }
15459
15460                                 ]
15461                             },
15462                             {
15463                                 tag : 'td',
15464                                 cls : 'fc-header-right',
15465                                 cn : [
15466                               /*      fc_button('month', 'left', '', 'month' ),
15467                                     fc_button('week', '', '', 'week' ),
15468                                     fc_button('day', 'right', '', 'day' )
15469                                 */    
15470
15471                                 ]
15472                             }
15473
15474                         ]
15475                     }
15476                 ]
15477             };
15478         }
15479         
15480         header = this.header;
15481         
15482        
15483         var cal_heads = function() {
15484             var ret = [];
15485             // fixme - handle this.
15486             
15487             for (var i =0; i < Date.dayNames.length; i++) {
15488                 var d = Date.dayNames[i];
15489                 ret.push({
15490                     tag: 'th',
15491                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15492                     html : d.substring(0,3)
15493                 });
15494                 
15495             }
15496             ret[0].cls += ' fc-first';
15497             ret[6].cls += ' fc-last';
15498             return ret;
15499         };
15500         var cal_cell = function(n) {
15501             return  {
15502                 tag: 'td',
15503                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15504                 cn : [
15505                     {
15506                         cn : [
15507                             {
15508                                 cls: 'fc-day-number',
15509                                 html: 'D'
15510                             },
15511                             {
15512                                 cls: 'fc-day-content',
15513                              
15514                                 cn : [
15515                                      {
15516                                         style: 'position: relative;' // height: 17px;
15517                                     }
15518                                 ]
15519                             }
15520                             
15521                             
15522                         ]
15523                     }
15524                 ]
15525                 
15526             }
15527         };
15528         var cal_rows = function() {
15529             
15530             var ret = [];
15531             for (var r = 0; r < 6; r++) {
15532                 var row= {
15533                     tag : 'tr',
15534                     cls : 'fc-week',
15535                     cn : []
15536                 };
15537                 
15538                 for (var i =0; i < Date.dayNames.length; i++) {
15539                     var d = Date.dayNames[i];
15540                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15541
15542                 }
15543                 row.cn[0].cls+=' fc-first';
15544                 row.cn[0].cn[0].style = 'min-height:90px';
15545                 row.cn[6].cls+=' fc-last';
15546                 ret.push(row);
15547                 
15548             }
15549             ret[0].cls += ' fc-first';
15550             ret[4].cls += ' fc-prev-last';
15551             ret[5].cls += ' fc-last';
15552             return ret;
15553             
15554         };
15555         
15556         var cal_table = {
15557             tag: 'table',
15558             cls: 'fc-border-separate',
15559             style : 'width:100%',
15560             cellspacing  : 0,
15561             cn : [
15562                 { 
15563                     tag: 'thead',
15564                     cn : [
15565                         { 
15566                             tag: 'tr',
15567                             cls : 'fc-first fc-last',
15568                             cn : cal_heads()
15569                         }
15570                     ]
15571                 },
15572                 { 
15573                     tag: 'tbody',
15574                     cn : cal_rows()
15575                 }
15576                   
15577             ]
15578         };
15579          
15580          var cfg = {
15581             cls : 'fc fc-ltr',
15582             cn : [
15583                 header,
15584                 {
15585                     cls : 'fc-content',
15586                     style : "position: relative;",
15587                     cn : [
15588                         {
15589                             cls : 'fc-view fc-view-month fc-grid',
15590                             style : 'position: relative',
15591                             unselectable : 'on',
15592                             cn : [
15593                                 {
15594                                     cls : 'fc-event-container',
15595                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15596                                 },
15597                                 cal_table
15598                             ]
15599                         }
15600                     ]
15601     
15602                 }
15603            ] 
15604             
15605         };
15606         
15607          
15608         
15609         return cfg;
15610     },
15611     
15612     
15613     initEvents : function()
15614     {
15615         if(!this.store){
15616             throw "can not find store for calendar";
15617         }
15618         
15619         var mark = {
15620             tag: "div",
15621             cls:"x-dlg-mask",
15622             style: "text-align:center",
15623             cn: [
15624                 {
15625                     tag: "div",
15626                     style: "background-color:white;width:50%;margin:250 auto",
15627                     cn: [
15628                         {
15629                             tag: "img",
15630                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15631                         },
15632                         {
15633                             tag: "span",
15634                             html: "Loading"
15635                         }
15636                         
15637                     ]
15638                 }
15639             ]
15640         };
15641         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15642         
15643         var size = this.el.select('.fc-content', true).first().getSize();
15644         this.maskEl.setSize(size.width, size.height);
15645         this.maskEl.enableDisplayMode("block");
15646         if(!this.loadMask){
15647             this.maskEl.hide();
15648         }
15649         
15650         this.store = Roo.factory(this.store, Roo.data);
15651         this.store.on('load', this.onLoad, this);
15652         this.store.on('beforeload', this.onBeforeLoad, this);
15653         
15654         this.resize();
15655         
15656         this.cells = this.el.select('.fc-day',true);
15657         //Roo.log(this.cells);
15658         this.textNodes = this.el.query('.fc-day-number');
15659         this.cells.addClassOnOver('fc-state-hover');
15660         
15661         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15662         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15663         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15664         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15665         
15666         this.on('monthchange', this.onMonthChange, this);
15667         
15668         this.update(new Date().clearTime());
15669     },
15670     
15671     resize : function() {
15672         var sz  = this.el.getSize();
15673         
15674         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15675         this.el.select('.fc-day-content div',true).setHeight(34);
15676     },
15677     
15678     
15679     // private
15680     showPrevMonth : function(e){
15681         this.update(this.activeDate.add("mo", -1));
15682     },
15683     showToday : function(e){
15684         this.update(new Date().clearTime());
15685     },
15686     // private
15687     showNextMonth : function(e){
15688         this.update(this.activeDate.add("mo", 1));
15689     },
15690
15691     // private
15692     showPrevYear : function(){
15693         this.update(this.activeDate.add("y", -1));
15694     },
15695
15696     // private
15697     showNextYear : function(){
15698         this.update(this.activeDate.add("y", 1));
15699     },
15700
15701     
15702    // private
15703     update : function(date)
15704     {
15705         var vd = this.activeDate;
15706         this.activeDate = date;
15707 //        if(vd && this.el){
15708 //            var t = date.getTime();
15709 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15710 //                Roo.log('using add remove');
15711 //                
15712 //                this.fireEvent('monthchange', this, date);
15713 //                
15714 //                this.cells.removeClass("fc-state-highlight");
15715 //                this.cells.each(function(c){
15716 //                   if(c.dateValue == t){
15717 //                       c.addClass("fc-state-highlight");
15718 //                       setTimeout(function(){
15719 //                            try{c.dom.firstChild.focus();}catch(e){}
15720 //                       }, 50);
15721 //                       return false;
15722 //                   }
15723 //                   return true;
15724 //                });
15725 //                return;
15726 //            }
15727 //        }
15728         
15729         var days = date.getDaysInMonth();
15730         
15731         var firstOfMonth = date.getFirstDateOfMonth();
15732         var startingPos = firstOfMonth.getDay()-this.startDay;
15733         
15734         if(startingPos < this.startDay){
15735             startingPos += 7;
15736         }
15737         
15738         var pm = date.add(Date.MONTH, -1);
15739         var prevStart = pm.getDaysInMonth()-startingPos;
15740 //        
15741         this.cells = this.el.select('.fc-day',true);
15742         this.textNodes = this.el.query('.fc-day-number');
15743         this.cells.addClassOnOver('fc-state-hover');
15744         
15745         var cells = this.cells.elements;
15746         var textEls = this.textNodes;
15747         
15748         Roo.each(cells, function(cell){
15749             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15750         });
15751         
15752         days += startingPos;
15753
15754         // convert everything to numbers so it's fast
15755         var day = 86400000;
15756         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15757         //Roo.log(d);
15758         //Roo.log(pm);
15759         //Roo.log(prevStart);
15760         
15761         var today = new Date().clearTime().getTime();
15762         var sel = date.clearTime().getTime();
15763         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15764         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15765         var ddMatch = this.disabledDatesRE;
15766         var ddText = this.disabledDatesText;
15767         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15768         var ddaysText = this.disabledDaysText;
15769         var format = this.format;
15770         
15771         var setCellClass = function(cal, cell){
15772             cell.row = 0;
15773             cell.events = [];
15774             cell.more = [];
15775             //Roo.log('set Cell Class');
15776             cell.title = "";
15777             var t = d.getTime();
15778             
15779             //Roo.log(d);
15780             
15781             cell.dateValue = t;
15782             if(t == today){
15783                 cell.className += " fc-today";
15784                 cell.className += " fc-state-highlight";
15785                 cell.title = cal.todayText;
15786             }
15787             if(t == sel){
15788                 // disable highlight in other month..
15789                 //cell.className += " fc-state-highlight";
15790                 
15791             }
15792             // disabling
15793             if(t < min) {
15794                 cell.className = " fc-state-disabled";
15795                 cell.title = cal.minText;
15796                 return;
15797             }
15798             if(t > max) {
15799                 cell.className = " fc-state-disabled";
15800                 cell.title = cal.maxText;
15801                 return;
15802             }
15803             if(ddays){
15804                 if(ddays.indexOf(d.getDay()) != -1){
15805                     cell.title = ddaysText;
15806                     cell.className = " fc-state-disabled";
15807                 }
15808             }
15809             if(ddMatch && format){
15810                 var fvalue = d.dateFormat(format);
15811                 if(ddMatch.test(fvalue)){
15812                     cell.title = ddText.replace("%0", fvalue);
15813                     cell.className = " fc-state-disabled";
15814                 }
15815             }
15816             
15817             if (!cell.initialClassName) {
15818                 cell.initialClassName = cell.dom.className;
15819             }
15820             
15821             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15822         };
15823
15824         var i = 0;
15825         
15826         for(; i < startingPos; i++) {
15827             textEls[i].innerHTML = (++prevStart);
15828             d.setDate(d.getDate()+1);
15829             
15830             cells[i].className = "fc-past fc-other-month";
15831             setCellClass(this, cells[i]);
15832         }
15833         
15834         var intDay = 0;
15835         
15836         for(; i < days; i++){
15837             intDay = i - startingPos + 1;
15838             textEls[i].innerHTML = (intDay);
15839             d.setDate(d.getDate()+1);
15840             
15841             cells[i].className = ''; // "x-date-active";
15842             setCellClass(this, cells[i]);
15843         }
15844         var extraDays = 0;
15845         
15846         for(; i < 42; i++) {
15847             textEls[i].innerHTML = (++extraDays);
15848             d.setDate(d.getDate()+1);
15849             
15850             cells[i].className = "fc-future fc-other-month";
15851             setCellClass(this, cells[i]);
15852         }
15853         
15854         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15855         
15856         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15857         
15858         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15859         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15860         
15861         if(totalRows != 6){
15862             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15863             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15864         }
15865         
15866         this.fireEvent('monthchange', this, date);
15867         
15868         
15869         /*
15870         if(!this.internalRender){
15871             var main = this.el.dom.firstChild;
15872             var w = main.offsetWidth;
15873             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15874             Roo.fly(main).setWidth(w);
15875             this.internalRender = true;
15876             // opera does not respect the auto grow header center column
15877             // then, after it gets a width opera refuses to recalculate
15878             // without a second pass
15879             if(Roo.isOpera && !this.secondPass){
15880                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15881                 this.secondPass = true;
15882                 this.update.defer(10, this, [date]);
15883             }
15884         }
15885         */
15886         
15887     },
15888     
15889     findCell : function(dt) {
15890         dt = dt.clearTime().getTime();
15891         var ret = false;
15892         this.cells.each(function(c){
15893             //Roo.log("check " +c.dateValue + '?=' + dt);
15894             if(c.dateValue == dt){
15895                 ret = c;
15896                 return false;
15897             }
15898             return true;
15899         });
15900         
15901         return ret;
15902     },
15903     
15904     findCells : function(ev) {
15905         var s = ev.start.clone().clearTime().getTime();
15906        // Roo.log(s);
15907         var e= ev.end.clone().clearTime().getTime();
15908        // Roo.log(e);
15909         var ret = [];
15910         this.cells.each(function(c){
15911              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15912             
15913             if(c.dateValue > e){
15914                 return ;
15915             }
15916             if(c.dateValue < s){
15917                 return ;
15918             }
15919             ret.push(c);
15920         });
15921         
15922         return ret;    
15923     },
15924     
15925 //    findBestRow: function(cells)
15926 //    {
15927 //        var ret = 0;
15928 //        
15929 //        for (var i =0 ; i < cells.length;i++) {
15930 //            ret  = Math.max(cells[i].rows || 0,ret);
15931 //        }
15932 //        return ret;
15933 //        
15934 //    },
15935     
15936     
15937     addItem : function(ev)
15938     {
15939         // look for vertical location slot in
15940         var cells = this.findCells(ev);
15941         
15942 //        ev.row = this.findBestRow(cells);
15943         
15944         // work out the location.
15945         
15946         var crow = false;
15947         var rows = [];
15948         for(var i =0; i < cells.length; i++) {
15949             
15950             cells[i].row = cells[0].row;
15951             
15952             if(i == 0){
15953                 cells[i].row = cells[i].row + 1;
15954             }
15955             
15956             if (!crow) {
15957                 crow = {
15958                     start : cells[i],
15959                     end :  cells[i]
15960                 };
15961                 continue;
15962             }
15963             if (crow.start.getY() == cells[i].getY()) {
15964                 // on same row.
15965                 crow.end = cells[i];
15966                 continue;
15967             }
15968             // different row.
15969             rows.push(crow);
15970             crow = {
15971                 start: cells[i],
15972                 end : cells[i]
15973             };
15974             
15975         }
15976         
15977         rows.push(crow);
15978         ev.els = [];
15979         ev.rows = rows;
15980         ev.cells = cells;
15981         
15982         cells[0].events.push(ev);
15983         
15984         this.calevents.push(ev);
15985     },
15986     
15987     clearEvents: function() {
15988         
15989         if(!this.calevents){
15990             return;
15991         }
15992         
15993         Roo.each(this.cells.elements, function(c){
15994             c.row = 0;
15995             c.events = [];
15996             c.more = [];
15997         });
15998         
15999         Roo.each(this.calevents, function(e) {
16000             Roo.each(e.els, function(el) {
16001                 el.un('mouseenter' ,this.onEventEnter, this);
16002                 el.un('mouseleave' ,this.onEventLeave, this);
16003                 el.remove();
16004             },this);
16005         },this);
16006         
16007         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16008             e.remove();
16009         });
16010         
16011     },
16012     
16013     renderEvents: function()
16014     {   
16015         var _this = this;
16016         
16017         this.cells.each(function(c) {
16018             
16019             if(c.row < 5){
16020                 return;
16021             }
16022             
16023             var ev = c.events;
16024             
16025             var r = 4;
16026             if(c.row != c.events.length){
16027                 r = 4 - (4 - (c.row - c.events.length));
16028             }
16029             
16030             c.events = ev.slice(0, r);
16031             c.more = ev.slice(r);
16032             
16033             if(c.more.length && c.more.length == 1){
16034                 c.events.push(c.more.pop());
16035             }
16036             
16037             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16038             
16039         });
16040             
16041         this.cells.each(function(c) {
16042             
16043             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16044             
16045             
16046             for (var e = 0; e < c.events.length; e++){
16047                 var ev = c.events[e];
16048                 var rows = ev.rows;
16049                 
16050                 for(var i = 0; i < rows.length; i++) {
16051                 
16052                     // how many rows should it span..
16053
16054                     var  cfg = {
16055                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16056                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16057
16058                         unselectable : "on",
16059                         cn : [
16060                             {
16061                                 cls: 'fc-event-inner',
16062                                 cn : [
16063     //                                {
16064     //                                  tag:'span',
16065     //                                  cls: 'fc-event-time',
16066     //                                  html : cells.length > 1 ? '' : ev.time
16067     //                                },
16068                                     {
16069                                       tag:'span',
16070                                       cls: 'fc-event-title',
16071                                       html : String.format('{0}', ev.title)
16072                                     }
16073
16074
16075                                 ]
16076                             },
16077                             {
16078                                 cls: 'ui-resizable-handle ui-resizable-e',
16079                                 html : '&nbsp;&nbsp;&nbsp'
16080                             }
16081
16082                         ]
16083                     };
16084
16085                     if (i == 0) {
16086                         cfg.cls += ' fc-event-start';
16087                     }
16088                     if ((i+1) == rows.length) {
16089                         cfg.cls += ' fc-event-end';
16090                     }
16091
16092                     var ctr = _this.el.select('.fc-event-container',true).first();
16093                     var cg = ctr.createChild(cfg);
16094
16095                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16096                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16097
16098                     var r = (c.more.length) ? 1 : 0;
16099                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16100                     cg.setWidth(ebox.right - sbox.x -2);
16101
16102                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16103                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16104                     cg.on('click', _this.onEventClick, _this, ev);
16105
16106                     ev.els.push(cg);
16107                     
16108                 }
16109                 
16110             }
16111             
16112             
16113             if(c.more.length){
16114                 var  cfg = {
16115                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16116                     style : 'position: absolute',
16117                     unselectable : "on",
16118                     cn : [
16119                         {
16120                             cls: 'fc-event-inner',
16121                             cn : [
16122                                 {
16123                                   tag:'span',
16124                                   cls: 'fc-event-title',
16125                                   html : 'More'
16126                                 }
16127
16128
16129                             ]
16130                         },
16131                         {
16132                             cls: 'ui-resizable-handle ui-resizable-e',
16133                             html : '&nbsp;&nbsp;&nbsp'
16134                         }
16135
16136                     ]
16137                 };
16138
16139                 var ctr = _this.el.select('.fc-event-container',true).first();
16140                 var cg = ctr.createChild(cfg);
16141
16142                 var sbox = c.select('.fc-day-content',true).first().getBox();
16143                 var ebox = c.select('.fc-day-content',true).first().getBox();
16144                 //Roo.log(cg);
16145                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16146                 cg.setWidth(ebox.right - sbox.x -2);
16147
16148                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16149                 
16150             }
16151             
16152         });
16153         
16154         
16155         
16156     },
16157     
16158     onEventEnter: function (e, el,event,d) {
16159         this.fireEvent('evententer', this, el, event);
16160     },
16161     
16162     onEventLeave: function (e, el,event,d) {
16163         this.fireEvent('eventleave', this, el, event);
16164     },
16165     
16166     onEventClick: function (e, el,event,d) {
16167         this.fireEvent('eventclick', this, el, event);
16168     },
16169     
16170     onMonthChange: function () {
16171         this.store.load();
16172     },
16173     
16174     onMoreEventClick: function(e, el, more)
16175     {
16176         var _this = this;
16177         
16178         this.calpopover.placement = 'right';
16179         this.calpopover.setTitle('More');
16180         
16181         this.calpopover.setContent('');
16182         
16183         var ctr = this.calpopover.el.select('.popover-content', true).first();
16184         
16185         Roo.each(more, function(m){
16186             var cfg = {
16187                 cls : 'fc-event-hori fc-event-draggable',
16188                 html : m.title
16189             };
16190             var cg = ctr.createChild(cfg);
16191             
16192             cg.on('click', _this.onEventClick, _this, m);
16193         });
16194         
16195         this.calpopover.show(el);
16196         
16197         
16198     },
16199     
16200     onLoad: function () 
16201     {   
16202         this.calevents = [];
16203         var cal = this;
16204         
16205         if(this.store.getCount() > 0){
16206             this.store.data.each(function(d){
16207                cal.addItem({
16208                     id : d.data.id,
16209                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16210                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16211                     time : d.data.start_time,
16212                     title : d.data.title,
16213                     description : d.data.description,
16214                     venue : d.data.venue
16215                 });
16216             });
16217         }
16218         
16219         this.renderEvents();
16220         
16221         if(this.calevents.length && this.loadMask){
16222             this.maskEl.hide();
16223         }
16224     },
16225     
16226     onBeforeLoad: function()
16227     {
16228         this.clearEvents();
16229         if(this.loadMask){
16230             this.maskEl.show();
16231         }
16232     }
16233 });
16234
16235  
16236  /*
16237  * - LGPL
16238  *
16239  * element
16240  * 
16241  */
16242
16243 /**
16244  * @class Roo.bootstrap.Popover
16245  * @extends Roo.bootstrap.Component
16246  * Bootstrap Popover class
16247  * @cfg {String} html contents of the popover   (or false to use children..)
16248  * @cfg {String} title of popover (or false to hide)
16249  * @cfg {String} placement how it is placed
16250  * @cfg {String} trigger click || hover (or false to trigger manually)
16251  * @cfg {String} over what (parent or false to trigger manually.)
16252  * @cfg {Number} delay - delay before showing
16253  
16254  * @constructor
16255  * Create a new Popover
16256  * @param {Object} config The config object
16257  */
16258
16259 Roo.bootstrap.Popover = function(config){
16260     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16261     
16262     this.addEvents({
16263         // raw events
16264          /**
16265          * @event show
16266          * After the popover show
16267          * 
16268          * @param {Roo.bootstrap.Popover} this
16269          */
16270         "show" : true,
16271         /**
16272          * @event hide
16273          * After the popover hide
16274          * 
16275          * @param {Roo.bootstrap.Popover} this
16276          */
16277         "hide" : true
16278     });
16279 };
16280
16281 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16282     
16283     title: 'Fill in a title',
16284     html: false,
16285     
16286     placement : 'right',
16287     trigger : 'hover', // hover
16288     
16289     delay : 0,
16290     
16291     over: 'parent',
16292     
16293     can_build_overlaid : false,
16294     
16295     getChildContainer : function()
16296     {
16297         return this.el.select('.popover-content',true).first();
16298     },
16299     
16300     getAutoCreate : function(){
16301          
16302         var cfg = {
16303            cls : 'popover roo-dynamic',
16304            style: 'display:block',
16305            cn : [
16306                 {
16307                     cls : 'arrow'
16308                 },
16309                 {
16310                     cls : 'popover-inner',
16311                     cn : [
16312                         {
16313                             tag: 'h3',
16314                             cls: 'popover-title',
16315                             html : this.title
16316                         },
16317                         {
16318                             cls : 'popover-content',
16319                             html : this.html
16320                         }
16321                     ]
16322                     
16323                 }
16324            ]
16325         };
16326         
16327         return cfg;
16328     },
16329     setTitle: function(str)
16330     {
16331         this.title = str;
16332         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16333     },
16334     setContent: function(str)
16335     {
16336         this.html = str;
16337         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16338     },
16339     // as it get's added to the bottom of the page.
16340     onRender : function(ct, position)
16341     {
16342         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16343         if(!this.el){
16344             var cfg = Roo.apply({},  this.getAutoCreate());
16345             cfg.id = Roo.id();
16346             
16347             if (this.cls) {
16348                 cfg.cls += ' ' + this.cls;
16349             }
16350             if (this.style) {
16351                 cfg.style = this.style;
16352             }
16353             //Roo.log("adding to ");
16354             this.el = Roo.get(document.body).createChild(cfg, position);
16355 //            Roo.log(this.el);
16356         }
16357         this.initEvents();
16358     },
16359     
16360     initEvents : function()
16361     {
16362         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16363         this.el.enableDisplayMode('block');
16364         this.el.hide();
16365         if (this.over === false) {
16366             return; 
16367         }
16368         if (this.triggers === false) {
16369             return;
16370         }
16371         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16372         var triggers = this.trigger ? this.trigger.split(' ') : [];
16373         Roo.each(triggers, function(trigger) {
16374         
16375             if (trigger == 'click') {
16376                 on_el.on('click', this.toggle, this);
16377             } else if (trigger != 'manual') {
16378                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16379                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16380       
16381                 on_el.on(eventIn  ,this.enter, this);
16382                 on_el.on(eventOut, this.leave, this);
16383             }
16384         }, this);
16385         
16386     },
16387     
16388     
16389     // private
16390     timeout : null,
16391     hoverState : null,
16392     
16393     toggle : function () {
16394         this.hoverState == 'in' ? this.leave() : this.enter();
16395     },
16396     
16397     enter : function () {
16398         
16399         clearTimeout(this.timeout);
16400     
16401         this.hoverState = 'in';
16402     
16403         if (!this.delay || !this.delay.show) {
16404             this.show();
16405             return;
16406         }
16407         var _t = this;
16408         this.timeout = setTimeout(function () {
16409             if (_t.hoverState == 'in') {
16410                 _t.show();
16411             }
16412         }, this.delay.show)
16413     },
16414     
16415     leave : function() {
16416         clearTimeout(this.timeout);
16417     
16418         this.hoverState = 'out';
16419     
16420         if (!this.delay || !this.delay.hide) {
16421             this.hide();
16422             return;
16423         }
16424         var _t = this;
16425         this.timeout = setTimeout(function () {
16426             if (_t.hoverState == 'out') {
16427                 _t.hide();
16428             }
16429         }, this.delay.hide)
16430     },
16431     
16432     show : function (on_el)
16433     {
16434         if (!on_el) {
16435             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16436         }
16437         
16438         // set content.
16439         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16440         if (this.html !== false) {
16441             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16442         }
16443         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16444         if (!this.title.length) {
16445             this.el.select('.popover-title',true).hide();
16446         }
16447         
16448         var placement = typeof this.placement == 'function' ?
16449             this.placement.call(this, this.el, on_el) :
16450             this.placement;
16451             
16452         var autoToken = /\s?auto?\s?/i;
16453         var autoPlace = autoToken.test(placement);
16454         if (autoPlace) {
16455             placement = placement.replace(autoToken, '') || 'top';
16456         }
16457         
16458         //this.el.detach()
16459         //this.el.setXY([0,0]);
16460         this.el.show();
16461         this.el.dom.style.display='block';
16462         this.el.addClass(placement);
16463         
16464         //this.el.appendTo(on_el);
16465         
16466         var p = this.getPosition();
16467         var box = this.el.getBox();
16468         
16469         if (autoPlace) {
16470             // fixme..
16471         }
16472         var align = Roo.bootstrap.Popover.alignment[placement];
16473         this.el.alignTo(on_el, align[0],align[1]);
16474         //var arrow = this.el.select('.arrow',true).first();
16475         //arrow.set(align[2], 
16476         
16477         this.el.addClass('in');
16478         
16479         
16480         if (this.el.hasClass('fade')) {
16481             // fade it?
16482         }
16483         
16484         this.hoverState = 'in';
16485         
16486         this.fireEvent('show', this);
16487         
16488     },
16489     hide : function()
16490     {
16491         this.el.setXY([0,0]);
16492         this.el.removeClass('in');
16493         this.el.hide();
16494         this.hoverState = null;
16495         
16496         this.fireEvent('hide', this);
16497     }
16498     
16499 });
16500
16501 Roo.bootstrap.Popover.alignment = {
16502     'left' : ['r-l', [-10,0], 'right'],
16503     'right' : ['l-r', [10,0], 'left'],
16504     'bottom' : ['t-b', [0,10], 'top'],
16505     'top' : [ 'b-t', [0,-10], 'bottom']
16506 };
16507
16508  /*
16509  * - LGPL
16510  *
16511  * Progress
16512  * 
16513  */
16514
16515 /**
16516  * @class Roo.bootstrap.Progress
16517  * @extends Roo.bootstrap.Component
16518  * Bootstrap Progress class
16519  * @cfg {Boolean} striped striped of the progress bar
16520  * @cfg {Boolean} active animated of the progress bar
16521  * 
16522  * 
16523  * @constructor
16524  * Create a new Progress
16525  * @param {Object} config The config object
16526  */
16527
16528 Roo.bootstrap.Progress = function(config){
16529     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16530 };
16531
16532 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16533     
16534     striped : false,
16535     active: false,
16536     
16537     getAutoCreate : function(){
16538         var cfg = {
16539             tag: 'div',
16540             cls: 'progress'
16541         };
16542         
16543         
16544         if(this.striped){
16545             cfg.cls += ' progress-striped';
16546         }
16547       
16548         if(this.active){
16549             cfg.cls += ' active';
16550         }
16551         
16552         
16553         return cfg;
16554     }
16555    
16556 });
16557
16558  
16559
16560  /*
16561  * - LGPL
16562  *
16563  * ProgressBar
16564  * 
16565  */
16566
16567 /**
16568  * @class Roo.bootstrap.ProgressBar
16569  * @extends Roo.bootstrap.Component
16570  * Bootstrap ProgressBar class
16571  * @cfg {Number} aria_valuenow aria-value now
16572  * @cfg {Number} aria_valuemin aria-value min
16573  * @cfg {Number} aria_valuemax aria-value max
16574  * @cfg {String} label label for the progress bar
16575  * @cfg {String} panel (success | info | warning | danger )
16576  * @cfg {String} role role of the progress bar
16577  * @cfg {String} sr_only text
16578  * 
16579  * 
16580  * @constructor
16581  * Create a new ProgressBar
16582  * @param {Object} config The config object
16583  */
16584
16585 Roo.bootstrap.ProgressBar = function(config){
16586     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16587 };
16588
16589 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16590     
16591     aria_valuenow : 0,
16592     aria_valuemin : 0,
16593     aria_valuemax : 100,
16594     label : false,
16595     panel : false,
16596     role : false,
16597     sr_only: false,
16598     
16599     getAutoCreate : function()
16600     {
16601         
16602         var cfg = {
16603             tag: 'div',
16604             cls: 'progress-bar',
16605             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16606         };
16607         
16608         if(this.sr_only){
16609             cfg.cn = {
16610                 tag: 'span',
16611                 cls: 'sr-only',
16612                 html: this.sr_only
16613             }
16614         }
16615         
16616         if(this.role){
16617             cfg.role = this.role;
16618         }
16619         
16620         if(this.aria_valuenow){
16621             cfg['aria-valuenow'] = this.aria_valuenow;
16622         }
16623         
16624         if(this.aria_valuemin){
16625             cfg['aria-valuemin'] = this.aria_valuemin;
16626         }
16627         
16628         if(this.aria_valuemax){
16629             cfg['aria-valuemax'] = this.aria_valuemax;
16630         }
16631         
16632         if(this.label && !this.sr_only){
16633             cfg.html = this.label;
16634         }
16635         
16636         if(this.panel){
16637             cfg.cls += ' progress-bar-' + this.panel;
16638         }
16639         
16640         return cfg;
16641     },
16642     
16643     update : function(aria_valuenow)
16644     {
16645         this.aria_valuenow = aria_valuenow;
16646         
16647         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16648     }
16649    
16650 });
16651
16652  
16653
16654  /*
16655  * - LGPL
16656  *
16657  * column
16658  * 
16659  */
16660
16661 /**
16662  * @class Roo.bootstrap.TabGroup
16663  * @extends Roo.bootstrap.Column
16664  * Bootstrap Column class
16665  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16666  * @cfg {Boolean} carousel true to make the group behave like a carousel
16667  * @cfg {Boolean} bullets show bullets for the panels
16668  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16669  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16670  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16671  * @cfg {Boolean} showarrow (true|false) show arrow default true
16672  * 
16673  * @constructor
16674  * Create a new TabGroup
16675  * @param {Object} config The config object
16676  */
16677
16678 Roo.bootstrap.TabGroup = function(config){
16679     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16680     if (!this.navId) {
16681         this.navId = Roo.id();
16682     }
16683     this.tabs = [];
16684     Roo.bootstrap.TabGroup.register(this);
16685     
16686 };
16687
16688 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16689     
16690     carousel : false,
16691     transition : false,
16692     bullets : 0,
16693     timer : 0,
16694     autoslide : false,
16695     slideFn : false,
16696     slideOnTouch : false,
16697     showarrow : true,
16698     
16699     getAutoCreate : function()
16700     {
16701         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16702         
16703         cfg.cls += ' tab-content';
16704         
16705         if (this.carousel) {
16706             cfg.cls += ' carousel slide';
16707             
16708             cfg.cn = [{
16709                cls : 'carousel-inner',
16710                cn : []
16711             }];
16712         
16713             if(this.bullets  && !Roo.isTouch){
16714                 
16715                 var bullets = {
16716                     cls : 'carousel-bullets',
16717                     cn : []
16718                 };
16719                
16720                 if(this.bullets_cls){
16721                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16722                 }
16723                 
16724                 bullets.cn.push({
16725                     cls : 'clear'
16726                 });
16727                 
16728                 cfg.cn[0].cn.push(bullets);
16729             }
16730             
16731             if(this.showarrow){
16732                 cfg.cn[0].cn.push({
16733                     tag : 'div',
16734                     class : 'carousel-arrow',
16735                     cn : [
16736                         {
16737                             tag : 'div',
16738                             class : 'carousel-prev',
16739                             cn : [
16740                                 {
16741                                     tag : 'i',
16742                                     class : 'fa fa-chevron-left'
16743                                 }
16744                             ]
16745                         },
16746                         {
16747                             tag : 'div',
16748                             class : 'carousel-next',
16749                             cn : [
16750                                 {
16751                                     tag : 'i',
16752                                     class : 'fa fa-chevron-right'
16753                                 }
16754                             ]
16755                         }
16756                     ]
16757                 });
16758             }
16759             
16760         }
16761         
16762         return cfg;
16763     },
16764     
16765     initEvents:  function()
16766     {
16767         if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16768             this.el.on("touchstart", this.onTouchStart, this);
16769         }
16770         
16771         if(this.autoslide){
16772             var _this = this;
16773             
16774             this.slideFn = window.setInterval(function() {
16775                 _this.showPanelNext();
16776             }, this.timer);
16777         }
16778         
16779         if(this.showarrow){
16780             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16781             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16782         }
16783         
16784         
16785     },
16786     
16787     onTouchStart : function(e, el, o)
16788     {
16789         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16790             return;
16791         }
16792         
16793         this.showPanelNext();
16794     },
16795     
16796     getChildContainer : function()
16797     {
16798         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16799     },
16800     
16801     /**
16802     * register a Navigation item
16803     * @param {Roo.bootstrap.NavItem} the navitem to add
16804     */
16805     register : function(item)
16806     {
16807         this.tabs.push( item);
16808         item.navId = this.navId; // not really needed..
16809         this.addBullet();
16810     
16811     },
16812     
16813     getActivePanel : function()
16814     {
16815         var r = false;
16816         Roo.each(this.tabs, function(t) {
16817             if (t.active) {
16818                 r = t;
16819                 return false;
16820             }
16821             return null;
16822         });
16823         return r;
16824         
16825     },
16826     getPanelByName : function(n)
16827     {
16828         var r = false;
16829         Roo.each(this.tabs, function(t) {
16830             if (t.tabId == n) {
16831                 r = t;
16832                 return false;
16833             }
16834             return null;
16835         });
16836         return r;
16837     },
16838     indexOfPanel : function(p)
16839     {
16840         var r = false;
16841         Roo.each(this.tabs, function(t,i) {
16842             if (t.tabId == p.tabId) {
16843                 r = i;
16844                 return false;
16845             }
16846             return null;
16847         });
16848         return r;
16849     },
16850     /**
16851      * show a specific panel
16852      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16853      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16854      */
16855     showPanel : function (pan)
16856     {
16857         if(this.transition || typeof(pan) == 'undefined'){
16858             Roo.log("waiting for the transitionend");
16859             return;
16860         }
16861         
16862         if (typeof(pan) == 'number') {
16863             pan = this.tabs[pan];
16864         }
16865         
16866         if (typeof(pan) == 'string') {
16867             pan = this.getPanelByName(pan);
16868         }
16869         
16870         var cur = this.getActivePanel();
16871         
16872         if(!pan || !cur){
16873             Roo.log('pan or acitve pan is undefined');
16874             return false;
16875         }
16876         
16877         if (pan.tabId == this.getActivePanel().tabId) {
16878             return true;
16879         }
16880         
16881         if (false === cur.fireEvent('beforedeactivate')) {
16882             return false;
16883         }
16884         
16885         if(this.bullets > 0 && !Roo.isTouch){
16886             this.setActiveBullet(this.indexOfPanel(pan));
16887         }
16888         
16889         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16890             
16891             this.transition = true;
16892             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16893             var lr = dir == 'next' ? 'left' : 'right';
16894             pan.el.addClass(dir); // or prev
16895             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16896             cur.el.addClass(lr); // or right
16897             pan.el.addClass(lr);
16898             
16899             var _this = this;
16900             cur.el.on('transitionend', function() {
16901                 Roo.log("trans end?");
16902                 
16903                 pan.el.removeClass([lr,dir]);
16904                 pan.setActive(true);
16905                 
16906                 cur.el.removeClass([lr]);
16907                 cur.setActive(false);
16908                 
16909                 _this.transition = false;
16910                 
16911             }, this, { single:  true } );
16912             
16913             return true;
16914         }
16915         
16916         cur.setActive(false);
16917         pan.setActive(true);
16918         
16919         return true;
16920         
16921     },
16922     showPanelNext : function()
16923     {
16924         var i = this.indexOfPanel(this.getActivePanel());
16925         
16926         if (i >= this.tabs.length - 1 && !this.autoslide) {
16927             return;
16928         }
16929         
16930         if (i >= this.tabs.length - 1 && this.autoslide) {
16931             i = -1;
16932         }
16933         
16934         this.showPanel(this.tabs[i+1]);
16935     },
16936     
16937     showPanelPrev : function()
16938     {
16939         var i = this.indexOfPanel(this.getActivePanel());
16940         
16941         if (i  < 1 && !this.autoslide) {
16942             return;
16943         }
16944         
16945         if (i < 1 && this.autoslide) {
16946             i = this.tabs.length;
16947         }
16948         
16949         this.showPanel(this.tabs[i-1]);
16950     },
16951     
16952     
16953     addBullet: function()
16954     {
16955         if(!this.bullets || Roo.isTouch){
16956             return;
16957         }
16958         var ctr = this.el.select('.carousel-bullets',true).first();
16959         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16960         var bullet = ctr.createChild({
16961             cls : 'bullet bullet-' + i
16962         },ctr.dom.lastChild);
16963         
16964         
16965         var _this = this;
16966         
16967         bullet.on('click', (function(e, el, o, ii, t){
16968
16969             e.preventDefault();
16970
16971             this.showPanel(ii);
16972
16973             if(this.autoslide && this.slideFn){
16974                 clearInterval(this.slideFn);
16975                 this.slideFn = window.setInterval(function() {
16976                     _this.showPanelNext();
16977                 }, this.timer);
16978             }
16979
16980         }).createDelegate(this, [i, bullet], true));
16981                 
16982         
16983     },
16984      
16985     setActiveBullet : function(i)
16986     {
16987         if(Roo.isTouch){
16988             return;
16989         }
16990         
16991         Roo.each(this.el.select('.bullet', true).elements, function(el){
16992             el.removeClass('selected');
16993         });
16994
16995         var bullet = this.el.select('.bullet-' + i, true).first();
16996         
16997         if(!bullet){
16998             return;
16999         }
17000         
17001         bullet.addClass('selected');
17002     }
17003     
17004     
17005   
17006 });
17007
17008  
17009
17010  
17011  
17012 Roo.apply(Roo.bootstrap.TabGroup, {
17013     
17014     groups: {},
17015      /**
17016     * register a Navigation Group
17017     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17018     */
17019     register : function(navgrp)
17020     {
17021         this.groups[navgrp.navId] = navgrp;
17022         
17023     },
17024     /**
17025     * fetch a Navigation Group based on the navigation ID
17026     * if one does not exist , it will get created.
17027     * @param {string} the navgroup to add
17028     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17029     */
17030     get: function(navId) {
17031         if (typeof(this.groups[navId]) == 'undefined') {
17032             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17033         }
17034         return this.groups[navId] ;
17035     }
17036     
17037     
17038     
17039 });
17040
17041  /*
17042  * - LGPL
17043  *
17044  * TabPanel
17045  * 
17046  */
17047
17048 /**
17049  * @class Roo.bootstrap.TabPanel
17050  * @extends Roo.bootstrap.Component
17051  * Bootstrap TabPanel class
17052  * @cfg {Boolean} active panel active
17053  * @cfg {String} html panel content
17054  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17055  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17056  * @cfg {String} href click to link..
17057  * 
17058  * 
17059  * @constructor
17060  * Create a new TabPanel
17061  * @param {Object} config The config object
17062  */
17063
17064 Roo.bootstrap.TabPanel = function(config){
17065     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17066     this.addEvents({
17067         /**
17068              * @event changed
17069              * Fires when the active status changes
17070              * @param {Roo.bootstrap.TabPanel} this
17071              * @param {Boolean} state the new state
17072             
17073          */
17074         'changed': true,
17075         /**
17076              * @event beforedeactivate
17077              * Fires before a tab is de-activated - can be used to do validation on a form.
17078              * @param {Roo.bootstrap.TabPanel} this
17079              * @return {Boolean} false if there is an error
17080             
17081          */
17082         'beforedeactivate': true
17083      });
17084     
17085     this.tabId = this.tabId || Roo.id();
17086   
17087 };
17088
17089 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17090     
17091     active: false,
17092     html: false,
17093     tabId: false,
17094     navId : false,
17095     href : '',
17096     
17097     getAutoCreate : function(){
17098         var cfg = {
17099             tag: 'div',
17100             // item is needed for carousel - not sure if it has any effect otherwise
17101             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17102             html: this.html || ''
17103         };
17104         
17105         if(this.active){
17106             cfg.cls += ' active';
17107         }
17108         
17109         if(this.tabId){
17110             cfg.tabId = this.tabId;
17111         }
17112         
17113         
17114         return cfg;
17115     },
17116     
17117     initEvents:  function()
17118     {
17119         var p = this.parent();
17120         this.navId = this.navId || p.navId;
17121         
17122         if (typeof(this.navId) != 'undefined') {
17123             // not really needed.. but just in case.. parent should be a NavGroup.
17124             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17125             
17126             tg.register(this);
17127             
17128             var i = tg.tabs.length - 1;
17129             
17130             if(this.active && tg.bullets > 0 && i < tg.bullets){
17131                 tg.setActiveBullet(i);
17132             }
17133         }
17134         
17135         if(this.href.length){
17136             this.el.on('click', this.onClick, this);
17137         }
17138         
17139     },
17140     
17141     onRender : function(ct, position)
17142     {
17143        // Roo.log("Call onRender: " + this.xtype);
17144         
17145         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17146         
17147         
17148         
17149         
17150         
17151     },
17152     
17153     setActive: function(state)
17154     {
17155         Roo.log("panel - set active " + this.tabId + "=" + state);
17156         
17157         this.active = state;
17158         if (!state) {
17159             this.el.removeClass('active');
17160             
17161         } else  if (!this.el.hasClass('active')) {
17162             this.el.addClass('active');
17163         }
17164         
17165         this.fireEvent('changed', this, state);
17166     },
17167     
17168     onClick: function(e)
17169     {
17170         e.preventDefault();
17171         
17172         window.location.href = this.href;
17173     }
17174     
17175     
17176 });
17177  
17178
17179  
17180
17181  /*
17182  * - LGPL
17183  *
17184  * DateField
17185  * 
17186  */
17187
17188 /**
17189  * @class Roo.bootstrap.DateField
17190  * @extends Roo.bootstrap.Input
17191  * Bootstrap DateField class
17192  * @cfg {Number} weekStart default 0
17193  * @cfg {String} viewMode default empty, (months|years)
17194  * @cfg {String} minViewMode default empty, (months|years)
17195  * @cfg {Number} startDate default -Infinity
17196  * @cfg {Number} endDate default Infinity
17197  * @cfg {Boolean} todayHighlight default false
17198  * @cfg {Boolean} todayBtn default false
17199  * @cfg {Boolean} calendarWeeks default false
17200  * @cfg {Object} daysOfWeekDisabled default empty
17201  * @cfg {Boolean} singleMode default false (true | false)
17202  * 
17203  * @cfg {Boolean} keyboardNavigation default true
17204  * @cfg {String} language default en
17205  * 
17206  * @constructor
17207  * Create a new DateField
17208  * @param {Object} config The config object
17209  */
17210
17211 Roo.bootstrap.DateField = function(config){
17212     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17213      this.addEvents({
17214             /**
17215              * @event show
17216              * Fires when this field show.
17217              * @param {Roo.bootstrap.DateField} this
17218              * @param {Mixed} date The date value
17219              */
17220             show : true,
17221             /**
17222              * @event show
17223              * Fires when this field hide.
17224              * @param {Roo.bootstrap.DateField} this
17225              * @param {Mixed} date The date value
17226              */
17227             hide : true,
17228             /**
17229              * @event select
17230              * Fires when select a date.
17231              * @param {Roo.bootstrap.DateField} this
17232              * @param {Mixed} date The date value
17233              */
17234             select : true,
17235             /**
17236              * @event beforeselect
17237              * Fires when before select a date.
17238              * @param {Roo.bootstrap.DateField} this
17239              * @param {Mixed} date The date value
17240              */
17241             beforeselect : true
17242         });
17243 };
17244
17245 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17246     
17247     /**
17248      * @cfg {String} format
17249      * The default date format string which can be overriden for localization support.  The format must be
17250      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17251      */
17252     format : "m/d/y",
17253     /**
17254      * @cfg {String} altFormats
17255      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17256      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17257      */
17258     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17259     
17260     weekStart : 0,
17261     
17262     viewMode : '',
17263     
17264     minViewMode : '',
17265     
17266     todayHighlight : false,
17267     
17268     todayBtn: false,
17269     
17270     language: 'en',
17271     
17272     keyboardNavigation: true,
17273     
17274     calendarWeeks: false,
17275     
17276     startDate: -Infinity,
17277     
17278     endDate: Infinity,
17279     
17280     daysOfWeekDisabled: [],
17281     
17282     _events: [],
17283     
17284     singleMode : false,
17285     
17286     UTCDate: function()
17287     {
17288         return new Date(Date.UTC.apply(Date, arguments));
17289     },
17290     
17291     UTCToday: function()
17292     {
17293         var today = new Date();
17294         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17295     },
17296     
17297     getDate: function() {
17298             var d = this.getUTCDate();
17299             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17300     },
17301     
17302     getUTCDate: function() {
17303             return this.date;
17304     },
17305     
17306     setDate: function(d) {
17307             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17308     },
17309     
17310     setUTCDate: function(d) {
17311             this.date = d;
17312             this.setValue(this.formatDate(this.date));
17313     },
17314         
17315     onRender: function(ct, position)
17316     {
17317         
17318         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17319         
17320         this.language = this.language || 'en';
17321         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17322         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17323         
17324         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17325         this.format = this.format || 'm/d/y';
17326         this.isInline = false;
17327         this.isInput = true;
17328         this.component = this.el.select('.add-on', true).first() || false;
17329         this.component = (this.component && this.component.length === 0) ? false : this.component;
17330         this.hasInput = this.component && this.inputEL().length;
17331         
17332         if (typeof(this.minViewMode === 'string')) {
17333             switch (this.minViewMode) {
17334                 case 'months':
17335                     this.minViewMode = 1;
17336                     break;
17337                 case 'years':
17338                     this.minViewMode = 2;
17339                     break;
17340                 default:
17341                     this.minViewMode = 0;
17342                     break;
17343             }
17344         }
17345         
17346         if (typeof(this.viewMode === 'string')) {
17347             switch (this.viewMode) {
17348                 case 'months':
17349                     this.viewMode = 1;
17350                     break;
17351                 case 'years':
17352                     this.viewMode = 2;
17353                     break;
17354                 default:
17355                     this.viewMode = 0;
17356                     break;
17357             }
17358         }
17359                 
17360         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17361         
17362 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17363         
17364         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17365         
17366         this.picker().on('mousedown', this.onMousedown, this);
17367         this.picker().on('click', this.onClick, this);
17368         
17369         this.picker().addClass('datepicker-dropdown');
17370         
17371         this.startViewMode = this.viewMode;
17372         
17373         if(this.singleMode){
17374             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17375                 v.setVisibilityMode(Roo.Element.DISPLAY);
17376                 v.hide();
17377             });
17378             
17379             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17380                 v.setStyle('width', '189px');
17381             });
17382         }
17383         
17384         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17385             if(!this.calendarWeeks){
17386                 v.remove();
17387                 return;
17388             }
17389             
17390             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17391             v.attr('colspan', function(i, val){
17392                 return parseInt(val) + 1;
17393             });
17394         });
17395                         
17396         
17397         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17398         
17399         this.setStartDate(this.startDate);
17400         this.setEndDate(this.endDate);
17401         
17402         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17403         
17404         this.fillDow();
17405         this.fillMonths();
17406         this.update();
17407         this.showMode();
17408         
17409         if(this.isInline) {
17410             this.show();
17411         }
17412     },
17413     
17414     picker : function()
17415     {
17416         return this.pickerEl;
17417 //        return this.el.select('.datepicker', true).first();
17418     },
17419     
17420     fillDow: function()
17421     {
17422         var dowCnt = this.weekStart;
17423         
17424         var dow = {
17425             tag: 'tr',
17426             cn: [
17427                 
17428             ]
17429         };
17430         
17431         if(this.calendarWeeks){
17432             dow.cn.push({
17433                 tag: 'th',
17434                 cls: 'cw',
17435                 html: '&nbsp;'
17436             })
17437         }
17438         
17439         while (dowCnt < this.weekStart + 7) {
17440             dow.cn.push({
17441                 tag: 'th',
17442                 cls: 'dow',
17443                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17444             });
17445         }
17446         
17447         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17448     },
17449     
17450     fillMonths: function()
17451     {    
17452         var i = 0;
17453         var months = this.picker().select('>.datepicker-months td', true).first();
17454         
17455         months.dom.innerHTML = '';
17456         
17457         while (i < 12) {
17458             var month = {
17459                 tag: 'span',
17460                 cls: 'month',
17461                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17462             };
17463             
17464             months.createChild(month);
17465         }
17466         
17467     },
17468     
17469     update: function()
17470     {
17471         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
17472         
17473         if (this.date < this.startDate) {
17474             this.viewDate = new Date(this.startDate);
17475         } else if (this.date > this.endDate) {
17476             this.viewDate = new Date(this.endDate);
17477         } else {
17478             this.viewDate = new Date(this.date);
17479         }
17480         
17481         this.fill();
17482     },
17483     
17484     fill: function() 
17485     {
17486         var d = new Date(this.viewDate),
17487                 year = d.getUTCFullYear(),
17488                 month = d.getUTCMonth(),
17489                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17490                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17491                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17492                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17493                 currentDate = this.date && this.date.valueOf(),
17494                 today = this.UTCToday();
17495         
17496         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17497         
17498 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17499         
17500 //        this.picker.select('>tfoot th.today').
17501 //                                              .text(dates[this.language].today)
17502 //                                              .toggle(this.todayBtn !== false);
17503     
17504         this.updateNavArrows();
17505         this.fillMonths();
17506                                                 
17507         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17508         
17509         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17510          
17511         prevMonth.setUTCDate(day);
17512         
17513         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17514         
17515         var nextMonth = new Date(prevMonth);
17516         
17517         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17518         
17519         nextMonth = nextMonth.valueOf();
17520         
17521         var fillMonths = false;
17522         
17523         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17524         
17525         while(prevMonth.valueOf() < nextMonth) {
17526             var clsName = '';
17527             
17528             if (prevMonth.getUTCDay() === this.weekStart) {
17529                 if(fillMonths){
17530                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17531                 }
17532                     
17533                 fillMonths = {
17534                     tag: 'tr',
17535                     cn: []
17536                 };
17537                 
17538                 if(this.calendarWeeks){
17539                     // ISO 8601: First week contains first thursday.
17540                     // ISO also states week starts on Monday, but we can be more abstract here.
17541                     var
17542                     // Start of current week: based on weekstart/current date
17543                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17544                     // Thursday of this week
17545                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17546                     // First Thursday of year, year from thursday
17547                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17548                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17549                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17550                     
17551                     fillMonths.cn.push({
17552                         tag: 'td',
17553                         cls: 'cw',
17554                         html: calWeek
17555                     });
17556                 }
17557             }
17558             
17559             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17560                 clsName += ' old';
17561             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17562                 clsName += ' new';
17563             }
17564             if (this.todayHighlight &&
17565                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17566                 prevMonth.getUTCMonth() == today.getMonth() &&
17567                 prevMonth.getUTCDate() == today.getDate()) {
17568                 clsName += ' today';
17569             }
17570             
17571             if (currentDate && prevMonth.valueOf() === currentDate) {
17572                 clsName += ' active';
17573             }
17574             
17575             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17576                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17577                     clsName += ' disabled';
17578             }
17579             
17580             fillMonths.cn.push({
17581                 tag: 'td',
17582                 cls: 'day ' + clsName,
17583                 html: prevMonth.getDate()
17584             });
17585             
17586             prevMonth.setDate(prevMonth.getDate()+1);
17587         }
17588           
17589         var currentYear = this.date && this.date.getUTCFullYear();
17590         var currentMonth = this.date && this.date.getUTCMonth();
17591         
17592         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17593         
17594         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17595             v.removeClass('active');
17596             
17597             if(currentYear === year && k === currentMonth){
17598                 v.addClass('active');
17599             }
17600             
17601             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17602                 v.addClass('disabled');
17603             }
17604             
17605         });
17606         
17607         
17608         year = parseInt(year/10, 10) * 10;
17609         
17610         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17611         
17612         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17613         
17614         year -= 1;
17615         for (var i = -1; i < 11; i++) {
17616             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17617                 tag: 'span',
17618                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17619                 html: year
17620             });
17621             
17622             year += 1;
17623         }
17624     },
17625     
17626     showMode: function(dir) 
17627     {
17628         if (dir) {
17629             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17630         }
17631         
17632         Roo.each(this.picker().select('>div',true).elements, function(v){
17633             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17634             v.hide();
17635         });
17636         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17637     },
17638     
17639     place: function()
17640     {
17641         if(this.isInline) {
17642             return;
17643         }
17644         
17645         this.picker().removeClass(['bottom', 'top']);
17646         
17647         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17648             /*
17649              * place to the top of element!
17650              *
17651              */
17652             
17653             this.picker().addClass('top');
17654             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17655             
17656             return;
17657         }
17658         
17659         this.picker().addClass('bottom');
17660         
17661         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17662     },
17663     
17664     parseDate : function(value)
17665     {
17666         if(!value || value instanceof Date){
17667             return value;
17668         }
17669         var v = Date.parseDate(value, this.format);
17670         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17671             v = Date.parseDate(value, 'Y-m-d');
17672         }
17673         if(!v && this.altFormats){
17674             if(!this.altFormatsArray){
17675                 this.altFormatsArray = this.altFormats.split("|");
17676             }
17677             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17678                 v = Date.parseDate(value, this.altFormatsArray[i]);
17679             }
17680         }
17681         return v;
17682     },
17683     
17684     formatDate : function(date, fmt)
17685     {   
17686         return (!date || !(date instanceof Date)) ?
17687         date : date.dateFormat(fmt || this.format);
17688     },
17689     
17690     onFocus : function()
17691     {
17692         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17693         this.show();
17694     },
17695     
17696     onBlur : function()
17697     {
17698         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17699         
17700         var d = this.inputEl().getValue();
17701         
17702         this.setValue(d);
17703                 
17704         this.hide();
17705     },
17706     
17707     show : function()
17708     {
17709         this.picker().show();
17710         this.update();
17711         this.place();
17712         
17713         this.fireEvent('show', this, this.date);
17714     },
17715     
17716     hide : function()
17717     {
17718         if(this.isInline) {
17719             return;
17720         }
17721         this.picker().hide();
17722         this.viewMode = this.startViewMode;
17723         this.showMode();
17724         
17725         this.fireEvent('hide', this, this.date);
17726         
17727     },
17728     
17729     onMousedown: function(e)
17730     {
17731         e.stopPropagation();
17732         e.preventDefault();
17733     },
17734     
17735     keyup: function(e)
17736     {
17737         Roo.bootstrap.DateField.superclass.keyup.call(this);
17738         this.update();
17739     },
17740
17741     setValue: function(v)
17742     {
17743         if(this.fireEvent('beforeselect', this, v) !== false){
17744             var d = new Date(this.parseDate(v) ).clearTime();
17745         
17746             if(isNaN(d.getTime())){
17747                 this.date = this.viewDate = '';
17748                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17749                 return;
17750             }
17751
17752             v = this.formatDate(d);
17753
17754             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17755
17756             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17757
17758             this.update();
17759
17760             this.fireEvent('select', this, this.date);
17761         }
17762     },
17763     
17764     getValue: function()
17765     {
17766         return this.formatDate(this.date);
17767     },
17768     
17769     fireKey: function(e)
17770     {
17771         if (!this.picker().isVisible()){
17772             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17773                 this.show();
17774             }
17775             return;
17776         }
17777         
17778         var dateChanged = false,
17779         dir, day, month,
17780         newDate, newViewDate;
17781         
17782         switch(e.keyCode){
17783             case 27: // escape
17784                 this.hide();
17785                 e.preventDefault();
17786                 break;
17787             case 37: // left
17788             case 39: // right
17789                 if (!this.keyboardNavigation) {
17790                     break;
17791                 }
17792                 dir = e.keyCode == 37 ? -1 : 1;
17793                 
17794                 if (e.ctrlKey){
17795                     newDate = this.moveYear(this.date, dir);
17796                     newViewDate = this.moveYear(this.viewDate, dir);
17797                 } else if (e.shiftKey){
17798                     newDate = this.moveMonth(this.date, dir);
17799                     newViewDate = this.moveMonth(this.viewDate, dir);
17800                 } else {
17801                     newDate = new Date(this.date);
17802                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17803                     newViewDate = new Date(this.viewDate);
17804                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17805                 }
17806                 if (this.dateWithinRange(newDate)){
17807                     this.date = newDate;
17808                     this.viewDate = newViewDate;
17809                     this.setValue(this.formatDate(this.date));
17810 //                    this.update();
17811                     e.preventDefault();
17812                     dateChanged = true;
17813                 }
17814                 break;
17815             case 38: // up
17816             case 40: // down
17817                 if (!this.keyboardNavigation) {
17818                     break;
17819                 }
17820                 dir = e.keyCode == 38 ? -1 : 1;
17821                 if (e.ctrlKey){
17822                     newDate = this.moveYear(this.date, dir);
17823                     newViewDate = this.moveYear(this.viewDate, dir);
17824                 } else if (e.shiftKey){
17825                     newDate = this.moveMonth(this.date, dir);
17826                     newViewDate = this.moveMonth(this.viewDate, dir);
17827                 } else {
17828                     newDate = new Date(this.date);
17829                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17830                     newViewDate = new Date(this.viewDate);
17831                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17832                 }
17833                 if (this.dateWithinRange(newDate)){
17834                     this.date = newDate;
17835                     this.viewDate = newViewDate;
17836                     this.setValue(this.formatDate(this.date));
17837 //                    this.update();
17838                     e.preventDefault();
17839                     dateChanged = true;
17840                 }
17841                 break;
17842             case 13: // enter
17843                 this.setValue(this.formatDate(this.date));
17844                 this.hide();
17845                 e.preventDefault();
17846                 break;
17847             case 9: // tab
17848                 this.setValue(this.formatDate(this.date));
17849                 this.hide();
17850                 break;
17851             case 16: // shift
17852             case 17: // ctrl
17853             case 18: // alt
17854                 break;
17855             default :
17856                 this.hide();
17857                 
17858         }
17859     },
17860     
17861     
17862     onClick: function(e) 
17863     {
17864         e.stopPropagation();
17865         e.preventDefault();
17866         
17867         var target = e.getTarget();
17868         
17869         if(target.nodeName.toLowerCase() === 'i'){
17870             target = Roo.get(target).dom.parentNode;
17871         }
17872         
17873         var nodeName = target.nodeName;
17874         var className = target.className;
17875         var html = target.innerHTML;
17876         //Roo.log(nodeName);
17877         
17878         switch(nodeName.toLowerCase()) {
17879             case 'th':
17880                 switch(className) {
17881                     case 'switch':
17882                         this.showMode(1);
17883                         break;
17884                     case 'prev':
17885                     case 'next':
17886                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17887                         switch(this.viewMode){
17888                                 case 0:
17889                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17890                                         break;
17891                                 case 1:
17892                                 case 2:
17893                                         this.viewDate = this.moveYear(this.viewDate, dir);
17894                                         break;
17895                         }
17896                         this.fill();
17897                         break;
17898                     case 'today':
17899                         var date = new Date();
17900                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17901 //                        this.fill()
17902                         this.setValue(this.formatDate(this.date));
17903                         
17904                         this.hide();
17905                         break;
17906                 }
17907                 break;
17908             case 'span':
17909                 if (className.indexOf('disabled') < 0) {
17910                     this.viewDate.setUTCDate(1);
17911                     if (className.indexOf('month') > -1) {
17912                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17913                     } else {
17914                         var year = parseInt(html, 10) || 0;
17915                         this.viewDate.setUTCFullYear(year);
17916                         
17917                     }
17918                     
17919                     if(this.singleMode){
17920                         this.setValue(this.formatDate(this.viewDate));
17921                         this.hide();
17922                         return;
17923                     }
17924                     
17925                     this.showMode(-1);
17926                     this.fill();
17927                 }
17928                 break;
17929                 
17930             case 'td':
17931                 //Roo.log(className);
17932                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17933                     var day = parseInt(html, 10) || 1;
17934                     var year = this.viewDate.getUTCFullYear(),
17935                         month = this.viewDate.getUTCMonth();
17936
17937                     if (className.indexOf('old') > -1) {
17938                         if(month === 0 ){
17939                             month = 11;
17940                             year -= 1;
17941                         }else{
17942                             month -= 1;
17943                         }
17944                     } else if (className.indexOf('new') > -1) {
17945                         if (month == 11) {
17946                             month = 0;
17947                             year += 1;
17948                         } else {
17949                             month += 1;
17950                         }
17951                     }
17952                     //Roo.log([year,month,day]);
17953                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17954                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17955 //                    this.fill();
17956                     //Roo.log(this.formatDate(this.date));
17957                     this.setValue(this.formatDate(this.date));
17958                     this.hide();
17959                 }
17960                 break;
17961         }
17962     },
17963     
17964     setStartDate: function(startDate)
17965     {
17966         this.startDate = startDate || -Infinity;
17967         if (this.startDate !== -Infinity) {
17968             this.startDate = this.parseDate(this.startDate);
17969         }
17970         this.update();
17971         this.updateNavArrows();
17972     },
17973
17974     setEndDate: function(endDate)
17975     {
17976         this.endDate = endDate || Infinity;
17977         if (this.endDate !== Infinity) {
17978             this.endDate = this.parseDate(this.endDate);
17979         }
17980         this.update();
17981         this.updateNavArrows();
17982     },
17983     
17984     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17985     {
17986         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17987         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17988             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17989         }
17990         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17991             return parseInt(d, 10);
17992         });
17993         this.update();
17994         this.updateNavArrows();
17995     },
17996     
17997     updateNavArrows: function() 
17998     {
17999         if(this.singleMode){
18000             return;
18001         }
18002         
18003         var d = new Date(this.viewDate),
18004         year = d.getUTCFullYear(),
18005         month = d.getUTCMonth();
18006         
18007         Roo.each(this.picker().select('.prev', true).elements, function(v){
18008             v.show();
18009             switch (this.viewMode) {
18010                 case 0:
18011
18012                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18013                         v.hide();
18014                     }
18015                     break;
18016                 case 1:
18017                 case 2:
18018                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18019                         v.hide();
18020                     }
18021                     break;
18022             }
18023         });
18024         
18025         Roo.each(this.picker().select('.next', true).elements, function(v){
18026             v.show();
18027             switch (this.viewMode) {
18028                 case 0:
18029
18030                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18031                         v.hide();
18032                     }
18033                     break;
18034                 case 1:
18035                 case 2:
18036                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18037                         v.hide();
18038                     }
18039                     break;
18040             }
18041         })
18042     },
18043     
18044     moveMonth: function(date, dir)
18045     {
18046         if (!dir) {
18047             return date;
18048         }
18049         var new_date = new Date(date.valueOf()),
18050         day = new_date.getUTCDate(),
18051         month = new_date.getUTCMonth(),
18052         mag = Math.abs(dir),
18053         new_month, test;
18054         dir = dir > 0 ? 1 : -1;
18055         if (mag == 1){
18056             test = dir == -1
18057             // If going back one month, make sure month is not current month
18058             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18059             ? function(){
18060                 return new_date.getUTCMonth() == month;
18061             }
18062             // If going forward one month, make sure month is as expected
18063             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18064             : function(){
18065                 return new_date.getUTCMonth() != new_month;
18066             };
18067             new_month = month + dir;
18068             new_date.setUTCMonth(new_month);
18069             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18070             if (new_month < 0 || new_month > 11) {
18071                 new_month = (new_month + 12) % 12;
18072             }
18073         } else {
18074             // For magnitudes >1, move one month at a time...
18075             for (var i=0; i<mag; i++) {
18076                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18077                 new_date = this.moveMonth(new_date, dir);
18078             }
18079             // ...then reset the day, keeping it in the new month
18080             new_month = new_date.getUTCMonth();
18081             new_date.setUTCDate(day);
18082             test = function(){
18083                 return new_month != new_date.getUTCMonth();
18084             };
18085         }
18086         // Common date-resetting loop -- if date is beyond end of month, make it
18087         // end of month
18088         while (test()){
18089             new_date.setUTCDate(--day);
18090             new_date.setUTCMonth(new_month);
18091         }
18092         return new_date;
18093     },
18094
18095     moveYear: function(date, dir)
18096     {
18097         return this.moveMonth(date, dir*12);
18098     },
18099
18100     dateWithinRange: function(date)
18101     {
18102         return date >= this.startDate && date <= this.endDate;
18103     },
18104
18105     
18106     remove: function() 
18107     {
18108         this.picker().remove();
18109     }
18110    
18111 });
18112
18113 Roo.apply(Roo.bootstrap.DateField,  {
18114     
18115     head : {
18116         tag: 'thead',
18117         cn: [
18118         {
18119             tag: 'tr',
18120             cn: [
18121             {
18122                 tag: 'th',
18123                 cls: 'prev',
18124                 html: '<i class="fa fa-arrow-left"/>'
18125             },
18126             {
18127                 tag: 'th',
18128                 cls: 'switch',
18129                 colspan: '5'
18130             },
18131             {
18132                 tag: 'th',
18133                 cls: 'next',
18134                 html: '<i class="fa fa-arrow-right"/>'
18135             }
18136
18137             ]
18138         }
18139         ]
18140     },
18141     
18142     content : {
18143         tag: 'tbody',
18144         cn: [
18145         {
18146             tag: 'tr',
18147             cn: [
18148             {
18149                 tag: 'td',
18150                 colspan: '7'
18151             }
18152             ]
18153         }
18154         ]
18155     },
18156     
18157     footer : {
18158         tag: 'tfoot',
18159         cn: [
18160         {
18161             tag: 'tr',
18162             cn: [
18163             {
18164                 tag: 'th',
18165                 colspan: '7',
18166                 cls: 'today'
18167             }
18168                     
18169             ]
18170         }
18171         ]
18172     },
18173     
18174     dates:{
18175         en: {
18176             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18177             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18178             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18179             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18180             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18181             today: "Today"
18182         }
18183     },
18184     
18185     modes: [
18186     {
18187         clsName: 'days',
18188         navFnc: 'Month',
18189         navStep: 1
18190     },
18191     {
18192         clsName: 'months',
18193         navFnc: 'FullYear',
18194         navStep: 1
18195     },
18196     {
18197         clsName: 'years',
18198         navFnc: 'FullYear',
18199         navStep: 10
18200     }]
18201 });
18202
18203 Roo.apply(Roo.bootstrap.DateField,  {
18204   
18205     template : {
18206         tag: 'div',
18207         cls: 'datepicker dropdown-menu roo-dynamic',
18208         cn: [
18209         {
18210             tag: 'div',
18211             cls: 'datepicker-days',
18212             cn: [
18213             {
18214                 tag: 'table',
18215                 cls: 'table-condensed',
18216                 cn:[
18217                 Roo.bootstrap.DateField.head,
18218                 {
18219                     tag: 'tbody'
18220                 },
18221                 Roo.bootstrap.DateField.footer
18222                 ]
18223             }
18224             ]
18225         },
18226         {
18227             tag: 'div',
18228             cls: 'datepicker-months',
18229             cn: [
18230             {
18231                 tag: 'table',
18232                 cls: 'table-condensed',
18233                 cn:[
18234                 Roo.bootstrap.DateField.head,
18235                 Roo.bootstrap.DateField.content,
18236                 Roo.bootstrap.DateField.footer
18237                 ]
18238             }
18239             ]
18240         },
18241         {
18242             tag: 'div',
18243             cls: 'datepicker-years',
18244             cn: [
18245             {
18246                 tag: 'table',
18247                 cls: 'table-condensed',
18248                 cn:[
18249                 Roo.bootstrap.DateField.head,
18250                 Roo.bootstrap.DateField.content,
18251                 Roo.bootstrap.DateField.footer
18252                 ]
18253             }
18254             ]
18255         }
18256         ]
18257     }
18258 });
18259
18260  
18261
18262  /*
18263  * - LGPL
18264  *
18265  * TimeField
18266  * 
18267  */
18268
18269 /**
18270  * @class Roo.bootstrap.TimeField
18271  * @extends Roo.bootstrap.Input
18272  * Bootstrap DateField class
18273  * 
18274  * 
18275  * @constructor
18276  * Create a new TimeField
18277  * @param {Object} config The config object
18278  */
18279
18280 Roo.bootstrap.TimeField = function(config){
18281     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18282     this.addEvents({
18283             /**
18284              * @event show
18285              * Fires when this field show.
18286              * @param {Roo.bootstrap.DateField} thisthis
18287              * @param {Mixed} date The date value
18288              */
18289             show : true,
18290             /**
18291              * @event show
18292              * Fires when this field hide.
18293              * @param {Roo.bootstrap.DateField} this
18294              * @param {Mixed} date The date value
18295              */
18296             hide : true,
18297             /**
18298              * @event select
18299              * Fires when select a date.
18300              * @param {Roo.bootstrap.DateField} this
18301              * @param {Mixed} date The date value
18302              */
18303             select : true
18304         });
18305 };
18306
18307 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18308     
18309     /**
18310      * @cfg {String} format
18311      * The default time format string which can be overriden for localization support.  The format must be
18312      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18313      */
18314     format : "H:i",
18315        
18316     onRender: function(ct, position)
18317     {
18318         
18319         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18320                 
18321         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18322         
18323         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18324         
18325         this.pop = this.picker().select('>.datepicker-time',true).first();
18326         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18327         
18328         this.picker().on('mousedown', this.onMousedown, this);
18329         this.picker().on('click', this.onClick, this);
18330         
18331         this.picker().addClass('datepicker-dropdown');
18332     
18333         this.fillTime();
18334         this.update();
18335             
18336         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18337         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18338         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18339         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18340         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18341         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18342
18343     },
18344     
18345     fireKey: function(e){
18346         if (!this.picker().isVisible()){
18347             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18348                 this.show();
18349             }
18350             return;
18351         }
18352
18353         e.preventDefault();
18354         
18355         switch(e.keyCode){
18356             case 27: // escape
18357                 this.hide();
18358                 break;
18359             case 37: // left
18360             case 39: // right
18361                 this.onTogglePeriod();
18362                 break;
18363             case 38: // up
18364                 this.onIncrementMinutes();
18365                 break;
18366             case 40: // down
18367                 this.onDecrementMinutes();
18368                 break;
18369             case 13: // enter
18370             case 9: // tab
18371                 this.setTime();
18372                 break;
18373         }
18374     },
18375     
18376     onClick: function(e) {
18377         e.stopPropagation();
18378         e.preventDefault();
18379     },
18380     
18381     picker : function()
18382     {
18383         return this.el.select('.datepicker', true).first();
18384     },
18385     
18386     fillTime: function()
18387     {    
18388         var time = this.pop.select('tbody', true).first();
18389         
18390         time.dom.innerHTML = '';
18391         
18392         time.createChild({
18393             tag: 'tr',
18394             cn: [
18395                 {
18396                     tag: 'td',
18397                     cn: [
18398                         {
18399                             tag: 'a',
18400                             href: '#',
18401                             cls: 'btn',
18402                             cn: [
18403                                 {
18404                                     tag: 'span',
18405                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18406                                 }
18407                             ]
18408                         } 
18409                     ]
18410                 },
18411                 {
18412                     tag: 'td',
18413                     cls: 'separator'
18414                 },
18415                 {
18416                     tag: 'td',
18417                     cn: [
18418                         {
18419                             tag: 'a',
18420                             href: '#',
18421                             cls: 'btn',
18422                             cn: [
18423                                 {
18424                                     tag: 'span',
18425                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18426                                 }
18427                             ]
18428                         }
18429                     ]
18430                 },
18431                 {
18432                     tag: 'td',
18433                     cls: 'separator'
18434                 }
18435             ]
18436         });
18437         
18438         time.createChild({
18439             tag: 'tr',
18440             cn: [
18441                 {
18442                     tag: 'td',
18443                     cn: [
18444                         {
18445                             tag: 'span',
18446                             cls: 'timepicker-hour',
18447                             html: '00'
18448                         }  
18449                     ]
18450                 },
18451                 {
18452                     tag: 'td',
18453                     cls: 'separator',
18454                     html: ':'
18455                 },
18456                 {
18457                     tag: 'td',
18458                     cn: [
18459                         {
18460                             tag: 'span',
18461                             cls: 'timepicker-minute',
18462                             html: '00'
18463                         }  
18464                     ]
18465                 },
18466                 {
18467                     tag: 'td',
18468                     cls: 'separator'
18469                 },
18470                 {
18471                     tag: 'td',
18472                     cn: [
18473                         {
18474                             tag: 'button',
18475                             type: 'button',
18476                             cls: 'btn btn-primary period',
18477                             html: 'AM'
18478                             
18479                         }
18480                     ]
18481                 }
18482             ]
18483         });
18484         
18485         time.createChild({
18486             tag: 'tr',
18487             cn: [
18488                 {
18489                     tag: 'td',
18490                     cn: [
18491                         {
18492                             tag: 'a',
18493                             href: '#',
18494                             cls: 'btn',
18495                             cn: [
18496                                 {
18497                                     tag: 'span',
18498                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18499                                 }
18500                             ]
18501                         }
18502                     ]
18503                 },
18504                 {
18505                     tag: 'td',
18506                     cls: 'separator'
18507                 },
18508                 {
18509                     tag: 'td',
18510                     cn: [
18511                         {
18512                             tag: 'a',
18513                             href: '#',
18514                             cls: 'btn',
18515                             cn: [
18516                                 {
18517                                     tag: 'span',
18518                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18519                                 }
18520                             ]
18521                         }
18522                     ]
18523                 },
18524                 {
18525                     tag: 'td',
18526                     cls: 'separator'
18527                 }
18528             ]
18529         });
18530         
18531     },
18532     
18533     update: function()
18534     {
18535         
18536         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18537         
18538         this.fill();
18539     },
18540     
18541     fill: function() 
18542     {
18543         var hours = this.time.getHours();
18544         var minutes = this.time.getMinutes();
18545         var period = 'AM';
18546         
18547         if(hours > 11){
18548             period = 'PM';
18549         }
18550         
18551         if(hours == 0){
18552             hours = 12;
18553         }
18554         
18555         
18556         if(hours > 12){
18557             hours = hours - 12;
18558         }
18559         
18560         if(hours < 10){
18561             hours = '0' + hours;
18562         }
18563         
18564         if(minutes < 10){
18565             minutes = '0' + minutes;
18566         }
18567         
18568         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18569         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18570         this.pop.select('button', true).first().dom.innerHTML = period;
18571         
18572     },
18573     
18574     place: function()
18575     {   
18576         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18577         
18578         var cls = ['bottom'];
18579         
18580         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18581             cls.pop();
18582             cls.push('top');
18583         }
18584         
18585         cls.push('right');
18586         
18587         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18588             cls.pop();
18589             cls.push('left');
18590         }
18591         
18592         this.picker().addClass(cls.join('-'));
18593         
18594         var _this = this;
18595         
18596         Roo.each(cls, function(c){
18597             if(c == 'bottom'){
18598                 _this.picker().setTop(_this.inputEl().getHeight());
18599                 return;
18600             }
18601             if(c == 'top'){
18602                 _this.picker().setTop(0 - _this.picker().getHeight());
18603                 return;
18604             }
18605             
18606             if(c == 'left'){
18607                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18608                 return;
18609             }
18610             if(c == 'right'){
18611                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18612                 return;
18613             }
18614         });
18615         
18616     },
18617   
18618     onFocus : function()
18619     {
18620         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18621         this.show();
18622     },
18623     
18624     onBlur : function()
18625     {
18626         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18627         this.hide();
18628     },
18629     
18630     show : function()
18631     {
18632         this.picker().show();
18633         this.pop.show();
18634         this.update();
18635         this.place();
18636         
18637         this.fireEvent('show', this, this.date);
18638     },
18639     
18640     hide : function()
18641     {
18642         this.picker().hide();
18643         this.pop.hide();
18644         
18645         this.fireEvent('hide', this, this.date);
18646     },
18647     
18648     setTime : function()
18649     {
18650         this.hide();
18651         this.setValue(this.time.format(this.format));
18652         
18653         this.fireEvent('select', this, this.date);
18654         
18655         
18656     },
18657     
18658     onMousedown: function(e){
18659         e.stopPropagation();
18660         e.preventDefault();
18661     },
18662     
18663     onIncrementHours: function()
18664     {
18665         Roo.log('onIncrementHours');
18666         this.time = this.time.add(Date.HOUR, 1);
18667         this.update();
18668         
18669     },
18670     
18671     onDecrementHours: function()
18672     {
18673         Roo.log('onDecrementHours');
18674         this.time = this.time.add(Date.HOUR, -1);
18675         this.update();
18676     },
18677     
18678     onIncrementMinutes: function()
18679     {
18680         Roo.log('onIncrementMinutes');
18681         this.time = this.time.add(Date.MINUTE, 1);
18682         this.update();
18683     },
18684     
18685     onDecrementMinutes: function()
18686     {
18687         Roo.log('onDecrementMinutes');
18688         this.time = this.time.add(Date.MINUTE, -1);
18689         this.update();
18690     },
18691     
18692     onTogglePeriod: function()
18693     {
18694         Roo.log('onTogglePeriod');
18695         this.time = this.time.add(Date.HOUR, 12);
18696         this.update();
18697     }
18698     
18699    
18700 });
18701
18702 Roo.apply(Roo.bootstrap.TimeField,  {
18703     
18704     content : {
18705         tag: 'tbody',
18706         cn: [
18707             {
18708                 tag: 'tr',
18709                 cn: [
18710                 {
18711                     tag: 'td',
18712                     colspan: '7'
18713                 }
18714                 ]
18715             }
18716         ]
18717     },
18718     
18719     footer : {
18720         tag: 'tfoot',
18721         cn: [
18722             {
18723                 tag: 'tr',
18724                 cn: [
18725                 {
18726                     tag: 'th',
18727                     colspan: '7',
18728                     cls: '',
18729                     cn: [
18730                         {
18731                             tag: 'button',
18732                             cls: 'btn btn-info ok',
18733                             html: 'OK'
18734                         }
18735                     ]
18736                 }
18737
18738                 ]
18739             }
18740         ]
18741     }
18742 });
18743
18744 Roo.apply(Roo.bootstrap.TimeField,  {
18745   
18746     template : {
18747         tag: 'div',
18748         cls: 'datepicker dropdown-menu',
18749         cn: [
18750             {
18751                 tag: 'div',
18752                 cls: 'datepicker-time',
18753                 cn: [
18754                 {
18755                     tag: 'table',
18756                     cls: 'table-condensed',
18757                     cn:[
18758                     Roo.bootstrap.TimeField.content,
18759                     Roo.bootstrap.TimeField.footer
18760                     ]
18761                 }
18762                 ]
18763             }
18764         ]
18765     }
18766 });
18767
18768  
18769
18770  /*
18771  * - LGPL
18772  *
18773  * MonthField
18774  * 
18775  */
18776
18777 /**
18778  * @class Roo.bootstrap.MonthField
18779  * @extends Roo.bootstrap.Input
18780  * Bootstrap MonthField class
18781  * 
18782  * @cfg {String} language default en
18783  * 
18784  * @constructor
18785  * Create a new MonthField
18786  * @param {Object} config The config object
18787  */
18788
18789 Roo.bootstrap.MonthField = function(config){
18790     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18791     
18792     this.addEvents({
18793         /**
18794          * @event show
18795          * Fires when this field show.
18796          * @param {Roo.bootstrap.MonthField} this
18797          * @param {Mixed} date The date value
18798          */
18799         show : true,
18800         /**
18801          * @event show
18802          * Fires when this field hide.
18803          * @param {Roo.bootstrap.MonthField} this
18804          * @param {Mixed} date The date value
18805          */
18806         hide : true,
18807         /**
18808          * @event select
18809          * Fires when select a date.
18810          * @param {Roo.bootstrap.MonthField} this
18811          * @param {String} oldvalue The old value
18812          * @param {String} newvalue The new value
18813          */
18814         select : true
18815     });
18816 };
18817
18818 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18819     
18820     onRender: function(ct, position)
18821     {
18822         
18823         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18824         
18825         this.language = this.language || 'en';
18826         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18827         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18828         
18829         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18830         this.isInline = false;
18831         this.isInput = true;
18832         this.component = this.el.select('.add-on', true).first() || false;
18833         this.component = (this.component && this.component.length === 0) ? false : this.component;
18834         this.hasInput = this.component && this.inputEL().length;
18835         
18836         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18837         
18838         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18839         
18840         this.picker().on('mousedown', this.onMousedown, this);
18841         this.picker().on('click', this.onClick, this);
18842         
18843         this.picker().addClass('datepicker-dropdown');
18844         
18845         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18846             v.setStyle('width', '189px');
18847         });
18848         
18849         this.fillMonths();
18850         
18851         this.update();
18852         
18853         if(this.isInline) {
18854             this.show();
18855         }
18856         
18857     },
18858     
18859     setValue: function(v, suppressEvent)
18860     {   
18861         var o = this.getValue();
18862         
18863         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18864         
18865         this.update();
18866
18867         if(suppressEvent !== true){
18868             this.fireEvent('select', this, o, v);
18869         }
18870         
18871     },
18872     
18873     getValue: function()
18874     {
18875         return this.value;
18876     },
18877     
18878     onClick: function(e) 
18879     {
18880         e.stopPropagation();
18881         e.preventDefault();
18882         
18883         var target = e.getTarget();
18884         
18885         if(target.nodeName.toLowerCase() === 'i'){
18886             target = Roo.get(target).dom.parentNode;
18887         }
18888         
18889         var nodeName = target.nodeName;
18890         var className = target.className;
18891         var html = target.innerHTML;
18892         
18893         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18894             return;
18895         }
18896         
18897         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18898         
18899         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18900         
18901         this.hide();
18902                         
18903     },
18904     
18905     picker : function()
18906     {
18907         return this.pickerEl;
18908     },
18909     
18910     fillMonths: function()
18911     {    
18912         var i = 0;
18913         var months = this.picker().select('>.datepicker-months td', true).first();
18914         
18915         months.dom.innerHTML = '';
18916         
18917         while (i < 12) {
18918             var month = {
18919                 tag: 'span',
18920                 cls: 'month',
18921                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18922             };
18923             
18924             months.createChild(month);
18925         }
18926         
18927     },
18928     
18929     update: function()
18930     {
18931         var _this = this;
18932         
18933         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18934             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18935         }
18936         
18937         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18938             e.removeClass('active');
18939             
18940             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18941                 e.addClass('active');
18942             }
18943         })
18944     },
18945     
18946     place: function()
18947     {
18948         if(this.isInline) {
18949             return;
18950         }
18951         
18952         this.picker().removeClass(['bottom', 'top']);
18953         
18954         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18955             /*
18956              * place to the top of element!
18957              *
18958              */
18959             
18960             this.picker().addClass('top');
18961             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18962             
18963             return;
18964         }
18965         
18966         this.picker().addClass('bottom');
18967         
18968         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18969     },
18970     
18971     onFocus : function()
18972     {
18973         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18974         this.show();
18975     },
18976     
18977     onBlur : function()
18978     {
18979         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18980         
18981         var d = this.inputEl().getValue();
18982         
18983         this.setValue(d);
18984                 
18985         this.hide();
18986     },
18987     
18988     show : function()
18989     {
18990         this.picker().show();
18991         this.picker().select('>.datepicker-months', true).first().show();
18992         this.update();
18993         this.place();
18994         
18995         this.fireEvent('show', this, this.date);
18996     },
18997     
18998     hide : function()
18999     {
19000         if(this.isInline) {
19001             return;
19002         }
19003         this.picker().hide();
19004         this.fireEvent('hide', this, this.date);
19005         
19006     },
19007     
19008     onMousedown: function(e)
19009     {
19010         e.stopPropagation();
19011         e.preventDefault();
19012     },
19013     
19014     keyup: function(e)
19015     {
19016         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19017         this.update();
19018     },
19019
19020     fireKey: function(e)
19021     {
19022         if (!this.picker().isVisible()){
19023             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19024                 this.show();
19025             }
19026             return;
19027         }
19028         
19029         var dir;
19030         
19031         switch(e.keyCode){
19032             case 27: // escape
19033                 this.hide();
19034                 e.preventDefault();
19035                 break;
19036             case 37: // left
19037             case 39: // right
19038                 dir = e.keyCode == 37 ? -1 : 1;
19039                 
19040                 this.vIndex = this.vIndex + dir;
19041                 
19042                 if(this.vIndex < 0){
19043                     this.vIndex = 0;
19044                 }
19045                 
19046                 if(this.vIndex > 11){
19047                     this.vIndex = 11;
19048                 }
19049                 
19050                 if(isNaN(this.vIndex)){
19051                     this.vIndex = 0;
19052                 }
19053                 
19054                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19055                 
19056                 break;
19057             case 38: // up
19058             case 40: // down
19059                 
19060                 dir = e.keyCode == 38 ? -1 : 1;
19061                 
19062                 this.vIndex = this.vIndex + dir * 4;
19063                 
19064                 if(this.vIndex < 0){
19065                     this.vIndex = 0;
19066                 }
19067                 
19068                 if(this.vIndex > 11){
19069                     this.vIndex = 11;
19070                 }
19071                 
19072                 if(isNaN(this.vIndex)){
19073                     this.vIndex = 0;
19074                 }
19075                 
19076                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19077                 break;
19078                 
19079             case 13: // enter
19080                 
19081                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19082                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19083                 }
19084                 
19085                 this.hide();
19086                 e.preventDefault();
19087                 break;
19088             case 9: // tab
19089                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19090                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19091                 }
19092                 this.hide();
19093                 break;
19094             case 16: // shift
19095             case 17: // ctrl
19096             case 18: // alt
19097                 break;
19098             default :
19099                 this.hide();
19100                 
19101         }
19102     },
19103     
19104     remove: function() 
19105     {
19106         this.picker().remove();
19107     }
19108    
19109 });
19110
19111 Roo.apply(Roo.bootstrap.MonthField,  {
19112     
19113     content : {
19114         tag: 'tbody',
19115         cn: [
19116         {
19117             tag: 'tr',
19118             cn: [
19119             {
19120                 tag: 'td',
19121                 colspan: '7'
19122             }
19123             ]
19124         }
19125         ]
19126     },
19127     
19128     dates:{
19129         en: {
19130             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19131             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19132         }
19133     }
19134 });
19135
19136 Roo.apply(Roo.bootstrap.MonthField,  {
19137   
19138     template : {
19139         tag: 'div',
19140         cls: 'datepicker dropdown-menu roo-dynamic',
19141         cn: [
19142             {
19143                 tag: 'div',
19144                 cls: 'datepicker-months',
19145                 cn: [
19146                 {
19147                     tag: 'table',
19148                     cls: 'table-condensed',
19149                     cn:[
19150                         Roo.bootstrap.DateField.content
19151                     ]
19152                 }
19153                 ]
19154             }
19155         ]
19156     }
19157 });
19158
19159  
19160
19161  
19162  /*
19163  * - LGPL
19164  *
19165  * CheckBox
19166  * 
19167  */
19168
19169 /**
19170  * @class Roo.bootstrap.CheckBox
19171  * @extends Roo.bootstrap.Input
19172  * Bootstrap CheckBox class
19173  * 
19174  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19175  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19176  * @cfg {String} boxLabel The text that appears beside the checkbox
19177  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19178  * @cfg {Boolean} checked initnal the element
19179  * @cfg {Boolean} inline inline the element (default false)
19180  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19181  * 
19182  * @constructor
19183  * Create a new CheckBox
19184  * @param {Object} config The config object
19185  */
19186
19187 Roo.bootstrap.CheckBox = function(config){
19188     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19189    
19190     this.addEvents({
19191         /**
19192         * @event check
19193         * Fires when the element is checked or unchecked.
19194         * @param {Roo.bootstrap.CheckBox} this This input
19195         * @param {Boolean} checked The new checked value
19196         */
19197        check : true
19198     });
19199     
19200 };
19201
19202 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19203   
19204     inputType: 'checkbox',
19205     inputValue: 1,
19206     valueOff: 0,
19207     boxLabel: false,
19208     checked: false,
19209     weight : false,
19210     inline: false,
19211     
19212     getAutoCreate : function()
19213     {
19214         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19215         
19216         var id = Roo.id();
19217         
19218         var cfg = {};
19219         
19220         cfg.cls = 'form-group ' + this.inputType; //input-group
19221         
19222         if(this.inline){
19223             cfg.cls += ' ' + this.inputType + '-inline';
19224         }
19225         
19226         var input =  {
19227             tag: 'input',
19228             id : id,
19229             type : this.inputType,
19230             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19231             cls : 'roo-' + this.inputType, //'form-box',
19232             placeholder : this.placeholder || ''
19233             
19234         };
19235         
19236         if (this.weight) { // Validity check?
19237             cfg.cls += " " + this.inputType + "-" + this.weight;
19238         }
19239         
19240         if (this.disabled) {
19241             input.disabled=true;
19242         }
19243         
19244         if(this.checked){
19245             input.checked = this.checked;
19246         }
19247         
19248         if (this.name) {
19249             input.name = this.name;
19250         }
19251         
19252         if (this.size) {
19253             input.cls += ' input-' + this.size;
19254         }
19255         
19256         var settings=this;
19257         
19258         ['xs','sm','md','lg'].map(function(size){
19259             if (settings[size]) {
19260                 cfg.cls += ' col-' + size + '-' + settings[size];
19261             }
19262         });
19263         
19264         var inputblock = input;
19265          
19266         if (this.before || this.after) {
19267             
19268             inputblock = {
19269                 cls : 'input-group',
19270                 cn :  [] 
19271             };
19272             
19273             if (this.before) {
19274                 inputblock.cn.push({
19275                     tag :'span',
19276                     cls : 'input-group-addon',
19277                     html : this.before
19278                 });
19279             }
19280             
19281             inputblock.cn.push(input);
19282             
19283             if (this.after) {
19284                 inputblock.cn.push({
19285                     tag :'span',
19286                     cls : 'input-group-addon',
19287                     html : this.after
19288                 });
19289             }
19290             
19291         }
19292         
19293         if (align ==='left' && this.fieldLabel.length) {
19294 //                Roo.log("left and has label");
19295                 cfg.cn = [
19296                     
19297                     {
19298                         tag: 'label',
19299                         'for' :  id,
19300                         cls : 'control-label col-md-' + this.labelWidth,
19301                         html : this.fieldLabel
19302                         
19303                     },
19304                     {
19305                         cls : "col-md-" + (12 - this.labelWidth), 
19306                         cn: [
19307                             inputblock
19308                         ]
19309                     }
19310                     
19311                 ];
19312         } else if ( this.fieldLabel.length) {
19313 //                Roo.log(" label");
19314                 cfg.cn = [
19315                    
19316                     {
19317                         tag: this.boxLabel ? 'span' : 'label',
19318                         'for': id,
19319                         cls: 'control-label box-input-label',
19320                         //cls : 'input-group-addon',
19321                         html : this.fieldLabel
19322                         
19323                     },
19324                     
19325                     inputblock
19326                     
19327                 ];
19328
19329         } else {
19330             
19331 //                Roo.log(" no label && no align");
19332                 cfg.cn = [  inputblock ] ;
19333                 
19334                 
19335         }
19336         
19337         if(this.boxLabel){
19338              var boxLabelCfg = {
19339                 tag: 'label',
19340                 //'for': id, // box label is handled by onclick - so no for...
19341                 cls: 'box-label',
19342                 html: this.boxLabel
19343             };
19344             
19345             if(this.tooltip){
19346                 boxLabelCfg.tooltip = this.tooltip;
19347             }
19348              
19349             cfg.cn.push(boxLabelCfg);
19350         }
19351         
19352         
19353        
19354         return cfg;
19355         
19356     },
19357     
19358     /**
19359      * return the real input element.
19360      */
19361     inputEl: function ()
19362     {
19363         return this.el.select('input.roo-' + this.inputType,true).first();
19364     },
19365     
19366     labelEl: function()
19367     {
19368         return this.el.select('label.control-label',true).first();
19369     },
19370     /* depricated... */
19371     
19372     label: function()
19373     {
19374         return this.labelEl();
19375     },
19376     
19377     boxLabelEl: function()
19378     {
19379         return this.el.select('label.box-label',true).first();
19380     },
19381     
19382     initEvents : function()
19383     {
19384 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19385         
19386         this.inputEl().on('click', this.onClick,  this);
19387         
19388         if (this.boxLabel) { 
19389             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19390         }
19391         
19392         this.startValue = this.getValue();
19393         
19394         if(this.groupId){
19395             Roo.bootstrap.CheckBox.register(this);
19396         }
19397     },
19398     
19399     onClick : function()
19400     {   
19401         this.setChecked(!this.checked);
19402     },
19403     
19404     setChecked : function(state,suppressEvent)
19405     {
19406         this.startValue = this.getValue();
19407         
19408         if(this.inputType == 'radio'){
19409             
19410             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19411                 e.dom.checked = false;
19412             });
19413             
19414             this.inputEl().dom.checked = true;
19415             
19416             this.inputEl().dom.value = this.inputValue;
19417             
19418             if(suppressEvent !== true){
19419                 this.fireEvent('check', this, true);
19420             }
19421             
19422             this.validate();
19423             
19424             return;
19425         }
19426         
19427         this.checked = state;
19428         
19429         this.inputEl().dom.checked = state;
19430         
19431         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19432         
19433         if(suppressEvent !== true){
19434             this.fireEvent('check', this, state);
19435         }
19436         
19437         this.validate();
19438     },
19439     
19440     getValue : function()
19441     {
19442         if(this.inputType == 'radio'){
19443             return this.getGroupValue();
19444         }
19445         
19446         return this.inputEl().getValue();
19447         
19448     },
19449     
19450     getGroupValue : function()
19451     {
19452         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19453             return '';
19454         }
19455         
19456         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19457     },
19458     
19459     setValue : function(v,suppressEvent)
19460     {
19461         if(this.inputType == 'radio'){
19462             this.setGroupValue(v, suppressEvent);
19463             return;
19464         }
19465         
19466         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19467         
19468         this.validate();
19469     },
19470     
19471     setGroupValue : function(v, suppressEvent)
19472     {
19473         this.startValue = this.getValue();
19474         
19475         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19476             e.dom.checked = false;
19477             
19478             if(e.dom.value == v){
19479                 e.dom.checked = true;
19480             }
19481         });
19482         
19483         if(suppressEvent !== true){
19484             this.fireEvent('check', this, true);
19485         }
19486
19487         this.validate();
19488         
19489         return;
19490     },
19491     
19492     validate : function()
19493     {
19494         if(
19495                 this.disabled || 
19496                 (this.inputType == 'radio' && this.validateRadio()) ||
19497                 (this.inputType == 'checkbox' && this.validateCheckbox())
19498         ){
19499             this.markValid();
19500             return true;
19501         }
19502         
19503         this.markInvalid();
19504         return false;
19505     },
19506     
19507     validateRadio : function()
19508     {
19509         var valid = false;
19510         
19511         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19512             if(!e.dom.checked){
19513                 return;
19514             }
19515             
19516             valid = true;
19517             
19518             return false;
19519         });
19520         
19521         return valid;
19522     },
19523     
19524     validateCheckbox : function()
19525     {
19526         if(!this.groupId){
19527             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19528         }
19529         
19530         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19531         
19532         if(!group){
19533             return false;
19534         }
19535         
19536         var r = false;
19537         
19538         for(var i in group){
19539             if(r){
19540                 break;
19541             }
19542             
19543             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19544         }
19545         
19546         return r;
19547     },
19548     
19549     /**
19550      * Mark this field as valid
19551      */
19552     markValid : function()
19553     {
19554         if(this.allowBlank){
19555             return;
19556         }
19557         
19558         var _this = this;
19559         
19560         this.fireEvent('valid', this);
19561         
19562         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19563         
19564         if(this.groupId){
19565             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19566         }
19567         
19568         if(label){
19569             label.markValid();
19570         }
19571         
19572         if(this.inputType == 'radio'){
19573             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19574                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19575                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19576             });
19577             
19578             return;
19579         }
19580         
19581         if(!this.groupId){
19582             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19583             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19584             return;
19585         }
19586         
19587         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19588             
19589         if(!group){
19590             return;
19591         }
19592         
19593         for(var i in group){
19594             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19595             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19596         }
19597     },
19598     
19599      /**
19600      * Mark this field as invalid
19601      * @param {String} msg The validation message
19602      */
19603     markInvalid : function(msg)
19604     {
19605         if(this.allowBlank){
19606             return;
19607         }
19608         
19609         var _this = this;
19610         
19611         this.fireEvent('invalid', this, msg);
19612         
19613         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19614         
19615         if(this.groupId){
19616             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19617         }
19618         
19619         if(label){
19620             label.markInvalid();
19621         }
19622             
19623         if(this.inputType == 'radio'){
19624             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19625                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19626                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19627             });
19628             
19629             return;
19630         }
19631         
19632         if(!this.groupId){
19633             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19634             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19635             return;
19636         }
19637         
19638         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19639         
19640         if(!group){
19641             return;
19642         }
19643         
19644         for(var i in group){
19645             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19646             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19647         }
19648         
19649     }
19650     
19651 });
19652
19653 Roo.apply(Roo.bootstrap.CheckBox, {
19654     
19655     groups: {},
19656     
19657      /**
19658     * register a CheckBox Group
19659     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19660     */
19661     register : function(checkbox)
19662     {
19663         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19664             this.groups[checkbox.groupId] = {};
19665         }
19666         
19667         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19668             return;
19669         }
19670         
19671         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19672         
19673     },
19674     /**
19675     * fetch a CheckBox Group based on the group ID
19676     * @param {string} the group ID
19677     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19678     */
19679     get: function(groupId) {
19680         if (typeof(this.groups[groupId]) == 'undefined') {
19681             return false;
19682         }
19683         
19684         return this.groups[groupId] ;
19685     }
19686     
19687     
19688 });
19689 /*
19690  * - LGPL
19691  *
19692  * Radio
19693  *
19694  *
19695  * not inline
19696  *<div class="radio">
19697   <label>
19698     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19699     Option one is this and that&mdash;be sure to include why it's great
19700   </label>
19701 </div>
19702  *
19703  *
19704  *inline
19705  *<span>
19706  *<label class="radio-inline">fieldLabel</label>
19707  *<label class="radio-inline">
19708   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19709 </label>
19710 <span>
19711  * 
19712  * 
19713  */
19714
19715 /**
19716  * @class Roo.bootstrap.Radio
19717  * @extends Roo.bootstrap.CheckBox
19718  * Bootstrap Radio class
19719
19720  * @constructor
19721  * Create a new Radio
19722  * @param {Object} config The config object
19723  */
19724
19725 Roo.bootstrap.Radio = function(config){
19726     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19727    
19728 };
19729
19730 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19731     
19732     inputType: 'radio',
19733     inputValue: '',
19734     valueOff: '',
19735     
19736     getAutoCreate : function()
19737     {
19738         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19739         align = align || 'left'; // default...
19740         
19741         
19742         
19743         var id = Roo.id();
19744         
19745         var cfg = {
19746                 tag : this.inline ? 'span' : 'div',
19747                 cls : '',
19748                 cn : []
19749         };
19750         
19751         var inline = this.inline ? ' radio-inline' : '';
19752         
19753         var lbl = {
19754                 tag: 'label' ,
19755                 // does not need for, as we wrap the input with it..
19756                 'for' : id,
19757                 cls : 'control-label box-label' + inline,
19758                 cn : []
19759         };
19760         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19761         
19762         var fieldLabel = {
19763             tag: 'label' ,
19764             //cls : 'control-label' + inline,
19765             html : this.fieldLabel,
19766             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19767         };
19768         
19769  
19770         
19771         
19772         var input =  {
19773             tag: 'input',
19774             id : id,
19775             type : this.inputType,
19776             //value : (!this.checked) ? this.valueOff : this.inputValue,
19777             value : this.inputValue,
19778             cls : 'roo-radio',
19779             placeholder : this.placeholder || '' // ?? needed????
19780             
19781         };
19782         if (this.weight) { // Validity check?
19783             input.cls += " radio-" + this.weight;
19784         }
19785         if (this.disabled) {
19786             input.disabled=true;
19787         }
19788         
19789         if(this.checked){
19790             input.checked = this.checked;
19791         }
19792         
19793         if (this.name) {
19794             input.name = this.name;
19795         }
19796         
19797         if (this.size) {
19798             input.cls += ' input-' + this.size;
19799         }
19800         
19801         //?? can span's inline have a width??
19802         
19803         var settings=this;
19804         ['xs','sm','md','lg'].map(function(size){
19805             if (settings[size]) {
19806                 cfg.cls += ' col-' + size + '-' + settings[size];
19807             }
19808         });
19809         
19810         var inputblock = input;
19811         
19812         if (this.before || this.after) {
19813             
19814             inputblock = {
19815                 cls : 'input-group',
19816                 tag : 'span',
19817                 cn :  [] 
19818             };
19819             if (this.before) {
19820                 inputblock.cn.push({
19821                     tag :'span',
19822                     cls : 'input-group-addon',
19823                     html : this.before
19824                 });
19825             }
19826             inputblock.cn.push(input);
19827             if (this.after) {
19828                 inputblock.cn.push({
19829                     tag :'span',
19830                     cls : 'input-group-addon',
19831                     html : this.after
19832                 });
19833             }
19834             
19835         };
19836         
19837         
19838         if (this.fieldLabel && this.fieldLabel.length) {
19839             cfg.cn.push(fieldLabel);
19840         }
19841        
19842         // normal bootstrap puts the input inside the label.
19843         // however with our styled version - it has to go after the input.
19844        
19845         //lbl.cn.push(inputblock);
19846         
19847         var lblwrap =  {
19848             tag: 'span',
19849             cls: 'radio' + inline,
19850             cn: [
19851                 inputblock,
19852                 lbl
19853             ]
19854         };
19855         
19856         cfg.cn.push( lblwrap);
19857         
19858         if(this.boxLabel){
19859             lbl.cn.push({
19860                 tag: 'span',
19861                 html: this.boxLabel
19862             })
19863         }
19864          
19865         
19866         return cfg;
19867         
19868     },
19869     
19870     initEvents : function()
19871     {
19872 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19873         
19874         this.inputEl().on('click', this.onClick,  this);
19875         if (this.boxLabel) {
19876             //Roo.log('find label');
19877             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19878         }
19879         
19880     },
19881     
19882     inputEl: function ()
19883     {
19884         return this.el.select('input.roo-radio',true).first();
19885     },
19886     onClick : function()
19887     {   
19888         Roo.log("click");
19889         this.setChecked(true);
19890     },
19891     
19892     setChecked : function(state,suppressEvent)
19893     {
19894         if(state){
19895             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19896                 v.dom.checked = false;
19897             });
19898         }
19899         Roo.log(this.inputEl().dom);
19900         this.checked = state;
19901         this.inputEl().dom.checked = state;
19902         
19903         if(suppressEvent !== true){
19904             this.fireEvent('check', this, state);
19905         }
19906         
19907         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19908         
19909     },
19910     
19911     getGroupValue : function()
19912     {
19913         var value = '';
19914         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19915             if(v.dom.checked == true){
19916                 value = v.dom.value;
19917             }
19918         });
19919         
19920         return value;
19921     },
19922     
19923     /**
19924      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19925      * @return {Mixed} value The field value
19926      */
19927     getValue : function(){
19928         return this.getGroupValue();
19929     }
19930     
19931 });
19932
19933  
19934 //<script type="text/javascript">
19935
19936 /*
19937  * Based  Ext JS Library 1.1.1
19938  * Copyright(c) 2006-2007, Ext JS, LLC.
19939  * LGPL
19940  *
19941  */
19942  
19943 /**
19944  * @class Roo.HtmlEditorCore
19945  * @extends Roo.Component
19946  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19947  *
19948  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19949  */
19950
19951 Roo.HtmlEditorCore = function(config){
19952     
19953     
19954     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19955     
19956     
19957     this.addEvents({
19958         /**
19959          * @event initialize
19960          * Fires when the editor is fully initialized (including the iframe)
19961          * @param {Roo.HtmlEditorCore} this
19962          */
19963         initialize: true,
19964         /**
19965          * @event activate
19966          * Fires when the editor is first receives the focus. Any insertion must wait
19967          * until after this event.
19968          * @param {Roo.HtmlEditorCore} this
19969          */
19970         activate: true,
19971          /**
19972          * @event beforesync
19973          * Fires before the textarea is updated with content from the editor iframe. Return false
19974          * to cancel the sync.
19975          * @param {Roo.HtmlEditorCore} this
19976          * @param {String} html
19977          */
19978         beforesync: true,
19979          /**
19980          * @event beforepush
19981          * Fires before the iframe editor is updated with content from the textarea. Return false
19982          * to cancel the push.
19983          * @param {Roo.HtmlEditorCore} this
19984          * @param {String} html
19985          */
19986         beforepush: true,
19987          /**
19988          * @event sync
19989          * Fires when the textarea is updated with content from the editor iframe.
19990          * @param {Roo.HtmlEditorCore} this
19991          * @param {String} html
19992          */
19993         sync: true,
19994          /**
19995          * @event push
19996          * Fires when the iframe editor is updated with content from the textarea.
19997          * @param {Roo.HtmlEditorCore} this
19998          * @param {String} html
19999          */
20000         push: true,
20001         
20002         /**
20003          * @event editorevent
20004          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20005          * @param {Roo.HtmlEditorCore} this
20006          */
20007         editorevent: true
20008         
20009     });
20010     
20011     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20012     
20013     // defaults : white / black...
20014     this.applyBlacklists();
20015     
20016     
20017     
20018 };
20019
20020
20021 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20022
20023
20024      /**
20025      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20026      */
20027     
20028     owner : false,
20029     
20030      /**
20031      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20032      *                        Roo.resizable.
20033      */
20034     resizable : false,
20035      /**
20036      * @cfg {Number} height (in pixels)
20037      */   
20038     height: 300,
20039    /**
20040      * @cfg {Number} width (in pixels)
20041      */   
20042     width: 500,
20043     
20044     /**
20045      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20046      * 
20047      */
20048     stylesheets: false,
20049     
20050     // id of frame..
20051     frameId: false,
20052     
20053     // private properties
20054     validationEvent : false,
20055     deferHeight: true,
20056     initialized : false,
20057     activated : false,
20058     sourceEditMode : false,
20059     onFocus : Roo.emptyFn,
20060     iframePad:3,
20061     hideMode:'offsets',
20062     
20063     clearUp: true,
20064     
20065     // blacklist + whitelisted elements..
20066     black: false,
20067     white: false,
20068      
20069     
20070
20071     /**
20072      * Protected method that will not generally be called directly. It
20073      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20074      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20075      */
20076     getDocMarkup : function(){
20077         // body styles..
20078         var st = '';
20079         
20080         // inherit styels from page...?? 
20081         if (this.stylesheets === false) {
20082             
20083             Roo.get(document.head).select('style').each(function(node) {
20084                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20085             });
20086             
20087             Roo.get(document.head).select('link').each(function(node) { 
20088                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20089             });
20090             
20091         } else if (!this.stylesheets.length) {
20092                 // simple..
20093                 st = '<style type="text/css">' +
20094                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20095                    '</style>';
20096         } else { 
20097             
20098         }
20099         
20100         st +=  '<style type="text/css">' +
20101             'IMG { cursor: pointer } ' +
20102         '</style>';
20103
20104         
20105         return '<html><head>' + st  +
20106             //<style type="text/css">' +
20107             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20108             //'</style>' +
20109             ' </head><body class="roo-htmleditor-body"></body></html>';
20110     },
20111
20112     // private
20113     onRender : function(ct, position)
20114     {
20115         var _t = this;
20116         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20117         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20118         
20119         
20120         this.el.dom.style.border = '0 none';
20121         this.el.dom.setAttribute('tabIndex', -1);
20122         this.el.addClass('x-hidden hide');
20123         
20124         
20125         
20126         if(Roo.isIE){ // fix IE 1px bogus margin
20127             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20128         }
20129        
20130         
20131         this.frameId = Roo.id();
20132         
20133          
20134         
20135         var iframe = this.owner.wrap.createChild({
20136             tag: 'iframe',
20137             cls: 'form-control', // bootstrap..
20138             id: this.frameId,
20139             name: this.frameId,
20140             frameBorder : 'no',
20141             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20142         }, this.el
20143         );
20144         
20145         
20146         this.iframe = iframe.dom;
20147
20148          this.assignDocWin();
20149         
20150         this.doc.designMode = 'on';
20151        
20152         this.doc.open();
20153         this.doc.write(this.getDocMarkup());
20154         this.doc.close();
20155
20156         
20157         var task = { // must defer to wait for browser to be ready
20158             run : function(){
20159                 //console.log("run task?" + this.doc.readyState);
20160                 this.assignDocWin();
20161                 if(this.doc.body || this.doc.readyState == 'complete'){
20162                     try {
20163                         this.doc.designMode="on";
20164                     } catch (e) {
20165                         return;
20166                     }
20167                     Roo.TaskMgr.stop(task);
20168                     this.initEditor.defer(10, this);
20169                 }
20170             },
20171             interval : 10,
20172             duration: 10000,
20173             scope: this
20174         };
20175         Roo.TaskMgr.start(task);
20176
20177     },
20178
20179     // private
20180     onResize : function(w, h)
20181     {
20182          Roo.log('resize: ' +w + ',' + h );
20183         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20184         if(!this.iframe){
20185             return;
20186         }
20187         if(typeof w == 'number'){
20188             
20189             this.iframe.style.width = w + 'px';
20190         }
20191         if(typeof h == 'number'){
20192             
20193             this.iframe.style.height = h + 'px';
20194             if(this.doc){
20195                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20196             }
20197         }
20198         
20199     },
20200
20201     /**
20202      * Toggles the editor between standard and source edit mode.
20203      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20204      */
20205     toggleSourceEdit : function(sourceEditMode){
20206         
20207         this.sourceEditMode = sourceEditMode === true;
20208         
20209         if(this.sourceEditMode){
20210  
20211             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20212             
20213         }else{
20214             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20215             //this.iframe.className = '';
20216             this.deferFocus();
20217         }
20218         //this.setSize(this.owner.wrap.getSize());
20219         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20220     },
20221
20222     
20223   
20224
20225     /**
20226      * Protected method that will not generally be called directly. If you need/want
20227      * custom HTML cleanup, this is the method you should override.
20228      * @param {String} html The HTML to be cleaned
20229      * return {String} The cleaned HTML
20230      */
20231     cleanHtml : function(html){
20232         html = String(html);
20233         if(html.length > 5){
20234             if(Roo.isSafari){ // strip safari nonsense
20235                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20236             }
20237         }
20238         if(html == '&nbsp;'){
20239             html = '';
20240         }
20241         return html;
20242     },
20243
20244     /**
20245      * HTML Editor -> Textarea
20246      * Protected method that will not generally be called directly. Syncs the contents
20247      * of the editor iframe with the textarea.
20248      */
20249     syncValue : function(){
20250         if(this.initialized){
20251             var bd = (this.doc.body || this.doc.documentElement);
20252             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20253             var html = bd.innerHTML;
20254             if(Roo.isSafari){
20255                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20256                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20257                 if(m && m[1]){
20258                     html = '<div style="'+m[0]+'">' + html + '</div>';
20259                 }
20260             }
20261             html = this.cleanHtml(html);
20262             // fix up the special chars.. normaly like back quotes in word...
20263             // however we do not want to do this with chinese..
20264             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20265                 var cc = b.charCodeAt();
20266                 if (
20267                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20268                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20269                     (cc >= 0xf900 && cc < 0xfb00 )
20270                 ) {
20271                         return b;
20272                 }
20273                 return "&#"+cc+";" 
20274             });
20275             if(this.owner.fireEvent('beforesync', this, html) !== false){
20276                 this.el.dom.value = html;
20277                 this.owner.fireEvent('sync', this, html);
20278             }
20279         }
20280     },
20281
20282     /**
20283      * Protected method that will not generally be called directly. Pushes the value of the textarea
20284      * into the iframe editor.
20285      */
20286     pushValue : function(){
20287         if(this.initialized){
20288             var v = this.el.dom.value.trim();
20289             
20290 //            if(v.length < 1){
20291 //                v = '&#160;';
20292 //            }
20293             
20294             if(this.owner.fireEvent('beforepush', this, v) !== false){
20295                 var d = (this.doc.body || this.doc.documentElement);
20296                 d.innerHTML = v;
20297                 this.cleanUpPaste();
20298                 this.el.dom.value = d.innerHTML;
20299                 this.owner.fireEvent('push', this, v);
20300             }
20301         }
20302     },
20303
20304     // private
20305     deferFocus : function(){
20306         this.focus.defer(10, this);
20307     },
20308
20309     // doc'ed in Field
20310     focus : function(){
20311         if(this.win && !this.sourceEditMode){
20312             this.win.focus();
20313         }else{
20314             this.el.focus();
20315         }
20316     },
20317     
20318     assignDocWin: function()
20319     {
20320         var iframe = this.iframe;
20321         
20322          if(Roo.isIE){
20323             this.doc = iframe.contentWindow.document;
20324             this.win = iframe.contentWindow;
20325         } else {
20326 //            if (!Roo.get(this.frameId)) {
20327 //                return;
20328 //            }
20329 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20330 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20331             
20332             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20333                 return;
20334             }
20335             
20336             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20337             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20338         }
20339     },
20340     
20341     // private
20342     initEditor : function(){
20343         //console.log("INIT EDITOR");
20344         this.assignDocWin();
20345         
20346         
20347         
20348         this.doc.designMode="on";
20349         this.doc.open();
20350         this.doc.write(this.getDocMarkup());
20351         this.doc.close();
20352         
20353         var dbody = (this.doc.body || this.doc.documentElement);
20354         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20355         // this copies styles from the containing element into thsi one..
20356         // not sure why we need all of this..
20357         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20358         
20359         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20360         //ss['background-attachment'] = 'fixed'; // w3c
20361         dbody.bgProperties = 'fixed'; // ie
20362         //Roo.DomHelper.applyStyles(dbody, ss);
20363         Roo.EventManager.on(this.doc, {
20364             //'mousedown': this.onEditorEvent,
20365             'mouseup': this.onEditorEvent,
20366             'dblclick': this.onEditorEvent,
20367             'click': this.onEditorEvent,
20368             'keyup': this.onEditorEvent,
20369             buffer:100,
20370             scope: this
20371         });
20372         if(Roo.isGecko){
20373             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20374         }
20375         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20376             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20377         }
20378         this.initialized = true;
20379
20380         this.owner.fireEvent('initialize', this);
20381         this.pushValue();
20382     },
20383
20384     // private
20385     onDestroy : function(){
20386         
20387         
20388         
20389         if(this.rendered){
20390             
20391             //for (var i =0; i < this.toolbars.length;i++) {
20392             //    // fixme - ask toolbars for heights?
20393             //    this.toolbars[i].onDestroy();
20394            // }
20395             
20396             //this.wrap.dom.innerHTML = '';
20397             //this.wrap.remove();
20398         }
20399     },
20400
20401     // private
20402     onFirstFocus : function(){
20403         
20404         this.assignDocWin();
20405         
20406         
20407         this.activated = true;
20408          
20409     
20410         if(Roo.isGecko){ // prevent silly gecko errors
20411             this.win.focus();
20412             var s = this.win.getSelection();
20413             if(!s.focusNode || s.focusNode.nodeType != 3){
20414                 var r = s.getRangeAt(0);
20415                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20416                 r.collapse(true);
20417                 this.deferFocus();
20418             }
20419             try{
20420                 this.execCmd('useCSS', true);
20421                 this.execCmd('styleWithCSS', false);
20422             }catch(e){}
20423         }
20424         this.owner.fireEvent('activate', this);
20425     },
20426
20427     // private
20428     adjustFont: function(btn){
20429         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20430         //if(Roo.isSafari){ // safari
20431         //    adjust *= 2;
20432        // }
20433         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20434         if(Roo.isSafari){ // safari
20435             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20436             v =  (v < 10) ? 10 : v;
20437             v =  (v > 48) ? 48 : v;
20438             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20439             
20440         }
20441         
20442         
20443         v = Math.max(1, v+adjust);
20444         
20445         this.execCmd('FontSize', v  );
20446     },
20447
20448     onEditorEvent : function(e)
20449     {
20450         this.owner.fireEvent('editorevent', this, e);
20451       //  this.updateToolbar();
20452         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20453     },
20454
20455     insertTag : function(tg)
20456     {
20457         // could be a bit smarter... -> wrap the current selected tRoo..
20458         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20459             
20460             range = this.createRange(this.getSelection());
20461             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20462             wrappingNode.appendChild(range.extractContents());
20463             range.insertNode(wrappingNode);
20464
20465             return;
20466             
20467             
20468             
20469         }
20470         this.execCmd("formatblock",   tg);
20471         
20472     },
20473     
20474     insertText : function(txt)
20475     {
20476         
20477         
20478         var range = this.createRange();
20479         range.deleteContents();
20480                //alert(Sender.getAttribute('label'));
20481                
20482         range.insertNode(this.doc.createTextNode(txt));
20483     } ,
20484     
20485      
20486
20487     /**
20488      * Executes a Midas editor command on the editor document and performs necessary focus and
20489      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20490      * @param {String} cmd The Midas command
20491      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20492      */
20493     relayCmd : function(cmd, value){
20494         this.win.focus();
20495         this.execCmd(cmd, value);
20496         this.owner.fireEvent('editorevent', this);
20497         //this.updateToolbar();
20498         this.owner.deferFocus();
20499     },
20500
20501     /**
20502      * Executes a Midas editor command directly on the editor document.
20503      * For visual commands, you should use {@link #relayCmd} instead.
20504      * <b>This should only be called after the editor is initialized.</b>
20505      * @param {String} cmd The Midas command
20506      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20507      */
20508     execCmd : function(cmd, value){
20509         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20510         this.syncValue();
20511     },
20512  
20513  
20514    
20515     /**
20516      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20517      * to insert tRoo.
20518      * @param {String} text | dom node.. 
20519      */
20520     insertAtCursor : function(text)
20521     {
20522         
20523         
20524         
20525         if(!this.activated){
20526             return;
20527         }
20528         /*
20529         if(Roo.isIE){
20530             this.win.focus();
20531             var r = this.doc.selection.createRange();
20532             if(r){
20533                 r.collapse(true);
20534                 r.pasteHTML(text);
20535                 this.syncValue();
20536                 this.deferFocus();
20537             
20538             }
20539             return;
20540         }
20541         */
20542         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20543             this.win.focus();
20544             
20545             
20546             // from jquery ui (MIT licenced)
20547             var range, node;
20548             var win = this.win;
20549             
20550             if (win.getSelection && win.getSelection().getRangeAt) {
20551                 range = win.getSelection().getRangeAt(0);
20552                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20553                 range.insertNode(node);
20554             } else if (win.document.selection && win.document.selection.createRange) {
20555                 // no firefox support
20556                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20557                 win.document.selection.createRange().pasteHTML(txt);
20558             } else {
20559                 // no firefox support
20560                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20561                 this.execCmd('InsertHTML', txt);
20562             } 
20563             
20564             this.syncValue();
20565             
20566             this.deferFocus();
20567         }
20568     },
20569  // private
20570     mozKeyPress : function(e){
20571         if(e.ctrlKey){
20572             var c = e.getCharCode(), cmd;
20573           
20574             if(c > 0){
20575                 c = String.fromCharCode(c).toLowerCase();
20576                 switch(c){
20577                     case 'b':
20578                         cmd = 'bold';
20579                         break;
20580                     case 'i':
20581                         cmd = 'italic';
20582                         break;
20583                     
20584                     case 'u':
20585                         cmd = 'underline';
20586                         break;
20587                     
20588                     case 'v':
20589                         this.cleanUpPaste.defer(100, this);
20590                         return;
20591                         
20592                 }
20593                 if(cmd){
20594                     this.win.focus();
20595                     this.execCmd(cmd);
20596                     this.deferFocus();
20597                     e.preventDefault();
20598                 }
20599                 
20600             }
20601         }
20602     },
20603
20604     // private
20605     fixKeys : function(){ // load time branching for fastest keydown performance
20606         if(Roo.isIE){
20607             return function(e){
20608                 var k = e.getKey(), r;
20609                 if(k == e.TAB){
20610                     e.stopEvent();
20611                     r = this.doc.selection.createRange();
20612                     if(r){
20613                         r.collapse(true);
20614                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20615                         this.deferFocus();
20616                     }
20617                     return;
20618                 }
20619                 
20620                 if(k == e.ENTER){
20621                     r = this.doc.selection.createRange();
20622                     if(r){
20623                         var target = r.parentElement();
20624                         if(!target || target.tagName.toLowerCase() != 'li'){
20625                             e.stopEvent();
20626                             r.pasteHTML('<br />');
20627                             r.collapse(false);
20628                             r.select();
20629                         }
20630                     }
20631                 }
20632                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20633                     this.cleanUpPaste.defer(100, this);
20634                     return;
20635                 }
20636                 
20637                 
20638             };
20639         }else if(Roo.isOpera){
20640             return function(e){
20641                 var k = e.getKey();
20642                 if(k == e.TAB){
20643                     e.stopEvent();
20644                     this.win.focus();
20645                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20646                     this.deferFocus();
20647                 }
20648                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20649                     this.cleanUpPaste.defer(100, this);
20650                     return;
20651                 }
20652                 
20653             };
20654         }else if(Roo.isSafari){
20655             return function(e){
20656                 var k = e.getKey();
20657                 
20658                 if(k == e.TAB){
20659                     e.stopEvent();
20660                     this.execCmd('InsertText','\t');
20661                     this.deferFocus();
20662                     return;
20663                 }
20664                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20665                     this.cleanUpPaste.defer(100, this);
20666                     return;
20667                 }
20668                 
20669              };
20670         }
20671     }(),
20672     
20673     getAllAncestors: function()
20674     {
20675         var p = this.getSelectedNode();
20676         var a = [];
20677         if (!p) {
20678             a.push(p); // push blank onto stack..
20679             p = this.getParentElement();
20680         }
20681         
20682         
20683         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20684             a.push(p);
20685             p = p.parentNode;
20686         }
20687         a.push(this.doc.body);
20688         return a;
20689     },
20690     lastSel : false,
20691     lastSelNode : false,
20692     
20693     
20694     getSelection : function() 
20695     {
20696         this.assignDocWin();
20697         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20698     },
20699     
20700     getSelectedNode: function() 
20701     {
20702         // this may only work on Gecko!!!
20703         
20704         // should we cache this!!!!
20705         
20706         
20707         
20708          
20709         var range = this.createRange(this.getSelection()).cloneRange();
20710         
20711         if (Roo.isIE) {
20712             var parent = range.parentElement();
20713             while (true) {
20714                 var testRange = range.duplicate();
20715                 testRange.moveToElementText(parent);
20716                 if (testRange.inRange(range)) {
20717                     break;
20718                 }
20719                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20720                     break;
20721                 }
20722                 parent = parent.parentElement;
20723             }
20724             return parent;
20725         }
20726         
20727         // is ancestor a text element.
20728         var ac =  range.commonAncestorContainer;
20729         if (ac.nodeType == 3) {
20730             ac = ac.parentNode;
20731         }
20732         
20733         var ar = ac.childNodes;
20734          
20735         var nodes = [];
20736         var other_nodes = [];
20737         var has_other_nodes = false;
20738         for (var i=0;i<ar.length;i++) {
20739             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20740                 continue;
20741             }
20742             // fullly contained node.
20743             
20744             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20745                 nodes.push(ar[i]);
20746                 continue;
20747             }
20748             
20749             // probably selected..
20750             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20751                 other_nodes.push(ar[i]);
20752                 continue;
20753             }
20754             // outer..
20755             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20756                 continue;
20757             }
20758             
20759             
20760             has_other_nodes = true;
20761         }
20762         if (!nodes.length && other_nodes.length) {
20763             nodes= other_nodes;
20764         }
20765         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20766             return false;
20767         }
20768         
20769         return nodes[0];
20770     },
20771     createRange: function(sel)
20772     {
20773         // this has strange effects when using with 
20774         // top toolbar - not sure if it's a great idea.
20775         //this.editor.contentWindow.focus();
20776         if (typeof sel != "undefined") {
20777             try {
20778                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20779             } catch(e) {
20780                 return this.doc.createRange();
20781             }
20782         } else {
20783             return this.doc.createRange();
20784         }
20785     },
20786     getParentElement: function()
20787     {
20788         
20789         this.assignDocWin();
20790         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20791         
20792         var range = this.createRange(sel);
20793          
20794         try {
20795             var p = range.commonAncestorContainer;
20796             while (p.nodeType == 3) { // text node
20797                 p = p.parentNode;
20798             }
20799             return p;
20800         } catch (e) {
20801             return null;
20802         }
20803     
20804     },
20805     /***
20806      *
20807      * Range intersection.. the hard stuff...
20808      *  '-1' = before
20809      *  '0' = hits..
20810      *  '1' = after.
20811      *         [ -- selected range --- ]
20812      *   [fail]                        [fail]
20813      *
20814      *    basically..
20815      *      if end is before start or  hits it. fail.
20816      *      if start is after end or hits it fail.
20817      *
20818      *   if either hits (but other is outside. - then it's not 
20819      *   
20820      *    
20821      **/
20822     
20823     
20824     // @see http://www.thismuchiknow.co.uk/?p=64.
20825     rangeIntersectsNode : function(range, node)
20826     {
20827         var nodeRange = node.ownerDocument.createRange();
20828         try {
20829             nodeRange.selectNode(node);
20830         } catch (e) {
20831             nodeRange.selectNodeContents(node);
20832         }
20833     
20834         var rangeStartRange = range.cloneRange();
20835         rangeStartRange.collapse(true);
20836     
20837         var rangeEndRange = range.cloneRange();
20838         rangeEndRange.collapse(false);
20839     
20840         var nodeStartRange = nodeRange.cloneRange();
20841         nodeStartRange.collapse(true);
20842     
20843         var nodeEndRange = nodeRange.cloneRange();
20844         nodeEndRange.collapse(false);
20845     
20846         return rangeStartRange.compareBoundaryPoints(
20847                  Range.START_TO_START, nodeEndRange) == -1 &&
20848                rangeEndRange.compareBoundaryPoints(
20849                  Range.START_TO_START, nodeStartRange) == 1;
20850         
20851          
20852     },
20853     rangeCompareNode : function(range, node)
20854     {
20855         var nodeRange = node.ownerDocument.createRange();
20856         try {
20857             nodeRange.selectNode(node);
20858         } catch (e) {
20859             nodeRange.selectNodeContents(node);
20860         }
20861         
20862         
20863         range.collapse(true);
20864     
20865         nodeRange.collapse(true);
20866      
20867         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20868         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20869          
20870         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20871         
20872         var nodeIsBefore   =  ss == 1;
20873         var nodeIsAfter    = ee == -1;
20874         
20875         if (nodeIsBefore && nodeIsAfter) {
20876             return 0; // outer
20877         }
20878         if (!nodeIsBefore && nodeIsAfter) {
20879             return 1; //right trailed.
20880         }
20881         
20882         if (nodeIsBefore && !nodeIsAfter) {
20883             return 2;  // left trailed.
20884         }
20885         // fully contined.
20886         return 3;
20887     },
20888
20889     // private? - in a new class?
20890     cleanUpPaste :  function()
20891     {
20892         // cleans up the whole document..
20893         Roo.log('cleanuppaste');
20894         
20895         this.cleanUpChildren(this.doc.body);
20896         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20897         if (clean != this.doc.body.innerHTML) {
20898             this.doc.body.innerHTML = clean;
20899         }
20900         
20901     },
20902     
20903     cleanWordChars : function(input) {// change the chars to hex code
20904         var he = Roo.HtmlEditorCore;
20905         
20906         var output = input;
20907         Roo.each(he.swapCodes, function(sw) { 
20908             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20909             
20910             output = output.replace(swapper, sw[1]);
20911         });
20912         
20913         return output;
20914     },
20915     
20916     
20917     cleanUpChildren : function (n)
20918     {
20919         if (!n.childNodes.length) {
20920             return;
20921         }
20922         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20923            this.cleanUpChild(n.childNodes[i]);
20924         }
20925     },
20926     
20927     
20928         
20929     
20930     cleanUpChild : function (node)
20931     {
20932         var ed = this;
20933         //console.log(node);
20934         if (node.nodeName == "#text") {
20935             // clean up silly Windows -- stuff?
20936             return; 
20937         }
20938         if (node.nodeName == "#comment") {
20939             node.parentNode.removeChild(node);
20940             // clean up silly Windows -- stuff?
20941             return; 
20942         }
20943         var lcname = node.tagName.toLowerCase();
20944         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20945         // whitelist of tags..
20946         
20947         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20948             // remove node.
20949             node.parentNode.removeChild(node);
20950             return;
20951             
20952         }
20953         
20954         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20955         
20956         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20957         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20958         
20959         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20960         //    remove_keep_children = true;
20961         //}
20962         
20963         if (remove_keep_children) {
20964             this.cleanUpChildren(node);
20965             // inserts everything just before this node...
20966             while (node.childNodes.length) {
20967                 var cn = node.childNodes[0];
20968                 node.removeChild(cn);
20969                 node.parentNode.insertBefore(cn, node);
20970             }
20971             node.parentNode.removeChild(node);
20972             return;
20973         }
20974         
20975         if (!node.attributes || !node.attributes.length) {
20976             this.cleanUpChildren(node);
20977             return;
20978         }
20979         
20980         function cleanAttr(n,v)
20981         {
20982             
20983             if (v.match(/^\./) || v.match(/^\//)) {
20984                 return;
20985             }
20986             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20987                 return;
20988             }
20989             if (v.match(/^#/)) {
20990                 return;
20991             }
20992 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20993             node.removeAttribute(n);
20994             
20995         }
20996         
20997         var cwhite = this.cwhite;
20998         var cblack = this.cblack;
20999             
21000         function cleanStyle(n,v)
21001         {
21002             if (v.match(/expression/)) { //XSS?? should we even bother..
21003                 node.removeAttribute(n);
21004                 return;
21005             }
21006             
21007             var parts = v.split(/;/);
21008             var clean = [];
21009             
21010             Roo.each(parts, function(p) {
21011                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21012                 if (!p.length) {
21013                     return true;
21014                 }
21015                 var l = p.split(':').shift().replace(/\s+/g,'');
21016                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21017                 
21018                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21019 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21020                     //node.removeAttribute(n);
21021                     return true;
21022                 }
21023                 //Roo.log()
21024                 // only allow 'c whitelisted system attributes'
21025                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21026 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21027                     //node.removeAttribute(n);
21028                     return true;
21029                 }
21030                 
21031                 
21032                  
21033                 
21034                 clean.push(p);
21035                 return true;
21036             });
21037             if (clean.length) { 
21038                 node.setAttribute(n, clean.join(';'));
21039             } else {
21040                 node.removeAttribute(n);
21041             }
21042             
21043         }
21044         
21045         
21046         for (var i = node.attributes.length-1; i > -1 ; i--) {
21047             var a = node.attributes[i];
21048             //console.log(a);
21049             
21050             if (a.name.toLowerCase().substr(0,2)=='on')  {
21051                 node.removeAttribute(a.name);
21052                 continue;
21053             }
21054             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21055                 node.removeAttribute(a.name);
21056                 continue;
21057             }
21058             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21059                 cleanAttr(a.name,a.value); // fixme..
21060                 continue;
21061             }
21062             if (a.name == 'style') {
21063                 cleanStyle(a.name,a.value);
21064                 continue;
21065             }
21066             /// clean up MS crap..
21067             // tecnically this should be a list of valid class'es..
21068             
21069             
21070             if (a.name == 'class') {
21071                 if (a.value.match(/^Mso/)) {
21072                     node.className = '';
21073                 }
21074                 
21075                 if (a.value.match(/body/)) {
21076                     node.className = '';
21077                 }
21078                 continue;
21079             }
21080             
21081             // style cleanup!?
21082             // class cleanup?
21083             
21084         }
21085         
21086         
21087         this.cleanUpChildren(node);
21088         
21089         
21090     },
21091     
21092     /**
21093      * Clean up MS wordisms...
21094      */
21095     cleanWord : function(node)
21096     {
21097         
21098         
21099         if (!node) {
21100             this.cleanWord(this.doc.body);
21101             return;
21102         }
21103         if (node.nodeName == "#text") {
21104             // clean up silly Windows -- stuff?
21105             return; 
21106         }
21107         if (node.nodeName == "#comment") {
21108             node.parentNode.removeChild(node);
21109             // clean up silly Windows -- stuff?
21110             return; 
21111         }
21112         
21113         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21114             node.parentNode.removeChild(node);
21115             return;
21116         }
21117         
21118         // remove - but keep children..
21119         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21120             while (node.childNodes.length) {
21121                 var cn = node.childNodes[0];
21122                 node.removeChild(cn);
21123                 node.parentNode.insertBefore(cn, node);
21124             }
21125             node.parentNode.removeChild(node);
21126             this.iterateChildren(node, this.cleanWord);
21127             return;
21128         }
21129         // clean styles
21130         if (node.className.length) {
21131             
21132             var cn = node.className.split(/\W+/);
21133             var cna = [];
21134             Roo.each(cn, function(cls) {
21135                 if (cls.match(/Mso[a-zA-Z]+/)) {
21136                     return;
21137                 }
21138                 cna.push(cls);
21139             });
21140             node.className = cna.length ? cna.join(' ') : '';
21141             if (!cna.length) {
21142                 node.removeAttribute("class");
21143             }
21144         }
21145         
21146         if (node.hasAttribute("lang")) {
21147             node.removeAttribute("lang");
21148         }
21149         
21150         if (node.hasAttribute("style")) {
21151             
21152             var styles = node.getAttribute("style").split(";");
21153             var nstyle = [];
21154             Roo.each(styles, function(s) {
21155                 if (!s.match(/:/)) {
21156                     return;
21157                 }
21158                 var kv = s.split(":");
21159                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21160                     return;
21161                 }
21162                 // what ever is left... we allow.
21163                 nstyle.push(s);
21164             });
21165             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21166             if (!nstyle.length) {
21167                 node.removeAttribute('style');
21168             }
21169         }
21170         this.iterateChildren(node, this.cleanWord);
21171         
21172         
21173         
21174     },
21175     /**
21176      * iterateChildren of a Node, calling fn each time, using this as the scole..
21177      * @param {DomNode} node node to iterate children of.
21178      * @param {Function} fn method of this class to call on each item.
21179      */
21180     iterateChildren : function(node, fn)
21181     {
21182         if (!node.childNodes.length) {
21183                 return;
21184         }
21185         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21186            fn.call(this, node.childNodes[i])
21187         }
21188     },
21189     
21190     
21191     /**
21192      * cleanTableWidths.
21193      *
21194      * Quite often pasting from word etc.. results in tables with column and widths.
21195      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21196      *
21197      */
21198     cleanTableWidths : function(node)
21199     {
21200          
21201          
21202         if (!node) {
21203             this.cleanTableWidths(this.doc.body);
21204             return;
21205         }
21206         
21207         // ignore list...
21208         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21209             return; 
21210         }
21211         Roo.log(node.tagName);
21212         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21213             this.iterateChildren(node, this.cleanTableWidths);
21214             return;
21215         }
21216         if (node.hasAttribute('width')) {
21217             node.removeAttribute('width');
21218         }
21219         
21220          
21221         if (node.hasAttribute("style")) {
21222             // pretty basic...
21223             
21224             var styles = node.getAttribute("style").split(";");
21225             var nstyle = [];
21226             Roo.each(styles, function(s) {
21227                 if (!s.match(/:/)) {
21228                     return;
21229                 }
21230                 var kv = s.split(":");
21231                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21232                     return;
21233                 }
21234                 // what ever is left... we allow.
21235                 nstyle.push(s);
21236             });
21237             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21238             if (!nstyle.length) {
21239                 node.removeAttribute('style');
21240             }
21241         }
21242         
21243         this.iterateChildren(node, this.cleanTableWidths);
21244         
21245         
21246     },
21247     
21248     
21249     
21250     
21251     domToHTML : function(currentElement, depth, nopadtext) {
21252         
21253         depth = depth || 0;
21254         nopadtext = nopadtext || false;
21255     
21256         if (!currentElement) {
21257             return this.domToHTML(this.doc.body);
21258         }
21259         
21260         //Roo.log(currentElement);
21261         var j;
21262         var allText = false;
21263         var nodeName = currentElement.nodeName;
21264         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21265         
21266         if  (nodeName == '#text') {
21267             
21268             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21269         }
21270         
21271         
21272         var ret = '';
21273         if (nodeName != 'BODY') {
21274              
21275             var i = 0;
21276             // Prints the node tagName, such as <A>, <IMG>, etc
21277             if (tagName) {
21278                 var attr = [];
21279                 for(i = 0; i < currentElement.attributes.length;i++) {
21280                     // quoting?
21281                     var aname = currentElement.attributes.item(i).name;
21282                     if (!currentElement.attributes.item(i).value.length) {
21283                         continue;
21284                     }
21285                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21286                 }
21287                 
21288                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21289             } 
21290             else {
21291                 
21292                 // eack
21293             }
21294         } else {
21295             tagName = false;
21296         }
21297         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21298             return ret;
21299         }
21300         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21301             nopadtext = true;
21302         }
21303         
21304         
21305         // Traverse the tree
21306         i = 0;
21307         var currentElementChild = currentElement.childNodes.item(i);
21308         var allText = true;
21309         var innerHTML  = '';
21310         lastnode = '';
21311         while (currentElementChild) {
21312             // Formatting code (indent the tree so it looks nice on the screen)
21313             var nopad = nopadtext;
21314             if (lastnode == 'SPAN') {
21315                 nopad  = true;
21316             }
21317             // text
21318             if  (currentElementChild.nodeName == '#text') {
21319                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21320                 toadd = nopadtext ? toadd : toadd.trim();
21321                 if (!nopad && toadd.length > 80) {
21322                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21323                 }
21324                 innerHTML  += toadd;
21325                 
21326                 i++;
21327                 currentElementChild = currentElement.childNodes.item(i);
21328                 lastNode = '';
21329                 continue;
21330             }
21331             allText = false;
21332             
21333             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21334                 
21335             // Recursively traverse the tree structure of the child node
21336             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21337             lastnode = currentElementChild.nodeName;
21338             i++;
21339             currentElementChild=currentElement.childNodes.item(i);
21340         }
21341         
21342         ret += innerHTML;
21343         
21344         if (!allText) {
21345                 // The remaining code is mostly for formatting the tree
21346             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21347         }
21348         
21349         
21350         if (tagName) {
21351             ret+= "</"+tagName+">";
21352         }
21353         return ret;
21354         
21355     },
21356         
21357     applyBlacklists : function()
21358     {
21359         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21360         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21361         
21362         this.white = [];
21363         this.black = [];
21364         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21365             if (b.indexOf(tag) > -1) {
21366                 return;
21367             }
21368             this.white.push(tag);
21369             
21370         }, this);
21371         
21372         Roo.each(w, function(tag) {
21373             if (b.indexOf(tag) > -1) {
21374                 return;
21375             }
21376             if (this.white.indexOf(tag) > -1) {
21377                 return;
21378             }
21379             this.white.push(tag);
21380             
21381         }, this);
21382         
21383         
21384         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21385             if (w.indexOf(tag) > -1) {
21386                 return;
21387             }
21388             this.black.push(tag);
21389             
21390         }, this);
21391         
21392         Roo.each(b, function(tag) {
21393             if (w.indexOf(tag) > -1) {
21394                 return;
21395             }
21396             if (this.black.indexOf(tag) > -1) {
21397                 return;
21398             }
21399             this.black.push(tag);
21400             
21401         }, this);
21402         
21403         
21404         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21405         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21406         
21407         this.cwhite = [];
21408         this.cblack = [];
21409         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21410             if (b.indexOf(tag) > -1) {
21411                 return;
21412             }
21413             this.cwhite.push(tag);
21414             
21415         }, this);
21416         
21417         Roo.each(w, function(tag) {
21418             if (b.indexOf(tag) > -1) {
21419                 return;
21420             }
21421             if (this.cwhite.indexOf(tag) > -1) {
21422                 return;
21423             }
21424             this.cwhite.push(tag);
21425             
21426         }, this);
21427         
21428         
21429         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21430             if (w.indexOf(tag) > -1) {
21431                 return;
21432             }
21433             this.cblack.push(tag);
21434             
21435         }, this);
21436         
21437         Roo.each(b, function(tag) {
21438             if (w.indexOf(tag) > -1) {
21439                 return;
21440             }
21441             if (this.cblack.indexOf(tag) > -1) {
21442                 return;
21443             }
21444             this.cblack.push(tag);
21445             
21446         }, this);
21447     },
21448     
21449     setStylesheets : function(stylesheets)
21450     {
21451         if(typeof(stylesheets) == 'string'){
21452             Roo.get(this.iframe.contentDocument.head).createChild({
21453                 tag : 'link',
21454                 rel : 'stylesheet',
21455                 type : 'text/css',
21456                 href : stylesheets
21457             });
21458             
21459             return;
21460         }
21461         var _this = this;
21462      
21463         Roo.each(stylesheets, function(s) {
21464             if(!s.length){
21465                 return;
21466             }
21467             
21468             Roo.get(_this.iframe.contentDocument.head).createChild({
21469                 tag : 'link',
21470                 rel : 'stylesheet',
21471                 type : 'text/css',
21472                 href : s
21473             });
21474         });
21475
21476         
21477     },
21478     
21479     removeStylesheets : function()
21480     {
21481         var _this = this;
21482         
21483         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21484             s.remove();
21485         });
21486     }
21487     
21488     // hide stuff that is not compatible
21489     /**
21490      * @event blur
21491      * @hide
21492      */
21493     /**
21494      * @event change
21495      * @hide
21496      */
21497     /**
21498      * @event focus
21499      * @hide
21500      */
21501     /**
21502      * @event specialkey
21503      * @hide
21504      */
21505     /**
21506      * @cfg {String} fieldClass @hide
21507      */
21508     /**
21509      * @cfg {String} focusClass @hide
21510      */
21511     /**
21512      * @cfg {String} autoCreate @hide
21513      */
21514     /**
21515      * @cfg {String} inputType @hide
21516      */
21517     /**
21518      * @cfg {String} invalidClass @hide
21519      */
21520     /**
21521      * @cfg {String} invalidText @hide
21522      */
21523     /**
21524      * @cfg {String} msgFx @hide
21525      */
21526     /**
21527      * @cfg {String} validateOnBlur @hide
21528      */
21529 });
21530
21531 Roo.HtmlEditorCore.white = [
21532         'area', 'br', 'img', 'input', 'hr', 'wbr',
21533         
21534        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21535        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21536        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21537        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21538        'table',   'ul',         'xmp', 
21539        
21540        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21541       'thead',   'tr', 
21542      
21543       'dir', 'menu', 'ol', 'ul', 'dl',
21544        
21545       'embed',  'object'
21546 ];
21547
21548
21549 Roo.HtmlEditorCore.black = [
21550     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21551         'applet', // 
21552         'base',   'basefont', 'bgsound', 'blink',  'body', 
21553         'frame',  'frameset', 'head',    'html',   'ilayer', 
21554         'iframe', 'layer',  'link',     'meta',    'object',   
21555         'script', 'style' ,'title',  'xml' // clean later..
21556 ];
21557 Roo.HtmlEditorCore.clean = [
21558     'script', 'style', 'title', 'xml'
21559 ];
21560 Roo.HtmlEditorCore.remove = [
21561     'font'
21562 ];
21563 // attributes..
21564
21565 Roo.HtmlEditorCore.ablack = [
21566     'on'
21567 ];
21568     
21569 Roo.HtmlEditorCore.aclean = [ 
21570     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21571 ];
21572
21573 // protocols..
21574 Roo.HtmlEditorCore.pwhite= [
21575         'http',  'https',  'mailto'
21576 ];
21577
21578 // white listed style attributes.
21579 Roo.HtmlEditorCore.cwhite= [
21580       //  'text-align', /// default is to allow most things..
21581       
21582          
21583 //        'font-size'//??
21584 ];
21585
21586 // black listed style attributes.
21587 Roo.HtmlEditorCore.cblack= [
21588       //  'font-size' -- this can be set by the project 
21589 ];
21590
21591
21592 Roo.HtmlEditorCore.swapCodes   =[ 
21593     [    8211, "--" ], 
21594     [    8212, "--" ], 
21595     [    8216,  "'" ],  
21596     [    8217, "'" ],  
21597     [    8220, '"' ],  
21598     [    8221, '"' ],  
21599     [    8226, "*" ],  
21600     [    8230, "..." ]
21601 ]; 
21602
21603     /*
21604  * - LGPL
21605  *
21606  * HtmlEditor
21607  * 
21608  */
21609
21610 /**
21611  * @class Roo.bootstrap.HtmlEditor
21612  * @extends Roo.bootstrap.TextArea
21613  * Bootstrap HtmlEditor class
21614
21615  * @constructor
21616  * Create a new HtmlEditor
21617  * @param {Object} config The config object
21618  */
21619
21620 Roo.bootstrap.HtmlEditor = function(config){
21621     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21622     if (!this.toolbars) {
21623         this.toolbars = [];
21624     }
21625     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21626     this.addEvents({
21627             /**
21628              * @event initialize
21629              * Fires when the editor is fully initialized (including the iframe)
21630              * @param {HtmlEditor} this
21631              */
21632             initialize: true,
21633             /**
21634              * @event activate
21635              * Fires when the editor is first receives the focus. Any insertion must wait
21636              * until after this event.
21637              * @param {HtmlEditor} this
21638              */
21639             activate: true,
21640              /**
21641              * @event beforesync
21642              * Fires before the textarea is updated with content from the editor iframe. Return false
21643              * to cancel the sync.
21644              * @param {HtmlEditor} this
21645              * @param {String} html
21646              */
21647             beforesync: true,
21648              /**
21649              * @event beforepush
21650              * Fires before the iframe editor is updated with content from the textarea. Return false
21651              * to cancel the push.
21652              * @param {HtmlEditor} this
21653              * @param {String} html
21654              */
21655             beforepush: true,
21656              /**
21657              * @event sync
21658              * Fires when the textarea is updated with content from the editor iframe.
21659              * @param {HtmlEditor} this
21660              * @param {String} html
21661              */
21662             sync: true,
21663              /**
21664              * @event push
21665              * Fires when the iframe editor is updated with content from the textarea.
21666              * @param {HtmlEditor} this
21667              * @param {String} html
21668              */
21669             push: true,
21670              /**
21671              * @event editmodechange
21672              * Fires when the editor switches edit modes
21673              * @param {HtmlEditor} this
21674              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21675              */
21676             editmodechange: true,
21677             /**
21678              * @event editorevent
21679              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21680              * @param {HtmlEditor} this
21681              */
21682             editorevent: true,
21683             /**
21684              * @event firstfocus
21685              * Fires when on first focus - needed by toolbars..
21686              * @param {HtmlEditor} this
21687              */
21688             firstfocus: true,
21689             /**
21690              * @event autosave
21691              * Auto save the htmlEditor value as a file into Events
21692              * @param {HtmlEditor} this
21693              */
21694             autosave: true,
21695             /**
21696              * @event savedpreview
21697              * preview the saved version of htmlEditor
21698              * @param {HtmlEditor} this
21699              */
21700             savedpreview: true
21701         });
21702 };
21703
21704
21705 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21706     
21707     
21708       /**
21709      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21710      */
21711     toolbars : false,
21712    
21713      /**
21714      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21715      *                        Roo.resizable.
21716      */
21717     resizable : false,
21718      /**
21719      * @cfg {Number} height (in pixels)
21720      */   
21721     height: 300,
21722    /**
21723      * @cfg {Number} width (in pixels)
21724      */   
21725     width: false,
21726     
21727     /**
21728      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21729      * 
21730      */
21731     stylesheets: false,
21732     
21733     // id of frame..
21734     frameId: false,
21735     
21736     // private properties
21737     validationEvent : false,
21738     deferHeight: true,
21739     initialized : false,
21740     activated : false,
21741     
21742     onFocus : Roo.emptyFn,
21743     iframePad:3,
21744     hideMode:'offsets',
21745     
21746     
21747     tbContainer : false,
21748     
21749     toolbarContainer :function() {
21750         return this.wrap.select('.x-html-editor-tb',true).first();
21751     },
21752
21753     /**
21754      * Protected method that will not generally be called directly. It
21755      * is called when the editor creates its toolbar. Override this method if you need to
21756      * add custom toolbar buttons.
21757      * @param {HtmlEditor} editor
21758      */
21759     createToolbar : function(){
21760         
21761         Roo.log("create toolbars");
21762         
21763         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21764         this.toolbars[0].render(this.toolbarContainer());
21765         
21766         return;
21767         
21768 //        if (!editor.toolbars || !editor.toolbars.length) {
21769 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21770 //        }
21771 //        
21772 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21773 //            editor.toolbars[i] = Roo.factory(
21774 //                    typeof(editor.toolbars[i]) == 'string' ?
21775 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21776 //                Roo.bootstrap.HtmlEditor);
21777 //            editor.toolbars[i].init(editor);
21778 //        }
21779     },
21780
21781      
21782     // private
21783     onRender : function(ct, position)
21784     {
21785        // Roo.log("Call onRender: " + this.xtype);
21786         var _t = this;
21787         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21788       
21789         this.wrap = this.inputEl().wrap({
21790             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21791         });
21792         
21793         this.editorcore.onRender(ct, position);
21794          
21795         if (this.resizable) {
21796             this.resizeEl = new Roo.Resizable(this.wrap, {
21797                 pinned : true,
21798                 wrap: true,
21799                 dynamic : true,
21800                 minHeight : this.height,
21801                 height: this.height,
21802                 handles : this.resizable,
21803                 width: this.width,
21804                 listeners : {
21805                     resize : function(r, w, h) {
21806                         _t.onResize(w,h); // -something
21807                     }
21808                 }
21809             });
21810             
21811         }
21812         this.createToolbar(this);
21813        
21814         
21815         if(!this.width && this.resizable){
21816             this.setSize(this.wrap.getSize());
21817         }
21818         if (this.resizeEl) {
21819             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21820             // should trigger onReize..
21821         }
21822         
21823     },
21824
21825     // private
21826     onResize : function(w, h)
21827     {
21828         Roo.log('resize: ' +w + ',' + h );
21829         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21830         var ew = false;
21831         var eh = false;
21832         
21833         if(this.inputEl() ){
21834             if(typeof w == 'number'){
21835                 var aw = w - this.wrap.getFrameWidth('lr');
21836                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21837                 ew = aw;
21838             }
21839             if(typeof h == 'number'){
21840                  var tbh = -11;  // fixme it needs to tool bar size!
21841                 for (var i =0; i < this.toolbars.length;i++) {
21842                     // fixme - ask toolbars for heights?
21843                     tbh += this.toolbars[i].el.getHeight();
21844                     //if (this.toolbars[i].footer) {
21845                     //    tbh += this.toolbars[i].footer.el.getHeight();
21846                     //}
21847                 }
21848               
21849                 
21850                 
21851                 
21852                 
21853                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21854                 ah -= 5; // knock a few pixes off for look..
21855                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21856                 var eh = ah;
21857             }
21858         }
21859         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21860         this.editorcore.onResize(ew,eh);
21861         
21862     },
21863
21864     /**
21865      * Toggles the editor between standard and source edit mode.
21866      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21867      */
21868     toggleSourceEdit : function(sourceEditMode)
21869     {
21870         this.editorcore.toggleSourceEdit(sourceEditMode);
21871         
21872         if(this.editorcore.sourceEditMode){
21873             Roo.log('editor - showing textarea');
21874             
21875 //            Roo.log('in');
21876 //            Roo.log(this.syncValue());
21877             this.syncValue();
21878             this.inputEl().removeClass(['hide', 'x-hidden']);
21879             this.inputEl().dom.removeAttribute('tabIndex');
21880             this.inputEl().focus();
21881         }else{
21882             Roo.log('editor - hiding textarea');
21883 //            Roo.log('out')
21884 //            Roo.log(this.pushValue()); 
21885             this.pushValue();
21886             
21887             this.inputEl().addClass(['hide', 'x-hidden']);
21888             this.inputEl().dom.setAttribute('tabIndex', -1);
21889             //this.deferFocus();
21890         }
21891          
21892         if(this.resizable){
21893             this.setSize(this.wrap.getSize());
21894         }
21895         
21896         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21897     },
21898  
21899     // private (for BoxComponent)
21900     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21901
21902     // private (for BoxComponent)
21903     getResizeEl : function(){
21904         return this.wrap;
21905     },
21906
21907     // private (for BoxComponent)
21908     getPositionEl : function(){
21909         return this.wrap;
21910     },
21911
21912     // private
21913     initEvents : function(){
21914         this.originalValue = this.getValue();
21915     },
21916
21917 //    /**
21918 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21919 //     * @method
21920 //     */
21921 //    markInvalid : Roo.emptyFn,
21922 //    /**
21923 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21924 //     * @method
21925 //     */
21926 //    clearInvalid : Roo.emptyFn,
21927
21928     setValue : function(v){
21929         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21930         this.editorcore.pushValue();
21931     },
21932
21933      
21934     // private
21935     deferFocus : function(){
21936         this.focus.defer(10, this);
21937     },
21938
21939     // doc'ed in Field
21940     focus : function(){
21941         this.editorcore.focus();
21942         
21943     },
21944       
21945
21946     // private
21947     onDestroy : function(){
21948         
21949         
21950         
21951         if(this.rendered){
21952             
21953             for (var i =0; i < this.toolbars.length;i++) {
21954                 // fixme - ask toolbars for heights?
21955                 this.toolbars[i].onDestroy();
21956             }
21957             
21958             this.wrap.dom.innerHTML = '';
21959             this.wrap.remove();
21960         }
21961     },
21962
21963     // private
21964     onFirstFocus : function(){
21965         //Roo.log("onFirstFocus");
21966         this.editorcore.onFirstFocus();
21967          for (var i =0; i < this.toolbars.length;i++) {
21968             this.toolbars[i].onFirstFocus();
21969         }
21970         
21971     },
21972     
21973     // private
21974     syncValue : function()
21975     {   
21976         this.editorcore.syncValue();
21977     },
21978     
21979     pushValue : function()
21980     {   
21981         this.editorcore.pushValue();
21982     }
21983      
21984     
21985     // hide stuff that is not compatible
21986     /**
21987      * @event blur
21988      * @hide
21989      */
21990     /**
21991      * @event change
21992      * @hide
21993      */
21994     /**
21995      * @event focus
21996      * @hide
21997      */
21998     /**
21999      * @event specialkey
22000      * @hide
22001      */
22002     /**
22003      * @cfg {String} fieldClass @hide
22004      */
22005     /**
22006      * @cfg {String} focusClass @hide
22007      */
22008     /**
22009      * @cfg {String} autoCreate @hide
22010      */
22011     /**
22012      * @cfg {String} inputType @hide
22013      */
22014     /**
22015      * @cfg {String} invalidClass @hide
22016      */
22017     /**
22018      * @cfg {String} invalidText @hide
22019      */
22020     /**
22021      * @cfg {String} msgFx @hide
22022      */
22023     /**
22024      * @cfg {String} validateOnBlur @hide
22025      */
22026 });
22027  
22028     
22029    
22030    
22031    
22032       
22033 Roo.namespace('Roo.bootstrap.htmleditor');
22034 /**
22035  * @class Roo.bootstrap.HtmlEditorToolbar1
22036  * Basic Toolbar
22037  * 
22038  * Usage:
22039  *
22040  new Roo.bootstrap.HtmlEditor({
22041     ....
22042     toolbars : [
22043         new Roo.bootstrap.HtmlEditorToolbar1({
22044             disable : { fonts: 1 , format: 1, ..., ... , ...],
22045             btns : [ .... ]
22046         })
22047     }
22048      
22049  * 
22050  * @cfg {Object} disable List of elements to disable..
22051  * @cfg {Array} btns List of additional buttons.
22052  * 
22053  * 
22054  * NEEDS Extra CSS? 
22055  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22056  */
22057  
22058 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22059 {
22060     
22061     Roo.apply(this, config);
22062     
22063     // default disabled, based on 'good practice'..
22064     this.disable = this.disable || {};
22065     Roo.applyIf(this.disable, {
22066         fontSize : true,
22067         colors : true,
22068         specialElements : true
22069     });
22070     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22071     
22072     this.editor = config.editor;
22073     this.editorcore = config.editor.editorcore;
22074     
22075     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22076     
22077     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22078     // dont call parent... till later.
22079 }
22080 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22081      
22082     bar : true,
22083     
22084     editor : false,
22085     editorcore : false,
22086     
22087     
22088     formats : [
22089         "p" ,  
22090         "h1","h2","h3","h4","h5","h6", 
22091         "pre", "code", 
22092         "abbr", "acronym", "address", "cite", "samp", "var",
22093         'div','span'
22094     ],
22095     
22096     onRender : function(ct, position)
22097     {
22098        // Roo.log("Call onRender: " + this.xtype);
22099         
22100        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22101        Roo.log(this.el);
22102        this.el.dom.style.marginBottom = '0';
22103        var _this = this;
22104        var editorcore = this.editorcore;
22105        var editor= this.editor;
22106        
22107        var children = [];
22108        var btn = function(id,cmd , toggle, handler){
22109        
22110             var  event = toggle ? 'toggle' : 'click';
22111        
22112             var a = {
22113                 size : 'sm',
22114                 xtype: 'Button',
22115                 xns: Roo.bootstrap,
22116                 glyphicon : id,
22117                 cmd : id || cmd,
22118                 enableToggle:toggle !== false,
22119                 //html : 'submit'
22120                 pressed : toggle ? false : null,
22121                 listeners : {}
22122             };
22123             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22124                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22125             };
22126             children.push(a);
22127             return a;
22128        }
22129         
22130         var style = {
22131                 xtype: 'Button',
22132                 size : 'sm',
22133                 xns: Roo.bootstrap,
22134                 glyphicon : 'font',
22135                 //html : 'submit'
22136                 menu : {
22137                     xtype: 'Menu',
22138                     xns: Roo.bootstrap,
22139                     items:  []
22140                 }
22141         };
22142         Roo.each(this.formats, function(f) {
22143             style.menu.items.push({
22144                 xtype :'MenuItem',
22145                 xns: Roo.bootstrap,
22146                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22147                 tagname : f,
22148                 listeners : {
22149                     click : function()
22150                     {
22151                         editorcore.insertTag(this.tagname);
22152                         editor.focus();
22153                     }
22154                 }
22155                 
22156             });
22157         });
22158          children.push(style);   
22159             
22160             
22161         btn('bold',false,true);
22162         btn('italic',false,true);
22163         btn('align-left', 'justifyleft',true);
22164         btn('align-center', 'justifycenter',true);
22165         btn('align-right' , 'justifyright',true);
22166         btn('link', false, false, function(btn) {
22167             //Roo.log("create link?");
22168             var url = prompt(this.createLinkText, this.defaultLinkValue);
22169             if(url && url != 'http:/'+'/'){
22170                 this.editorcore.relayCmd('createlink', url);
22171             }
22172         }),
22173         btn('list','insertunorderedlist',true);
22174         btn('pencil', false,true, function(btn){
22175                 Roo.log(this);
22176                 
22177                 this.toggleSourceEdit(btn.pressed);
22178         });
22179         /*
22180         var cog = {
22181                 xtype: 'Button',
22182                 size : 'sm',
22183                 xns: Roo.bootstrap,
22184                 glyphicon : 'cog',
22185                 //html : 'submit'
22186                 menu : {
22187                     xtype: 'Menu',
22188                     xns: Roo.bootstrap,
22189                     items:  []
22190                 }
22191         };
22192         
22193         cog.menu.items.push({
22194             xtype :'MenuItem',
22195             xns: Roo.bootstrap,
22196             html : Clean styles,
22197             tagname : f,
22198             listeners : {
22199                 click : function()
22200                 {
22201                     editorcore.insertTag(this.tagname);
22202                     editor.focus();
22203                 }
22204             }
22205             
22206         });
22207        */
22208         
22209          
22210        this.xtype = 'NavSimplebar';
22211         
22212         for(var i=0;i< children.length;i++) {
22213             
22214             this.buttons.add(this.addxtypeChild(children[i]));
22215             
22216         }
22217         
22218         editor.on('editorevent', this.updateToolbar, this);
22219     },
22220     onBtnClick : function(id)
22221     {
22222        this.editorcore.relayCmd(id);
22223        this.editorcore.focus();
22224     },
22225     
22226     /**
22227      * Protected method that will not generally be called directly. It triggers
22228      * a toolbar update by reading the markup state of the current selection in the editor.
22229      */
22230     updateToolbar: function(){
22231
22232         if(!this.editorcore.activated){
22233             this.editor.onFirstFocus(); // is this neeed?
22234             return;
22235         }
22236
22237         var btns = this.buttons; 
22238         var doc = this.editorcore.doc;
22239         btns.get('bold').setActive(doc.queryCommandState('bold'));
22240         btns.get('italic').setActive(doc.queryCommandState('italic'));
22241         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22242         
22243         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22244         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22245         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22246         
22247         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22248         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22249          /*
22250         
22251         var ans = this.editorcore.getAllAncestors();
22252         if (this.formatCombo) {
22253             
22254             
22255             var store = this.formatCombo.store;
22256             this.formatCombo.setValue("");
22257             for (var i =0; i < ans.length;i++) {
22258                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22259                     // select it..
22260                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22261                     break;
22262                 }
22263             }
22264         }
22265         
22266         
22267         
22268         // hides menus... - so this cant be on a menu...
22269         Roo.bootstrap.MenuMgr.hideAll();
22270         */
22271         Roo.bootstrap.MenuMgr.hideAll();
22272         //this.editorsyncValue();
22273     },
22274     onFirstFocus: function() {
22275         this.buttons.each(function(item){
22276            item.enable();
22277         });
22278     },
22279     toggleSourceEdit : function(sourceEditMode){
22280         
22281           
22282         if(sourceEditMode){
22283             Roo.log("disabling buttons");
22284            this.buttons.each( function(item){
22285                 if(item.cmd != 'pencil'){
22286                     item.disable();
22287                 }
22288             });
22289           
22290         }else{
22291             Roo.log("enabling buttons");
22292             if(this.editorcore.initialized){
22293                 this.buttons.each( function(item){
22294                     item.enable();
22295                 });
22296             }
22297             
22298         }
22299         Roo.log("calling toggole on editor");
22300         // tell the editor that it's been pressed..
22301         this.editor.toggleSourceEdit(sourceEditMode);
22302        
22303     }
22304 });
22305
22306
22307
22308
22309
22310 /**
22311  * @class Roo.bootstrap.Table.AbstractSelectionModel
22312  * @extends Roo.util.Observable
22313  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22314  * implemented by descendant classes.  This class should not be directly instantiated.
22315  * @constructor
22316  */
22317 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22318     this.locked = false;
22319     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22320 };
22321
22322
22323 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22324     /** @ignore Called by the grid automatically. Do not call directly. */
22325     init : function(grid){
22326         this.grid = grid;
22327         this.initEvents();
22328     },
22329
22330     /**
22331      * Locks the selections.
22332      */
22333     lock : function(){
22334         this.locked = true;
22335     },
22336
22337     /**
22338      * Unlocks the selections.
22339      */
22340     unlock : function(){
22341         this.locked = false;
22342     },
22343
22344     /**
22345      * Returns true if the selections are locked.
22346      * @return {Boolean}
22347      */
22348     isLocked : function(){
22349         return this.locked;
22350     }
22351 });
22352 /**
22353  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22354  * @class Roo.bootstrap.Table.RowSelectionModel
22355  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22356  * It supports multiple selections and keyboard selection/navigation. 
22357  * @constructor
22358  * @param {Object} config
22359  */
22360
22361 Roo.bootstrap.Table.RowSelectionModel = function(config){
22362     Roo.apply(this, config);
22363     this.selections = new Roo.util.MixedCollection(false, function(o){
22364         return o.id;
22365     });
22366
22367     this.last = false;
22368     this.lastActive = false;
22369
22370     this.addEvents({
22371         /**
22372              * @event selectionchange
22373              * Fires when the selection changes
22374              * @param {SelectionModel} this
22375              */
22376             "selectionchange" : true,
22377         /**
22378              * @event afterselectionchange
22379              * Fires after the selection changes (eg. by key press or clicking)
22380              * @param {SelectionModel} this
22381              */
22382             "afterselectionchange" : true,
22383         /**
22384              * @event beforerowselect
22385              * Fires when a row is selected being selected, return false to cancel.
22386              * @param {SelectionModel} this
22387              * @param {Number} rowIndex The selected index
22388              * @param {Boolean} keepExisting False if other selections will be cleared
22389              */
22390             "beforerowselect" : true,
22391         /**
22392              * @event rowselect
22393              * Fires when a row is selected.
22394              * @param {SelectionModel} this
22395              * @param {Number} rowIndex The selected index
22396              * @param {Roo.data.Record} r The record
22397              */
22398             "rowselect" : true,
22399         /**
22400              * @event rowdeselect
22401              * Fires when a row is deselected.
22402              * @param {SelectionModel} this
22403              * @param {Number} rowIndex The selected index
22404              */
22405         "rowdeselect" : true
22406     });
22407     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22408     this.locked = false;
22409  };
22410
22411 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22412     /**
22413      * @cfg {Boolean} singleSelect
22414      * True to allow selection of only one row at a time (defaults to false)
22415      */
22416     singleSelect : false,
22417
22418     // private
22419     initEvents : function()
22420     {
22421
22422         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22423         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22424         //}else{ // allow click to work like normal
22425          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22426         //}
22427         this.grid.on("rowclick", this.handleMouseDown, this);
22428         
22429         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22430             "up" : function(e){
22431                 if(!e.shiftKey){
22432                     this.selectPrevious(e.shiftKey);
22433                 }else if(this.last !== false && this.lastActive !== false){
22434                     var last = this.last;
22435                     this.selectRange(this.last,  this.lastActive-1);
22436                     this.grid.getView().focusRow(this.lastActive);
22437                     if(last !== false){
22438                         this.last = last;
22439                     }
22440                 }else{
22441                     this.selectFirstRow();
22442                 }
22443                 this.fireEvent("afterselectionchange", this);
22444             },
22445             "down" : function(e){
22446                 if(!e.shiftKey){
22447                     this.selectNext(e.shiftKey);
22448                 }else if(this.last !== false && this.lastActive !== false){
22449                     var last = this.last;
22450                     this.selectRange(this.last,  this.lastActive+1);
22451                     this.grid.getView().focusRow(this.lastActive);
22452                     if(last !== false){
22453                         this.last = last;
22454                     }
22455                 }else{
22456                     this.selectFirstRow();
22457                 }
22458                 this.fireEvent("afterselectionchange", this);
22459             },
22460             scope: this
22461         });
22462         /*
22463         var view = this.grid.view;
22464         view.on("refresh", this.onRefresh, this);
22465         view.on("rowupdated", this.onRowUpdated, this);
22466         view.on("rowremoved", this.onRemove, this);
22467         */
22468     },
22469
22470     // private
22471     onRefresh : function(){
22472         var ds = this.grid.dataSource, i, v = this.grid.view;
22473         var s = this.selections;
22474         s.each(function(r){
22475             if((i = ds.indexOfId(r.id)) != -1){
22476                 v.onRowSelect(i);
22477             }else{
22478                 s.remove(r);
22479             }
22480         });
22481     },
22482
22483     // private
22484     onRemove : function(v, index, r){
22485         this.selections.remove(r);
22486     },
22487
22488     // private
22489     onRowUpdated : function(v, index, r){
22490         if(this.isSelected(r)){
22491             v.onRowSelect(index);
22492         }
22493     },
22494
22495     /**
22496      * Select records.
22497      * @param {Array} records The records to select
22498      * @param {Boolean} keepExisting (optional) True to keep existing selections
22499      */
22500     selectRecords : function(records, keepExisting){
22501         if(!keepExisting){
22502             this.clearSelections();
22503         }
22504         var ds = this.grid.dataSource;
22505         for(var i = 0, len = records.length; i < len; i++){
22506             this.selectRow(ds.indexOf(records[i]), true);
22507         }
22508     },
22509
22510     /**
22511      * Gets the number of selected rows.
22512      * @return {Number}
22513      */
22514     getCount : function(){
22515         return this.selections.length;
22516     },
22517
22518     /**
22519      * Selects the first row in the grid.
22520      */
22521     selectFirstRow : function(){
22522         this.selectRow(0);
22523     },
22524
22525     /**
22526      * Select the last row.
22527      * @param {Boolean} keepExisting (optional) True to keep existing selections
22528      */
22529     selectLastRow : function(keepExisting){
22530         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22531     },
22532
22533     /**
22534      * Selects the row immediately following the last selected row.
22535      * @param {Boolean} keepExisting (optional) True to keep existing selections
22536      */
22537     selectNext : function(keepExisting){
22538         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22539             this.selectRow(this.last+1, keepExisting);
22540             this.grid.getView().focusRow(this.last);
22541         }
22542     },
22543
22544     /**
22545      * Selects the row that precedes the last selected row.
22546      * @param {Boolean} keepExisting (optional) True to keep existing selections
22547      */
22548     selectPrevious : function(keepExisting){
22549         if(this.last){
22550             this.selectRow(this.last-1, keepExisting);
22551             this.grid.getView().focusRow(this.last);
22552         }
22553     },
22554
22555     /**
22556      * Returns the selected records
22557      * @return {Array} Array of selected records
22558      */
22559     getSelections : function(){
22560         return [].concat(this.selections.items);
22561     },
22562
22563     /**
22564      * Returns the first selected record.
22565      * @return {Record}
22566      */
22567     getSelected : function(){
22568         return this.selections.itemAt(0);
22569     },
22570
22571
22572     /**
22573      * Clears all selections.
22574      */
22575     clearSelections : function(fast){
22576         if(this.locked) {
22577             return;
22578         }
22579         if(fast !== true){
22580             var ds = this.grid.dataSource;
22581             var s = this.selections;
22582             s.each(function(r){
22583                 this.deselectRow(ds.indexOfId(r.id));
22584             }, this);
22585             s.clear();
22586         }else{
22587             this.selections.clear();
22588         }
22589         this.last = false;
22590     },
22591
22592
22593     /**
22594      * Selects all rows.
22595      */
22596     selectAll : function(){
22597         if(this.locked) {
22598             return;
22599         }
22600         this.selections.clear();
22601         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22602             this.selectRow(i, true);
22603         }
22604     },
22605
22606     /**
22607      * Returns True if there is a selection.
22608      * @return {Boolean}
22609      */
22610     hasSelection : function(){
22611         return this.selections.length > 0;
22612     },
22613
22614     /**
22615      * Returns True if the specified row is selected.
22616      * @param {Number/Record} record The record or index of the record to check
22617      * @return {Boolean}
22618      */
22619     isSelected : function(index){
22620         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22621         return (r && this.selections.key(r.id) ? true : false);
22622     },
22623
22624     /**
22625      * Returns True if the specified record id is selected.
22626      * @param {String} id The id of record to check
22627      * @return {Boolean}
22628      */
22629     isIdSelected : function(id){
22630         return (this.selections.key(id) ? true : false);
22631     },
22632
22633     // private
22634     handleMouseDown : function(e, t){
22635         var view = this.grid.getView(), rowIndex;
22636         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22637             return;
22638         };
22639         if(e.shiftKey && this.last !== false){
22640             var last = this.last;
22641             this.selectRange(last, rowIndex, e.ctrlKey);
22642             this.last = last; // reset the last
22643             view.focusRow(rowIndex);
22644         }else{
22645             var isSelected = this.isSelected(rowIndex);
22646             if(e.button !== 0 && isSelected){
22647                 view.focusRow(rowIndex);
22648             }else if(e.ctrlKey && isSelected){
22649                 this.deselectRow(rowIndex);
22650             }else if(!isSelected){
22651                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22652                 view.focusRow(rowIndex);
22653             }
22654         }
22655         this.fireEvent("afterselectionchange", this);
22656     },
22657     // private
22658     handleDragableRowClick :  function(grid, rowIndex, e) 
22659     {
22660         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22661             this.selectRow(rowIndex, false);
22662             grid.view.focusRow(rowIndex);
22663              this.fireEvent("afterselectionchange", this);
22664         }
22665     },
22666     
22667     /**
22668      * Selects multiple rows.
22669      * @param {Array} rows Array of the indexes of the row to select
22670      * @param {Boolean} keepExisting (optional) True to keep existing selections
22671      */
22672     selectRows : function(rows, keepExisting){
22673         if(!keepExisting){
22674             this.clearSelections();
22675         }
22676         for(var i = 0, len = rows.length; i < len; i++){
22677             this.selectRow(rows[i], true);
22678         }
22679     },
22680
22681     /**
22682      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22683      * @param {Number} startRow The index of the first row in the range
22684      * @param {Number} endRow The index of the last row in the range
22685      * @param {Boolean} keepExisting (optional) True to retain existing selections
22686      */
22687     selectRange : function(startRow, endRow, keepExisting){
22688         if(this.locked) {
22689             return;
22690         }
22691         if(!keepExisting){
22692             this.clearSelections();
22693         }
22694         if(startRow <= endRow){
22695             for(var i = startRow; i <= endRow; i++){
22696                 this.selectRow(i, true);
22697             }
22698         }else{
22699             for(var i = startRow; i >= endRow; i--){
22700                 this.selectRow(i, true);
22701             }
22702         }
22703     },
22704
22705     /**
22706      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22707      * @param {Number} startRow The index of the first row in the range
22708      * @param {Number} endRow The index of the last row in the range
22709      */
22710     deselectRange : function(startRow, endRow, preventViewNotify){
22711         if(this.locked) {
22712             return;
22713         }
22714         for(var i = startRow; i <= endRow; i++){
22715             this.deselectRow(i, preventViewNotify);
22716         }
22717     },
22718
22719     /**
22720      * Selects a row.
22721      * @param {Number} row The index of the row to select
22722      * @param {Boolean} keepExisting (optional) True to keep existing selections
22723      */
22724     selectRow : function(index, keepExisting, preventViewNotify){
22725         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22726             return;
22727         }
22728         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22729             if(!keepExisting || this.singleSelect){
22730                 this.clearSelections();
22731             }
22732             var r = this.grid.dataSource.getAt(index);
22733             this.selections.add(r);
22734             this.last = this.lastActive = index;
22735             if(!preventViewNotify){
22736                 this.grid.getView().onRowSelect(index);
22737             }
22738             this.fireEvent("rowselect", this, index, r);
22739             this.fireEvent("selectionchange", this);
22740         }
22741     },
22742
22743     /**
22744      * Deselects a row.
22745      * @param {Number} row The index of the row to deselect
22746      */
22747     deselectRow : function(index, preventViewNotify){
22748         if(this.locked) {
22749             return;
22750         }
22751         if(this.last == index){
22752             this.last = false;
22753         }
22754         if(this.lastActive == index){
22755             this.lastActive = false;
22756         }
22757         var r = this.grid.dataSource.getAt(index);
22758         this.selections.remove(r);
22759         if(!preventViewNotify){
22760             this.grid.getView().onRowDeselect(index);
22761         }
22762         this.fireEvent("rowdeselect", this, index);
22763         this.fireEvent("selectionchange", this);
22764     },
22765
22766     // private
22767     restoreLast : function(){
22768         if(this._last){
22769             this.last = this._last;
22770         }
22771     },
22772
22773     // private
22774     acceptsNav : function(row, col, cm){
22775         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22776     },
22777
22778     // private
22779     onEditorKey : function(field, e){
22780         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22781         if(k == e.TAB){
22782             e.stopEvent();
22783             ed.completeEdit();
22784             if(e.shiftKey){
22785                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22786             }else{
22787                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22788             }
22789         }else if(k == e.ENTER && !e.ctrlKey){
22790             e.stopEvent();
22791             ed.completeEdit();
22792             if(e.shiftKey){
22793                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22794             }else{
22795                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22796             }
22797         }else if(k == e.ESC){
22798             ed.cancelEdit();
22799         }
22800         if(newCell){
22801             g.startEditing(newCell[0], newCell[1]);
22802         }
22803     }
22804 });/*
22805  * Based on:
22806  * Ext JS Library 1.1.1
22807  * Copyright(c) 2006-2007, Ext JS, LLC.
22808  *
22809  * Originally Released Under LGPL - original licence link has changed is not relivant.
22810  *
22811  * Fork - LGPL
22812  * <script type="text/javascript">
22813  */
22814  
22815 /**
22816  * @class Roo.bootstrap.PagingToolbar
22817  * @extends Roo.bootstrap.NavSimplebar
22818  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22819  * @constructor
22820  * Create a new PagingToolbar
22821  * @param {Object} config The config object
22822  * @param {Roo.data.Store} store
22823  */
22824 Roo.bootstrap.PagingToolbar = function(config)
22825 {
22826     // old args format still supported... - xtype is prefered..
22827         // created from xtype...
22828     
22829     this.ds = config.dataSource;
22830     
22831     if (config.store && !this.ds) {
22832         this.store= Roo.factory(config.store, Roo.data);
22833         this.ds = this.store;
22834         this.ds.xmodule = this.xmodule || false;
22835     }
22836     
22837     this.toolbarItems = [];
22838     if (config.items) {
22839         this.toolbarItems = config.items;
22840     }
22841     
22842     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22843     
22844     this.cursor = 0;
22845     
22846     if (this.ds) { 
22847         this.bind(this.ds);
22848     }
22849     
22850     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22851     
22852 };
22853
22854 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22855     /**
22856      * @cfg {Roo.data.Store} dataSource
22857      * The underlying data store providing the paged data
22858      */
22859     /**
22860      * @cfg {String/HTMLElement/Element} container
22861      * container The id or element that will contain the toolbar
22862      */
22863     /**
22864      * @cfg {Boolean} displayInfo
22865      * True to display the displayMsg (defaults to false)
22866      */
22867     /**
22868      * @cfg {Number} pageSize
22869      * The number of records to display per page (defaults to 20)
22870      */
22871     pageSize: 20,
22872     /**
22873      * @cfg {String} displayMsg
22874      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22875      */
22876     displayMsg : 'Displaying {0} - {1} of {2}',
22877     /**
22878      * @cfg {String} emptyMsg
22879      * The message to display when no records are found (defaults to "No data to display")
22880      */
22881     emptyMsg : 'No data to display',
22882     /**
22883      * Customizable piece of the default paging text (defaults to "Page")
22884      * @type String
22885      */
22886     beforePageText : "Page",
22887     /**
22888      * Customizable piece of the default paging text (defaults to "of %0")
22889      * @type String
22890      */
22891     afterPageText : "of {0}",
22892     /**
22893      * Customizable piece of the default paging text (defaults to "First Page")
22894      * @type String
22895      */
22896     firstText : "First Page",
22897     /**
22898      * Customizable piece of the default paging text (defaults to "Previous Page")
22899      * @type String
22900      */
22901     prevText : "Previous Page",
22902     /**
22903      * Customizable piece of the default paging text (defaults to "Next Page")
22904      * @type String
22905      */
22906     nextText : "Next Page",
22907     /**
22908      * Customizable piece of the default paging text (defaults to "Last Page")
22909      * @type String
22910      */
22911     lastText : "Last Page",
22912     /**
22913      * Customizable piece of the default paging text (defaults to "Refresh")
22914      * @type String
22915      */
22916     refreshText : "Refresh",
22917
22918     buttons : false,
22919     // private
22920     onRender : function(ct, position) 
22921     {
22922         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22923         this.navgroup.parentId = this.id;
22924         this.navgroup.onRender(this.el, null);
22925         // add the buttons to the navgroup
22926         
22927         if(this.displayInfo){
22928             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22929             this.displayEl = this.el.select('.x-paging-info', true).first();
22930 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22931 //            this.displayEl = navel.el.select('span',true).first();
22932         }
22933         
22934         var _this = this;
22935         
22936         if(this.buttons){
22937             Roo.each(_this.buttons, function(e){ // this might need to use render????
22938                Roo.factory(e).onRender(_this.el, null);
22939             });
22940         }
22941             
22942         Roo.each(_this.toolbarItems, function(e) {
22943             _this.navgroup.addItem(e);
22944         });
22945         
22946         
22947         this.first = this.navgroup.addItem({
22948             tooltip: this.firstText,
22949             cls: "prev",
22950             icon : 'fa fa-backward',
22951             disabled: true,
22952             preventDefault: true,
22953             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22954         });
22955         
22956         this.prev =  this.navgroup.addItem({
22957             tooltip: this.prevText,
22958             cls: "prev",
22959             icon : 'fa fa-step-backward',
22960             disabled: true,
22961             preventDefault: true,
22962             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22963         });
22964     //this.addSeparator();
22965         
22966         
22967         var field = this.navgroup.addItem( {
22968             tagtype : 'span',
22969             cls : 'x-paging-position',
22970             
22971             html : this.beforePageText  +
22972                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22973                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22974          } ); //?? escaped?
22975         
22976         this.field = field.el.select('input', true).first();
22977         this.field.on("keydown", this.onPagingKeydown, this);
22978         this.field.on("focus", function(){this.dom.select();});
22979     
22980     
22981         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22982         //this.field.setHeight(18);
22983         //this.addSeparator();
22984         this.next = this.navgroup.addItem({
22985             tooltip: this.nextText,
22986             cls: "next",
22987             html : ' <i class="fa fa-step-forward">',
22988             disabled: true,
22989             preventDefault: true,
22990             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22991         });
22992         this.last = this.navgroup.addItem({
22993             tooltip: this.lastText,
22994             icon : 'fa fa-forward',
22995             cls: "next",
22996             disabled: true,
22997             preventDefault: true,
22998             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22999         });
23000     //this.addSeparator();
23001         this.loading = this.navgroup.addItem({
23002             tooltip: this.refreshText,
23003             icon: 'fa fa-refresh',
23004             preventDefault: true,
23005             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23006         });
23007         
23008     },
23009
23010     // private
23011     updateInfo : function(){
23012         if(this.displayEl){
23013             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23014             var msg = count == 0 ?
23015                 this.emptyMsg :
23016                 String.format(
23017                     this.displayMsg,
23018                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23019                 );
23020             this.displayEl.update(msg);
23021         }
23022     },
23023
23024     // private
23025     onLoad : function(ds, r, o){
23026        this.cursor = o.params ? o.params.start : 0;
23027        var d = this.getPageData(),
23028             ap = d.activePage,
23029             ps = d.pages;
23030         
23031        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23032        this.field.dom.value = ap;
23033        this.first.setDisabled(ap == 1);
23034        this.prev.setDisabled(ap == 1);
23035        this.next.setDisabled(ap == ps);
23036        this.last.setDisabled(ap == ps);
23037        this.loading.enable();
23038        this.updateInfo();
23039     },
23040
23041     // private
23042     getPageData : function(){
23043         var total = this.ds.getTotalCount();
23044         return {
23045             total : total,
23046             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23047             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23048         };
23049     },
23050
23051     // private
23052     onLoadError : function(){
23053         this.loading.enable();
23054     },
23055
23056     // private
23057     onPagingKeydown : function(e){
23058         var k = e.getKey();
23059         var d = this.getPageData();
23060         if(k == e.RETURN){
23061             var v = this.field.dom.value, pageNum;
23062             if(!v || isNaN(pageNum = parseInt(v, 10))){
23063                 this.field.dom.value = d.activePage;
23064                 return;
23065             }
23066             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23067             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23068             e.stopEvent();
23069         }
23070         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))
23071         {
23072           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23073           this.field.dom.value = pageNum;
23074           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23075           e.stopEvent();
23076         }
23077         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23078         {
23079           var v = this.field.dom.value, pageNum; 
23080           var increment = (e.shiftKey) ? 10 : 1;
23081           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23082                 increment *= -1;
23083           }
23084           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23085             this.field.dom.value = d.activePage;
23086             return;
23087           }
23088           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23089           {
23090             this.field.dom.value = parseInt(v, 10) + increment;
23091             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23092             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23093           }
23094           e.stopEvent();
23095         }
23096     },
23097
23098     // private
23099     beforeLoad : function(){
23100         if(this.loading){
23101             this.loading.disable();
23102         }
23103     },
23104
23105     // private
23106     onClick : function(which){
23107         
23108         var ds = this.ds;
23109         if (!ds) {
23110             return;
23111         }
23112         
23113         switch(which){
23114             case "first":
23115                 ds.load({params:{start: 0, limit: this.pageSize}});
23116             break;
23117             case "prev":
23118                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23119             break;
23120             case "next":
23121                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23122             break;
23123             case "last":
23124                 var total = ds.getTotalCount();
23125                 var extra = total % this.pageSize;
23126                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23127                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23128             break;
23129             case "refresh":
23130                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23131             break;
23132         }
23133     },
23134
23135     /**
23136      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23137      * @param {Roo.data.Store} store The data store to unbind
23138      */
23139     unbind : function(ds){
23140         ds.un("beforeload", this.beforeLoad, this);
23141         ds.un("load", this.onLoad, this);
23142         ds.un("loadexception", this.onLoadError, this);
23143         ds.un("remove", this.updateInfo, this);
23144         ds.un("add", this.updateInfo, this);
23145         this.ds = undefined;
23146     },
23147
23148     /**
23149      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23150      * @param {Roo.data.Store} store The data store to bind
23151      */
23152     bind : function(ds){
23153         ds.on("beforeload", this.beforeLoad, this);
23154         ds.on("load", this.onLoad, this);
23155         ds.on("loadexception", this.onLoadError, this);
23156         ds.on("remove", this.updateInfo, this);
23157         ds.on("add", this.updateInfo, this);
23158         this.ds = ds;
23159     }
23160 });/*
23161  * - LGPL
23162  *
23163  * element
23164  * 
23165  */
23166
23167 /**
23168  * @class Roo.bootstrap.MessageBar
23169  * @extends Roo.bootstrap.Component
23170  * Bootstrap MessageBar class
23171  * @cfg {String} html contents of the MessageBar
23172  * @cfg {String} weight (info | success | warning | danger) default info
23173  * @cfg {String} beforeClass insert the bar before the given class
23174  * @cfg {Boolean} closable (true | false) default false
23175  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23176  * 
23177  * @constructor
23178  * Create a new Element
23179  * @param {Object} config The config object
23180  */
23181
23182 Roo.bootstrap.MessageBar = function(config){
23183     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23184 };
23185
23186 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23187     
23188     html: '',
23189     weight: 'info',
23190     closable: false,
23191     fixed: false,
23192     beforeClass: 'bootstrap-sticky-wrap',
23193     
23194     getAutoCreate : function(){
23195         
23196         var cfg = {
23197             tag: 'div',
23198             cls: 'alert alert-dismissable alert-' + this.weight,
23199             cn: [
23200                 {
23201                     tag: 'span',
23202                     cls: 'message',
23203                     html: this.html || ''
23204                 }
23205             ]
23206         };
23207         
23208         if(this.fixed){
23209             cfg.cls += ' alert-messages-fixed';
23210         }
23211         
23212         if(this.closable){
23213             cfg.cn.push({
23214                 tag: 'button',
23215                 cls: 'close',
23216                 html: 'x'
23217             });
23218         }
23219         
23220         return cfg;
23221     },
23222     
23223     onRender : function(ct, position)
23224     {
23225         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23226         
23227         if(!this.el){
23228             var cfg = Roo.apply({},  this.getAutoCreate());
23229             cfg.id = Roo.id();
23230             
23231             if (this.cls) {
23232                 cfg.cls += ' ' + this.cls;
23233             }
23234             if (this.style) {
23235                 cfg.style = this.style;
23236             }
23237             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23238             
23239             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23240         }
23241         
23242         this.el.select('>button.close').on('click', this.hide, this);
23243         
23244     },
23245     
23246     show : function()
23247     {
23248         if (!this.rendered) {
23249             this.render();
23250         }
23251         
23252         this.el.show();
23253         
23254         this.fireEvent('show', this);
23255         
23256     },
23257     
23258     hide : function()
23259     {
23260         if (!this.rendered) {
23261             this.render();
23262         }
23263         
23264         this.el.hide();
23265         
23266         this.fireEvent('hide', this);
23267     },
23268     
23269     update : function()
23270     {
23271 //        var e = this.el.dom.firstChild;
23272 //        
23273 //        if(this.closable){
23274 //            e = e.nextSibling;
23275 //        }
23276 //        
23277 //        e.data = this.html || '';
23278
23279         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23280     }
23281    
23282 });
23283
23284  
23285
23286      /*
23287  * - LGPL
23288  *
23289  * Graph
23290  * 
23291  */
23292
23293
23294 /**
23295  * @class Roo.bootstrap.Graph
23296  * @extends Roo.bootstrap.Component
23297  * Bootstrap Graph class
23298 > Prameters
23299  -sm {number} sm 4
23300  -md {number} md 5
23301  @cfg {String} graphtype  bar | vbar | pie
23302  @cfg {number} g_x coodinator | centre x (pie)
23303  @cfg {number} g_y coodinator | centre y (pie)
23304  @cfg {number} g_r radius (pie)
23305  @cfg {number} g_height height of the chart (respected by all elements in the set)
23306  @cfg {number} g_width width of the chart (respected by all elements in the set)
23307  @cfg {Object} title The title of the chart
23308     
23309  -{Array}  values
23310  -opts (object) options for the chart 
23311      o {
23312      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23313      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23314      o vgutter (number)
23315      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.
23316      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23317      o to
23318      o stretch (boolean)
23319      o }
23320  -opts (object) options for the pie
23321      o{
23322      o cut
23323      o startAngle (number)
23324      o endAngle (number)
23325      } 
23326  *
23327  * @constructor
23328  * Create a new Input
23329  * @param {Object} config The config object
23330  */
23331
23332 Roo.bootstrap.Graph = function(config){
23333     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23334     
23335     this.addEvents({
23336         // img events
23337         /**
23338          * @event click
23339          * The img click event for the img.
23340          * @param {Roo.EventObject} e
23341          */
23342         "click" : true
23343     });
23344 };
23345
23346 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23347     
23348     sm: 4,
23349     md: 5,
23350     graphtype: 'bar',
23351     g_height: 250,
23352     g_width: 400,
23353     g_x: 50,
23354     g_y: 50,
23355     g_r: 30,
23356     opts:{
23357         //g_colors: this.colors,
23358         g_type: 'soft',
23359         g_gutter: '20%'
23360
23361     },
23362     title : false,
23363
23364     getAutoCreate : function(){
23365         
23366         var cfg = {
23367             tag: 'div',
23368             html : null
23369         };
23370         
23371         
23372         return  cfg;
23373     },
23374
23375     onRender : function(ct,position){
23376         
23377         
23378         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23379         
23380         if (typeof(Raphael) == 'undefined') {
23381             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23382             return;
23383         }
23384         
23385         this.raphael = Raphael(this.el.dom);
23386         
23387                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23388                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23389                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23390                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23391                 /*
23392                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23393                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23394                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23395                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23396                 
23397                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23398                 r.barchart(330, 10, 300, 220, data1);
23399                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23400                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23401                 */
23402                 
23403                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23404                 // r.barchart(30, 30, 560, 250,  xdata, {
23405                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23406                 //     axis : "0 0 1 1",
23407                 //     axisxlabels :  xdata
23408                 //     //yvalues : cols,
23409                    
23410                 // });
23411 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23412 //        
23413 //        this.load(null,xdata,{
23414 //                axis : "0 0 1 1",
23415 //                axisxlabels :  xdata
23416 //                });
23417
23418     },
23419
23420     load : function(graphtype,xdata,opts)
23421     {
23422         this.raphael.clear();
23423         if(!graphtype) {
23424             graphtype = this.graphtype;
23425         }
23426         if(!opts){
23427             opts = this.opts;
23428         }
23429         var r = this.raphael,
23430             fin = function () {
23431                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23432             },
23433             fout = function () {
23434                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23435             },
23436             pfin = function() {
23437                 this.sector.stop();
23438                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23439
23440                 if (this.label) {
23441                     this.label[0].stop();
23442                     this.label[0].attr({ r: 7.5 });
23443                     this.label[1].attr({ "font-weight": 800 });
23444                 }
23445             },
23446             pfout = function() {
23447                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23448
23449                 if (this.label) {
23450                     this.label[0].animate({ r: 5 }, 500, "bounce");
23451                     this.label[1].attr({ "font-weight": 400 });
23452                 }
23453             };
23454
23455         switch(graphtype){
23456             case 'bar':
23457                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23458                 break;
23459             case 'hbar':
23460                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23461                 break;
23462             case 'pie':
23463 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23464 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23465 //            
23466                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23467                 
23468                 break;
23469
23470         }
23471         
23472         if(this.title){
23473             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23474         }
23475         
23476     },
23477     
23478     setTitle: function(o)
23479     {
23480         this.title = o;
23481     },
23482     
23483     initEvents: function() {
23484         
23485         if(!this.href){
23486             this.el.on('click', this.onClick, this);
23487         }
23488     },
23489     
23490     onClick : function(e)
23491     {
23492         Roo.log('img onclick');
23493         this.fireEvent('click', this, e);
23494     }
23495    
23496 });
23497
23498  
23499 /*
23500  * - LGPL
23501  *
23502  * numberBox
23503  * 
23504  */
23505 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23506
23507 /**
23508  * @class Roo.bootstrap.dash.NumberBox
23509  * @extends Roo.bootstrap.Component
23510  * Bootstrap NumberBox class
23511  * @cfg {String} headline Box headline
23512  * @cfg {String} content Box content
23513  * @cfg {String} icon Box icon
23514  * @cfg {String} footer Footer text
23515  * @cfg {String} fhref Footer href
23516  * 
23517  * @constructor
23518  * Create a new NumberBox
23519  * @param {Object} config The config object
23520  */
23521
23522
23523 Roo.bootstrap.dash.NumberBox = function(config){
23524     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23525     
23526 };
23527
23528 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23529     
23530     headline : '',
23531     content : '',
23532     icon : '',
23533     footer : '',
23534     fhref : '',
23535     ficon : '',
23536     
23537     getAutoCreate : function(){
23538         
23539         var cfg = {
23540             tag : 'div',
23541             cls : 'small-box ',
23542             cn : [
23543                 {
23544                     tag : 'div',
23545                     cls : 'inner',
23546                     cn :[
23547                         {
23548                             tag : 'h3',
23549                             cls : 'roo-headline',
23550                             html : this.headline
23551                         },
23552                         {
23553                             tag : 'p',
23554                             cls : 'roo-content',
23555                             html : this.content
23556                         }
23557                     ]
23558                 }
23559             ]
23560         };
23561         
23562         if(this.icon){
23563             cfg.cn.push({
23564                 tag : 'div',
23565                 cls : 'icon',
23566                 cn :[
23567                     {
23568                         tag : 'i',
23569                         cls : 'ion ' + this.icon
23570                     }
23571                 ]
23572             });
23573         }
23574         
23575         if(this.footer){
23576             var footer = {
23577                 tag : 'a',
23578                 cls : 'small-box-footer',
23579                 href : this.fhref || '#',
23580                 html : this.footer
23581             };
23582             
23583             cfg.cn.push(footer);
23584             
23585         }
23586         
23587         return  cfg;
23588     },
23589
23590     onRender : function(ct,position){
23591         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23592
23593
23594        
23595                 
23596     },
23597
23598     setHeadline: function (value)
23599     {
23600         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23601     },
23602     
23603     setFooter: function (value, href)
23604     {
23605         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23606         
23607         if(href){
23608             this.el.select('a.small-box-footer',true).first().attr('href', href);
23609         }
23610         
23611     },
23612
23613     setContent: function (value)
23614     {
23615         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23616     },
23617
23618     initEvents: function() 
23619     {   
23620         
23621     }
23622     
23623 });
23624
23625  
23626 /*
23627  * - LGPL
23628  *
23629  * TabBox
23630  * 
23631  */
23632 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23633
23634 /**
23635  * @class Roo.bootstrap.dash.TabBox
23636  * @extends Roo.bootstrap.Component
23637  * Bootstrap TabBox class
23638  * @cfg {String} title Title of the TabBox
23639  * @cfg {String} icon Icon of the TabBox
23640  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23641  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23642  * 
23643  * @constructor
23644  * Create a new TabBox
23645  * @param {Object} config The config object
23646  */
23647
23648
23649 Roo.bootstrap.dash.TabBox = function(config){
23650     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23651     this.addEvents({
23652         // raw events
23653         /**
23654          * @event addpane
23655          * When a pane is added
23656          * @param {Roo.bootstrap.dash.TabPane} pane
23657          */
23658         "addpane" : true,
23659         /**
23660          * @event activatepane
23661          * When a pane is activated
23662          * @param {Roo.bootstrap.dash.TabPane} pane
23663          */
23664         "activatepane" : true
23665         
23666          
23667     });
23668     
23669     this.panes = [];
23670 };
23671
23672 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23673
23674     title : '',
23675     icon : false,
23676     showtabs : true,
23677     tabScrollable : false,
23678     
23679     getChildContainer : function()
23680     {
23681         return this.el.select('.tab-content', true).first();
23682     },
23683     
23684     getAutoCreate : function(){
23685         
23686         var header = {
23687             tag: 'li',
23688             cls: 'pull-left header',
23689             html: this.title,
23690             cn : []
23691         };
23692         
23693         if(this.icon){
23694             header.cn.push({
23695                 tag: 'i',
23696                 cls: 'fa ' + this.icon
23697             });
23698         }
23699         
23700         var h = {
23701             tag: 'ul',
23702             cls: 'nav nav-tabs pull-right',
23703             cn: [
23704                 header
23705             ]
23706         };
23707         
23708         if(this.tabScrollable){
23709             h = {
23710                 tag: 'div',
23711                 cls: 'tab-header',
23712                 cn: [
23713                     {
23714                         tag: 'ul',
23715                         cls: 'nav nav-tabs pull-right',
23716                         cn: [
23717                             header
23718                         ]
23719                     }
23720                 ]
23721             };
23722         }
23723         
23724         var cfg = {
23725             tag: 'div',
23726             cls: 'nav-tabs-custom',
23727             cn: [
23728                 h,
23729                 {
23730                     tag: 'div',
23731                     cls: 'tab-content no-padding',
23732                     cn: []
23733                 }
23734             ]
23735         };
23736
23737         return  cfg;
23738     },
23739     initEvents : function()
23740     {
23741         //Roo.log('add add pane handler');
23742         this.on('addpane', this.onAddPane, this);
23743     },
23744      /**
23745      * Updates the box title
23746      * @param {String} html to set the title to.
23747      */
23748     setTitle : function(value)
23749     {
23750         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23751     },
23752     onAddPane : function(pane)
23753     {
23754         this.panes.push(pane);
23755         //Roo.log('addpane');
23756         //Roo.log(pane);
23757         // tabs are rendere left to right..
23758         if(!this.showtabs){
23759             return;
23760         }
23761         
23762         var ctr = this.el.select('.nav-tabs', true).first();
23763          
23764          
23765         var existing = ctr.select('.nav-tab',true);
23766         var qty = existing.getCount();;
23767         
23768         
23769         var tab = ctr.createChild({
23770             tag : 'li',
23771             cls : 'nav-tab' + (qty ? '' : ' active'),
23772             cn : [
23773                 {
23774                     tag : 'a',
23775                     href:'#',
23776                     html : pane.title
23777                 }
23778             ]
23779         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23780         pane.tab = tab;
23781         
23782         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23783         if (!qty) {
23784             pane.el.addClass('active');
23785         }
23786         
23787                 
23788     },
23789     onTabClick : function(ev,un,ob,pane)
23790     {
23791         //Roo.log('tab - prev default');
23792         ev.preventDefault();
23793         
23794         
23795         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23796         pane.tab.addClass('active');
23797         //Roo.log(pane.title);
23798         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23799         // technically we should have a deactivate event.. but maybe add later.
23800         // and it should not de-activate the selected tab...
23801         this.fireEvent('activatepane', pane);
23802         pane.el.addClass('active');
23803         pane.fireEvent('activate');
23804         
23805         
23806     },
23807     
23808     getActivePane : function()
23809     {
23810         var r = false;
23811         Roo.each(this.panes, function(p) {
23812             if(p.el.hasClass('active')){
23813                 r = p;
23814                 return false;
23815             }
23816             
23817             return;
23818         });
23819         
23820         return r;
23821     }
23822     
23823     
23824 });
23825
23826  
23827 /*
23828  * - LGPL
23829  *
23830  * Tab pane
23831  * 
23832  */
23833 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23834 /**
23835  * @class Roo.bootstrap.TabPane
23836  * @extends Roo.bootstrap.Component
23837  * Bootstrap TabPane class
23838  * @cfg {Boolean} active (false | true) Default false
23839  * @cfg {String} title title of panel
23840
23841  * 
23842  * @constructor
23843  * Create a new TabPane
23844  * @param {Object} config The config object
23845  */
23846
23847 Roo.bootstrap.dash.TabPane = function(config){
23848     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23849     
23850     this.addEvents({
23851         // raw events
23852         /**
23853          * @event activate
23854          * When a pane is activated
23855          * @param {Roo.bootstrap.dash.TabPane} pane
23856          */
23857         "activate" : true
23858          
23859     });
23860 };
23861
23862 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23863     
23864     active : false,
23865     title : '',
23866     
23867     // the tabBox that this is attached to.
23868     tab : false,
23869      
23870     getAutoCreate : function() 
23871     {
23872         var cfg = {
23873             tag: 'div',
23874             cls: 'tab-pane'
23875         };
23876         
23877         if(this.active){
23878             cfg.cls += ' active';
23879         }
23880         
23881         return cfg;
23882     },
23883     initEvents  : function()
23884     {
23885         //Roo.log('trigger add pane handler');
23886         this.parent().fireEvent('addpane', this)
23887     },
23888     
23889      /**
23890      * Updates the tab title 
23891      * @param {String} html to set the title to.
23892      */
23893     setTitle: function(str)
23894     {
23895         if (!this.tab) {
23896             return;
23897         }
23898         this.title = str;
23899         this.tab.select('a', true).first().dom.innerHTML = str;
23900         
23901     }
23902     
23903     
23904     
23905 });
23906
23907  
23908
23909
23910  /*
23911  * - LGPL
23912  *
23913  * menu
23914  * 
23915  */
23916 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23917
23918 /**
23919  * @class Roo.bootstrap.menu.Menu
23920  * @extends Roo.bootstrap.Component
23921  * Bootstrap Menu class - container for Menu
23922  * @cfg {String} html Text of the menu
23923  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23924  * @cfg {String} icon Font awesome icon
23925  * @cfg {String} pos Menu align to (top | bottom) default bottom
23926  * 
23927  * 
23928  * @constructor
23929  * Create a new Menu
23930  * @param {Object} config The config object
23931  */
23932
23933
23934 Roo.bootstrap.menu.Menu = function(config){
23935     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23936     
23937     this.addEvents({
23938         /**
23939          * @event beforeshow
23940          * Fires before this menu is displayed
23941          * @param {Roo.bootstrap.menu.Menu} this
23942          */
23943         beforeshow : true,
23944         /**
23945          * @event beforehide
23946          * Fires before this menu is hidden
23947          * @param {Roo.bootstrap.menu.Menu} this
23948          */
23949         beforehide : true,
23950         /**
23951          * @event show
23952          * Fires after this menu is displayed
23953          * @param {Roo.bootstrap.menu.Menu} this
23954          */
23955         show : true,
23956         /**
23957          * @event hide
23958          * Fires after this menu is hidden
23959          * @param {Roo.bootstrap.menu.Menu} this
23960          */
23961         hide : true,
23962         /**
23963          * @event click
23964          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23965          * @param {Roo.bootstrap.menu.Menu} this
23966          * @param {Roo.EventObject} e
23967          */
23968         click : true
23969     });
23970     
23971 };
23972
23973 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23974     
23975     submenu : false,
23976     html : '',
23977     weight : 'default',
23978     icon : false,
23979     pos : 'bottom',
23980     
23981     
23982     getChildContainer : function() {
23983         if(this.isSubMenu){
23984             return this.el;
23985         }
23986         
23987         return this.el.select('ul.dropdown-menu', true).first();  
23988     },
23989     
23990     getAutoCreate : function()
23991     {
23992         var text = [
23993             {
23994                 tag : 'span',
23995                 cls : 'roo-menu-text',
23996                 html : this.html
23997             }
23998         ];
23999         
24000         if(this.icon){
24001             text.unshift({
24002                 tag : 'i',
24003                 cls : 'fa ' + this.icon
24004             })
24005         }
24006         
24007         
24008         var cfg = {
24009             tag : 'div',
24010             cls : 'btn-group',
24011             cn : [
24012                 {
24013                     tag : 'button',
24014                     cls : 'dropdown-button btn btn-' + this.weight,
24015                     cn : text
24016                 },
24017                 {
24018                     tag : 'button',
24019                     cls : 'dropdown-toggle btn btn-' + this.weight,
24020                     cn : [
24021                         {
24022                             tag : 'span',
24023                             cls : 'caret'
24024                         }
24025                     ]
24026                 },
24027                 {
24028                     tag : 'ul',
24029                     cls : 'dropdown-menu'
24030                 }
24031             ]
24032             
24033         };
24034         
24035         if(this.pos == 'top'){
24036             cfg.cls += ' dropup';
24037         }
24038         
24039         if(this.isSubMenu){
24040             cfg = {
24041                 tag : 'ul',
24042                 cls : 'dropdown-menu'
24043             }
24044         }
24045         
24046         return cfg;
24047     },
24048     
24049     onRender : function(ct, position)
24050     {
24051         this.isSubMenu = ct.hasClass('dropdown-submenu');
24052         
24053         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24054     },
24055     
24056     initEvents : function() 
24057     {
24058         if(this.isSubMenu){
24059             return;
24060         }
24061         
24062         this.hidden = true;
24063         
24064         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24065         this.triggerEl.on('click', this.onTriggerPress, this);
24066         
24067         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24068         this.buttonEl.on('click', this.onClick, this);
24069         
24070     },
24071     
24072     list : function()
24073     {
24074         if(this.isSubMenu){
24075             return this.el;
24076         }
24077         
24078         return this.el.select('ul.dropdown-menu', true).first();
24079     },
24080     
24081     onClick : function(e)
24082     {
24083         this.fireEvent("click", this, e);
24084     },
24085     
24086     onTriggerPress  : function(e)
24087     {   
24088         if (this.isVisible()) {
24089             this.hide();
24090         } else {
24091             this.show();
24092         }
24093     },
24094     
24095     isVisible : function(){
24096         return !this.hidden;
24097     },
24098     
24099     show : function()
24100     {
24101         this.fireEvent("beforeshow", this);
24102         
24103         this.hidden = false;
24104         this.el.addClass('open');
24105         
24106         Roo.get(document).on("mouseup", this.onMouseUp, this);
24107         
24108         this.fireEvent("show", this);
24109         
24110         
24111     },
24112     
24113     hide : function()
24114     {
24115         this.fireEvent("beforehide", this);
24116         
24117         this.hidden = true;
24118         this.el.removeClass('open');
24119         
24120         Roo.get(document).un("mouseup", this.onMouseUp);
24121         
24122         this.fireEvent("hide", this);
24123     },
24124     
24125     onMouseUp : function()
24126     {
24127         this.hide();
24128     }
24129     
24130 });
24131
24132  
24133  /*
24134  * - LGPL
24135  *
24136  * menu item
24137  * 
24138  */
24139 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24140
24141 /**
24142  * @class Roo.bootstrap.menu.Item
24143  * @extends Roo.bootstrap.Component
24144  * Bootstrap MenuItem class
24145  * @cfg {Boolean} submenu (true | false) default false
24146  * @cfg {String} html text of the item
24147  * @cfg {String} href the link
24148  * @cfg {Boolean} disable (true | false) default false
24149  * @cfg {Boolean} preventDefault (true | false) default true
24150  * @cfg {String} icon Font awesome icon
24151  * @cfg {String} pos Submenu align to (left | right) default right 
24152  * 
24153  * 
24154  * @constructor
24155  * Create a new Item
24156  * @param {Object} config The config object
24157  */
24158
24159
24160 Roo.bootstrap.menu.Item = function(config){
24161     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24162     this.addEvents({
24163         /**
24164          * @event mouseover
24165          * Fires when the mouse is hovering over this menu
24166          * @param {Roo.bootstrap.menu.Item} this
24167          * @param {Roo.EventObject} e
24168          */
24169         mouseover : true,
24170         /**
24171          * @event mouseout
24172          * Fires when the mouse exits this menu
24173          * @param {Roo.bootstrap.menu.Item} this
24174          * @param {Roo.EventObject} e
24175          */
24176         mouseout : true,
24177         // raw events
24178         /**
24179          * @event click
24180          * The raw click event for the entire grid.
24181          * @param {Roo.EventObject} e
24182          */
24183         click : true
24184     });
24185 };
24186
24187 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24188     
24189     submenu : false,
24190     href : '',
24191     html : '',
24192     preventDefault: true,
24193     disable : false,
24194     icon : false,
24195     pos : 'right',
24196     
24197     getAutoCreate : function()
24198     {
24199         var text = [
24200             {
24201                 tag : 'span',
24202                 cls : 'roo-menu-item-text',
24203                 html : this.html
24204             }
24205         ];
24206         
24207         if(this.icon){
24208             text.unshift({
24209                 tag : 'i',
24210                 cls : 'fa ' + this.icon
24211             })
24212         }
24213         
24214         var cfg = {
24215             tag : 'li',
24216             cn : [
24217                 {
24218                     tag : 'a',
24219                     href : this.href || '#',
24220                     cn : text
24221                 }
24222             ]
24223         };
24224         
24225         if(this.disable){
24226             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24227         }
24228         
24229         if(this.submenu){
24230             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24231             
24232             if(this.pos == 'left'){
24233                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24234             }
24235         }
24236         
24237         return cfg;
24238     },
24239     
24240     initEvents : function() 
24241     {
24242         this.el.on('mouseover', this.onMouseOver, this);
24243         this.el.on('mouseout', this.onMouseOut, this);
24244         
24245         this.el.select('a', true).first().on('click', this.onClick, this);
24246         
24247     },
24248     
24249     onClick : function(e)
24250     {
24251         if(this.preventDefault){
24252             e.preventDefault();
24253         }
24254         
24255         this.fireEvent("click", this, e);
24256     },
24257     
24258     onMouseOver : function(e)
24259     {
24260         if(this.submenu && this.pos == 'left'){
24261             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24262         }
24263         
24264         this.fireEvent("mouseover", this, e);
24265     },
24266     
24267     onMouseOut : function(e)
24268     {
24269         this.fireEvent("mouseout", this, e);
24270     }
24271 });
24272
24273  
24274
24275  /*
24276  * - LGPL
24277  *
24278  * menu separator
24279  * 
24280  */
24281 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24282
24283 /**
24284  * @class Roo.bootstrap.menu.Separator
24285  * @extends Roo.bootstrap.Component
24286  * Bootstrap Separator class
24287  * 
24288  * @constructor
24289  * Create a new Separator
24290  * @param {Object} config The config object
24291  */
24292
24293
24294 Roo.bootstrap.menu.Separator = function(config){
24295     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24296 };
24297
24298 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24299     
24300     getAutoCreate : function(){
24301         var cfg = {
24302             tag : 'li',
24303             cls: 'divider'
24304         };
24305         
24306         return cfg;
24307     }
24308    
24309 });
24310
24311  
24312
24313  /*
24314  * - LGPL
24315  *
24316  * Tooltip
24317  * 
24318  */
24319
24320 /**
24321  * @class Roo.bootstrap.Tooltip
24322  * Bootstrap Tooltip class
24323  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24324  * to determine which dom element triggers the tooltip.
24325  * 
24326  * It needs to add support for additional attributes like tooltip-position
24327  * 
24328  * @constructor
24329  * Create a new Toolti
24330  * @param {Object} config The config object
24331  */
24332
24333 Roo.bootstrap.Tooltip = function(config){
24334     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24335 };
24336
24337 Roo.apply(Roo.bootstrap.Tooltip, {
24338     /**
24339      * @function init initialize tooltip monitoring.
24340      * @static
24341      */
24342     currentEl : false,
24343     currentTip : false,
24344     currentRegion : false,
24345     
24346     //  init : delay?
24347     
24348     init : function()
24349     {
24350         Roo.get(document).on('mouseover', this.enter ,this);
24351         Roo.get(document).on('mouseout', this.leave, this);
24352          
24353         
24354         this.currentTip = new Roo.bootstrap.Tooltip();
24355     },
24356     
24357     enter : function(ev)
24358     {
24359         var dom = ev.getTarget();
24360         
24361         //Roo.log(['enter',dom]);
24362         var el = Roo.fly(dom);
24363         if (this.currentEl) {
24364             //Roo.log(dom);
24365             //Roo.log(this.currentEl);
24366             //Roo.log(this.currentEl.contains(dom));
24367             if (this.currentEl == el) {
24368                 return;
24369             }
24370             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24371                 return;
24372             }
24373
24374         }
24375         
24376         if (this.currentTip.el) {
24377             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24378         }    
24379         //Roo.log(ev);
24380         
24381         if(!el || el.dom == document){
24382             return;
24383         }
24384         
24385         var bindEl = el;
24386         
24387         // you can not look for children, as if el is the body.. then everythign is the child..
24388         if (!el.attr('tooltip')) { //
24389             if (!el.select("[tooltip]").elements.length) {
24390                 return;
24391             }
24392             // is the mouse over this child...?
24393             bindEl = el.select("[tooltip]").first();
24394             var xy = ev.getXY();
24395             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24396                 //Roo.log("not in region.");
24397                 return;
24398             }
24399             //Roo.log("child element over..");
24400             
24401         }
24402         this.currentEl = bindEl;
24403         this.currentTip.bind(bindEl);
24404         this.currentRegion = Roo.lib.Region.getRegion(dom);
24405         this.currentTip.enter();
24406         
24407     },
24408     leave : function(ev)
24409     {
24410         var dom = ev.getTarget();
24411         //Roo.log(['leave',dom]);
24412         if (!this.currentEl) {
24413             return;
24414         }
24415         
24416         
24417         if (dom != this.currentEl.dom) {
24418             return;
24419         }
24420         var xy = ev.getXY();
24421         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24422             return;
24423         }
24424         // only activate leave if mouse cursor is outside... bounding box..
24425         
24426         
24427         
24428         
24429         if (this.currentTip) {
24430             this.currentTip.leave();
24431         }
24432         //Roo.log('clear currentEl');
24433         this.currentEl = false;
24434         
24435         
24436     },
24437     alignment : {
24438         'left' : ['r-l', [-2,0], 'right'],
24439         'right' : ['l-r', [2,0], 'left'],
24440         'bottom' : ['t-b', [0,2], 'top'],
24441         'top' : [ 'b-t', [0,-2], 'bottom']
24442     }
24443     
24444 });
24445
24446
24447 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24448     
24449     
24450     bindEl : false,
24451     
24452     delay : null, // can be { show : 300 , hide: 500}
24453     
24454     timeout : null,
24455     
24456     hoverState : null, //???
24457     
24458     placement : 'bottom', 
24459     
24460     getAutoCreate : function(){
24461     
24462         var cfg = {
24463            cls : 'tooltip',
24464            role : 'tooltip',
24465            cn : [
24466                 {
24467                     cls : 'tooltip-arrow'
24468                 },
24469                 {
24470                     cls : 'tooltip-inner'
24471                 }
24472            ]
24473         };
24474         
24475         return cfg;
24476     },
24477     bind : function(el)
24478     {
24479         this.bindEl = el;
24480     },
24481       
24482     
24483     enter : function () {
24484        
24485         if (this.timeout != null) {
24486             clearTimeout(this.timeout);
24487         }
24488         
24489         this.hoverState = 'in';
24490          //Roo.log("enter - show");
24491         if (!this.delay || !this.delay.show) {
24492             this.show();
24493             return;
24494         }
24495         var _t = this;
24496         this.timeout = setTimeout(function () {
24497             if (_t.hoverState == 'in') {
24498                 _t.show();
24499             }
24500         }, this.delay.show);
24501     },
24502     leave : function()
24503     {
24504         clearTimeout(this.timeout);
24505     
24506         this.hoverState = 'out';
24507          if (!this.delay || !this.delay.hide) {
24508             this.hide();
24509             return;
24510         }
24511        
24512         var _t = this;
24513         this.timeout = setTimeout(function () {
24514             //Roo.log("leave - timeout");
24515             
24516             if (_t.hoverState == 'out') {
24517                 _t.hide();
24518                 Roo.bootstrap.Tooltip.currentEl = false;
24519             }
24520         }, delay);
24521     },
24522     
24523     show : function ()
24524     {
24525         if (!this.el) {
24526             this.render(document.body);
24527         }
24528         // set content.
24529         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24530         
24531         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24532         
24533         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24534         
24535         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24536         
24537         var placement = typeof this.placement == 'function' ?
24538             this.placement.call(this, this.el, on_el) :
24539             this.placement;
24540             
24541         var autoToken = /\s?auto?\s?/i;
24542         var autoPlace = autoToken.test(placement);
24543         if (autoPlace) {
24544             placement = placement.replace(autoToken, '') || 'top';
24545         }
24546         
24547         //this.el.detach()
24548         //this.el.setXY([0,0]);
24549         this.el.show();
24550         //this.el.dom.style.display='block';
24551         
24552         //this.el.appendTo(on_el);
24553         
24554         var p = this.getPosition();
24555         var box = this.el.getBox();
24556         
24557         if (autoPlace) {
24558             // fixme..
24559         }
24560         
24561         var align = Roo.bootstrap.Tooltip.alignment[placement];
24562         
24563         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24564         
24565         if(placement == 'top' || placement == 'bottom'){
24566             if(xy[0] < 0){
24567                 placement = 'right';
24568             }
24569             
24570             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24571                 placement = 'left';
24572             }
24573             
24574             var scroll = Roo.select('body', true).first().getScroll();
24575             
24576             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24577                 placement = 'top';
24578             }
24579             
24580         }
24581         
24582         align = Roo.bootstrap.Tooltip.alignment[placement];
24583         
24584         this.el.alignTo(this.bindEl, align[0],align[1]);
24585         //var arrow = this.el.select('.arrow',true).first();
24586         //arrow.set(align[2], 
24587         
24588         this.el.addClass(placement);
24589         
24590         this.el.addClass('in fade');
24591         
24592         this.hoverState = null;
24593         
24594         if (this.el.hasClass('fade')) {
24595             // fade it?
24596         }
24597         
24598     },
24599     hide : function()
24600     {
24601          
24602         if (!this.el) {
24603             return;
24604         }
24605         //this.el.setXY([0,0]);
24606         this.el.removeClass('in');
24607         //this.el.hide();
24608         
24609     }
24610     
24611 });
24612  
24613
24614  /*
24615  * - LGPL
24616  *
24617  * Location Picker
24618  * 
24619  */
24620
24621 /**
24622  * @class Roo.bootstrap.LocationPicker
24623  * @extends Roo.bootstrap.Component
24624  * Bootstrap LocationPicker class
24625  * @cfg {Number} latitude Position when init default 0
24626  * @cfg {Number} longitude Position when init default 0
24627  * @cfg {Number} zoom default 15
24628  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24629  * @cfg {Boolean} mapTypeControl default false
24630  * @cfg {Boolean} disableDoubleClickZoom default false
24631  * @cfg {Boolean} scrollwheel default true
24632  * @cfg {Boolean} streetViewControl default false
24633  * @cfg {Number} radius default 0
24634  * @cfg {String} locationName
24635  * @cfg {Boolean} draggable default true
24636  * @cfg {Boolean} enableAutocomplete default false
24637  * @cfg {Boolean} enableReverseGeocode default true
24638  * @cfg {String} markerTitle
24639  * 
24640  * @constructor
24641  * Create a new LocationPicker
24642  * @param {Object} config The config object
24643  */
24644
24645
24646 Roo.bootstrap.LocationPicker = function(config){
24647     
24648     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24649     
24650     this.addEvents({
24651         /**
24652          * @event initial
24653          * Fires when the picker initialized.
24654          * @param {Roo.bootstrap.LocationPicker} this
24655          * @param {Google Location} location
24656          */
24657         initial : true,
24658         /**
24659          * @event positionchanged
24660          * Fires when the picker position changed.
24661          * @param {Roo.bootstrap.LocationPicker} this
24662          * @param {Google Location} location
24663          */
24664         positionchanged : true,
24665         /**
24666          * @event resize
24667          * Fires when the map resize.
24668          * @param {Roo.bootstrap.LocationPicker} this
24669          */
24670         resize : true,
24671         /**
24672          * @event show
24673          * Fires when the map show.
24674          * @param {Roo.bootstrap.LocationPicker} this
24675          */
24676         show : true,
24677         /**
24678          * @event hide
24679          * Fires when the map hide.
24680          * @param {Roo.bootstrap.LocationPicker} this
24681          */
24682         hide : true,
24683         /**
24684          * @event mapClick
24685          * Fires when click the map.
24686          * @param {Roo.bootstrap.LocationPicker} this
24687          * @param {Map event} e
24688          */
24689         mapClick : true,
24690         /**
24691          * @event mapRightClick
24692          * Fires when right click the map.
24693          * @param {Roo.bootstrap.LocationPicker} this
24694          * @param {Map event} e
24695          */
24696         mapRightClick : true,
24697         /**
24698          * @event markerClick
24699          * Fires when click the marker.
24700          * @param {Roo.bootstrap.LocationPicker} this
24701          * @param {Map event} e
24702          */
24703         markerClick : true,
24704         /**
24705          * @event markerRightClick
24706          * Fires when right click the marker.
24707          * @param {Roo.bootstrap.LocationPicker} this
24708          * @param {Map event} e
24709          */
24710         markerRightClick : true,
24711         /**
24712          * @event OverlayViewDraw
24713          * Fires when OverlayView Draw
24714          * @param {Roo.bootstrap.LocationPicker} this
24715          */
24716         OverlayViewDraw : true,
24717         /**
24718          * @event OverlayViewOnAdd
24719          * Fires when OverlayView Draw
24720          * @param {Roo.bootstrap.LocationPicker} this
24721          */
24722         OverlayViewOnAdd : true,
24723         /**
24724          * @event OverlayViewOnRemove
24725          * Fires when OverlayView Draw
24726          * @param {Roo.bootstrap.LocationPicker} this
24727          */
24728         OverlayViewOnRemove : true,
24729         /**
24730          * @event OverlayViewShow
24731          * Fires when OverlayView Draw
24732          * @param {Roo.bootstrap.LocationPicker} this
24733          * @param {Pixel} cpx
24734          */
24735         OverlayViewShow : true,
24736         /**
24737          * @event OverlayViewHide
24738          * Fires when OverlayView Draw
24739          * @param {Roo.bootstrap.LocationPicker} this
24740          */
24741         OverlayViewHide : true,
24742         /**
24743          * @event loadexception
24744          * Fires when load google lib failed.
24745          * @param {Roo.bootstrap.LocationPicker} this
24746          */
24747         loadexception : true
24748     });
24749         
24750 };
24751
24752 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24753     
24754     gMapContext: false,
24755     
24756     latitude: 0,
24757     longitude: 0,
24758     zoom: 15,
24759     mapTypeId: false,
24760     mapTypeControl: false,
24761     disableDoubleClickZoom: false,
24762     scrollwheel: true,
24763     streetViewControl: false,
24764     radius: 0,
24765     locationName: '',
24766     draggable: true,
24767     enableAutocomplete: false,
24768     enableReverseGeocode: true,
24769     markerTitle: '',
24770     
24771     getAutoCreate: function()
24772     {
24773
24774         var cfg = {
24775             tag: 'div',
24776             cls: 'roo-location-picker'
24777         };
24778         
24779         return cfg
24780     },
24781     
24782     initEvents: function(ct, position)
24783     {       
24784         if(!this.el.getWidth() || this.isApplied()){
24785             return;
24786         }
24787         
24788         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24789         
24790         this.initial();
24791     },
24792     
24793     initial: function()
24794     {
24795         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24796             this.fireEvent('loadexception', this);
24797             return;
24798         }
24799         
24800         if(!this.mapTypeId){
24801             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24802         }
24803         
24804         this.gMapContext = this.GMapContext();
24805         
24806         this.initOverlayView();
24807         
24808         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24809         
24810         var _this = this;
24811                 
24812         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24813             _this.setPosition(_this.gMapContext.marker.position);
24814         });
24815         
24816         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24817             _this.fireEvent('mapClick', this, event);
24818             
24819         });
24820
24821         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24822             _this.fireEvent('mapRightClick', this, event);
24823             
24824         });
24825         
24826         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24827             _this.fireEvent('markerClick', this, event);
24828             
24829         });
24830
24831         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24832             _this.fireEvent('markerRightClick', this, event);
24833             
24834         });
24835         
24836         this.setPosition(this.gMapContext.location);
24837         
24838         this.fireEvent('initial', this, this.gMapContext.location);
24839     },
24840     
24841     initOverlayView: function()
24842     {
24843         var _this = this;
24844         
24845         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24846             
24847             draw: function()
24848             {
24849                 _this.fireEvent('OverlayViewDraw', _this);
24850             },
24851             
24852             onAdd: function()
24853             {
24854                 _this.fireEvent('OverlayViewOnAdd', _this);
24855             },
24856             
24857             onRemove: function()
24858             {
24859                 _this.fireEvent('OverlayViewOnRemove', _this);
24860             },
24861             
24862             show: function(cpx)
24863             {
24864                 _this.fireEvent('OverlayViewShow', _this, cpx);
24865             },
24866             
24867             hide: function()
24868             {
24869                 _this.fireEvent('OverlayViewHide', _this);
24870             }
24871             
24872         });
24873     },
24874     
24875     fromLatLngToContainerPixel: function(event)
24876     {
24877         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24878     },
24879     
24880     isApplied: function() 
24881     {
24882         return this.getGmapContext() == false ? false : true;
24883     },
24884     
24885     getGmapContext: function() 
24886     {
24887         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24888     },
24889     
24890     GMapContext: function() 
24891     {
24892         var position = new google.maps.LatLng(this.latitude, this.longitude);
24893         
24894         var _map = new google.maps.Map(this.el.dom, {
24895             center: position,
24896             zoom: this.zoom,
24897             mapTypeId: this.mapTypeId,
24898             mapTypeControl: this.mapTypeControl,
24899             disableDoubleClickZoom: this.disableDoubleClickZoom,
24900             scrollwheel: this.scrollwheel,
24901             streetViewControl: this.streetViewControl,
24902             locationName: this.locationName,
24903             draggable: this.draggable,
24904             enableAutocomplete: this.enableAutocomplete,
24905             enableReverseGeocode: this.enableReverseGeocode
24906         });
24907         
24908         var _marker = new google.maps.Marker({
24909             position: position,
24910             map: _map,
24911             title: this.markerTitle,
24912             draggable: this.draggable
24913         });
24914         
24915         return {
24916             map: _map,
24917             marker: _marker,
24918             circle: null,
24919             location: position,
24920             radius: this.radius,
24921             locationName: this.locationName,
24922             addressComponents: {
24923                 formatted_address: null,
24924                 addressLine1: null,
24925                 addressLine2: null,
24926                 streetName: null,
24927                 streetNumber: null,
24928                 city: null,
24929                 district: null,
24930                 state: null,
24931                 stateOrProvince: null
24932             },
24933             settings: this,
24934             domContainer: this.el.dom,
24935             geodecoder: new google.maps.Geocoder()
24936         };
24937     },
24938     
24939     drawCircle: function(center, radius, options) 
24940     {
24941         if (this.gMapContext.circle != null) {
24942             this.gMapContext.circle.setMap(null);
24943         }
24944         if (radius > 0) {
24945             radius *= 1;
24946             options = Roo.apply({}, options, {
24947                 strokeColor: "#0000FF",
24948                 strokeOpacity: .35,
24949                 strokeWeight: 2,
24950                 fillColor: "#0000FF",
24951                 fillOpacity: .2
24952             });
24953             
24954             options.map = this.gMapContext.map;
24955             options.radius = radius;
24956             options.center = center;
24957             this.gMapContext.circle = new google.maps.Circle(options);
24958             return this.gMapContext.circle;
24959         }
24960         
24961         return null;
24962     },
24963     
24964     setPosition: function(location) 
24965     {
24966         this.gMapContext.location = location;
24967         this.gMapContext.marker.setPosition(location);
24968         this.gMapContext.map.panTo(location);
24969         this.drawCircle(location, this.gMapContext.radius, {});
24970         
24971         var _this = this;
24972         
24973         if (this.gMapContext.settings.enableReverseGeocode) {
24974             this.gMapContext.geodecoder.geocode({
24975                 latLng: this.gMapContext.location
24976             }, function(results, status) {
24977                 
24978                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24979                     _this.gMapContext.locationName = results[0].formatted_address;
24980                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24981                     
24982                     _this.fireEvent('positionchanged', this, location);
24983                 }
24984             });
24985             
24986             return;
24987         }
24988         
24989         this.fireEvent('positionchanged', this, location);
24990     },
24991     
24992     resize: function()
24993     {
24994         google.maps.event.trigger(this.gMapContext.map, "resize");
24995         
24996         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24997         
24998         this.fireEvent('resize', this);
24999     },
25000     
25001     setPositionByLatLng: function(latitude, longitude)
25002     {
25003         this.setPosition(new google.maps.LatLng(latitude, longitude));
25004     },
25005     
25006     getCurrentPosition: function() 
25007     {
25008         return {
25009             latitude: this.gMapContext.location.lat(),
25010             longitude: this.gMapContext.location.lng()
25011         };
25012     },
25013     
25014     getAddressName: function() 
25015     {
25016         return this.gMapContext.locationName;
25017     },
25018     
25019     getAddressComponents: function() 
25020     {
25021         return this.gMapContext.addressComponents;
25022     },
25023     
25024     address_component_from_google_geocode: function(address_components) 
25025     {
25026         var result = {};
25027         
25028         for (var i = 0; i < address_components.length; i++) {
25029             var component = address_components[i];
25030             if (component.types.indexOf("postal_code") >= 0) {
25031                 result.postalCode = component.short_name;
25032             } else if (component.types.indexOf("street_number") >= 0) {
25033                 result.streetNumber = component.short_name;
25034             } else if (component.types.indexOf("route") >= 0) {
25035                 result.streetName = component.short_name;
25036             } else if (component.types.indexOf("neighborhood") >= 0) {
25037                 result.city = component.short_name;
25038             } else if (component.types.indexOf("locality") >= 0) {
25039                 result.city = component.short_name;
25040             } else if (component.types.indexOf("sublocality") >= 0) {
25041                 result.district = component.short_name;
25042             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25043                 result.stateOrProvince = component.short_name;
25044             } else if (component.types.indexOf("country") >= 0) {
25045                 result.country = component.short_name;
25046             }
25047         }
25048         
25049         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25050         result.addressLine2 = "";
25051         return result;
25052     },
25053     
25054     setZoomLevel: function(zoom)
25055     {
25056         this.gMapContext.map.setZoom(zoom);
25057     },
25058     
25059     show: function()
25060     {
25061         if(!this.el){
25062             return;
25063         }
25064         
25065         this.el.show();
25066         
25067         this.resize();
25068         
25069         this.fireEvent('show', this);
25070     },
25071     
25072     hide: function()
25073     {
25074         if(!this.el){
25075             return;
25076         }
25077         
25078         this.el.hide();
25079         
25080         this.fireEvent('hide', this);
25081     }
25082     
25083 });
25084
25085 Roo.apply(Roo.bootstrap.LocationPicker, {
25086     
25087     OverlayView : function(map, options)
25088     {
25089         options = options || {};
25090         
25091         this.setMap(map);
25092     }
25093     
25094     
25095 });/*
25096  * - LGPL
25097  *
25098  * Alert
25099  * 
25100  */
25101
25102 /**
25103  * @class Roo.bootstrap.Alert
25104  * @extends Roo.bootstrap.Component
25105  * Bootstrap Alert class
25106  * @cfg {String} title The title of alert
25107  * @cfg {String} html The content of alert
25108  * @cfg {String} weight (  success | info | warning | danger )
25109  * @cfg {String} faicon font-awesomeicon
25110  * 
25111  * @constructor
25112  * Create a new alert
25113  * @param {Object} config The config object
25114  */
25115
25116
25117 Roo.bootstrap.Alert = function(config){
25118     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25119     
25120 };
25121
25122 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25123     
25124     title: '',
25125     html: '',
25126     weight: false,
25127     faicon: false,
25128     
25129     getAutoCreate : function()
25130     {
25131         
25132         var cfg = {
25133             tag : 'div',
25134             cls : 'alert',
25135             cn : [
25136                 {
25137                     tag : 'i',
25138                     cls : 'roo-alert-icon'
25139                     
25140                 },
25141                 {
25142                     tag : 'b',
25143                     cls : 'roo-alert-title',
25144                     html : this.title
25145                 },
25146                 {
25147                     tag : 'span',
25148                     cls : 'roo-alert-text',
25149                     html : this.html
25150                 }
25151             ]
25152         };
25153         
25154         if(this.faicon){
25155             cfg.cn[0].cls += ' fa ' + this.faicon;
25156         }
25157         
25158         if(this.weight){
25159             cfg.cls += ' alert-' + this.weight;
25160         }
25161         
25162         return cfg;
25163     },
25164     
25165     initEvents: function() 
25166     {
25167         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25168     },
25169     
25170     setTitle : function(str)
25171     {
25172         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25173     },
25174     
25175     setText : function(str)
25176     {
25177         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25178     },
25179     
25180     setWeight : function(weight)
25181     {
25182         if(this.weight){
25183             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25184         }
25185         
25186         this.weight = weight;
25187         
25188         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25189     },
25190     
25191     setIcon : function(icon)
25192     {
25193         if(this.faicon){
25194             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25195         }
25196         
25197         this.faicon = icon;
25198         
25199         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25200     },
25201     
25202     hide: function() 
25203     {
25204         this.el.hide();   
25205     },
25206     
25207     show: function() 
25208     {  
25209         this.el.show();   
25210     }
25211     
25212 });
25213
25214  
25215 /*
25216 * Licence: LGPL
25217 */
25218
25219 /**
25220  * @class Roo.bootstrap.UploadCropbox
25221  * @extends Roo.bootstrap.Component
25222  * Bootstrap UploadCropbox class
25223  * @cfg {String} emptyText show when image has been loaded
25224  * @cfg {String} rotateNotify show when image too small to rotate
25225  * @cfg {Number} errorTimeout default 3000
25226  * @cfg {Number} minWidth default 300
25227  * @cfg {Number} minHeight default 300
25228  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25229  * @cfg {Boolean} isDocument (true|false) default false
25230  * @cfg {String} url action url
25231  * @cfg {String} paramName default 'imageUpload'
25232  * @cfg {String} method default POST
25233  * @cfg {Boolean} loadMask (true|false) default true
25234  * @cfg {Boolean} loadingText default 'Loading...'
25235  * 
25236  * @constructor
25237  * Create a new UploadCropbox
25238  * @param {Object} config The config object
25239  */
25240
25241 Roo.bootstrap.UploadCropbox = function(config){
25242     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25243     
25244     this.addEvents({
25245         /**
25246          * @event beforeselectfile
25247          * Fire before select file
25248          * @param {Roo.bootstrap.UploadCropbox} this
25249          */
25250         "beforeselectfile" : true,
25251         /**
25252          * @event initial
25253          * Fire after initEvent
25254          * @param {Roo.bootstrap.UploadCropbox} this
25255          */
25256         "initial" : true,
25257         /**
25258          * @event crop
25259          * Fire after initEvent
25260          * @param {Roo.bootstrap.UploadCropbox} this
25261          * @param {String} data
25262          */
25263         "crop" : true,
25264         /**
25265          * @event prepare
25266          * Fire when preparing the file data
25267          * @param {Roo.bootstrap.UploadCropbox} this
25268          * @param {Object} file
25269          */
25270         "prepare" : true,
25271         /**
25272          * @event exception
25273          * Fire when get exception
25274          * @param {Roo.bootstrap.UploadCropbox} this
25275          * @param {XMLHttpRequest} xhr
25276          */
25277         "exception" : true,
25278         /**
25279          * @event beforeloadcanvas
25280          * Fire before load the canvas
25281          * @param {Roo.bootstrap.UploadCropbox} this
25282          * @param {String} src
25283          */
25284         "beforeloadcanvas" : true,
25285         /**
25286          * @event trash
25287          * Fire when trash image
25288          * @param {Roo.bootstrap.UploadCropbox} this
25289          */
25290         "trash" : true,
25291         /**
25292          * @event download
25293          * Fire when download the image
25294          * @param {Roo.bootstrap.UploadCropbox} this
25295          */
25296         "download" : true,
25297         /**
25298          * @event footerbuttonclick
25299          * Fire when footerbuttonclick
25300          * @param {Roo.bootstrap.UploadCropbox} this
25301          * @param {String} type
25302          */
25303         "footerbuttonclick" : true,
25304         /**
25305          * @event resize
25306          * Fire when resize
25307          * @param {Roo.bootstrap.UploadCropbox} this
25308          */
25309         "resize" : true,
25310         /**
25311          * @event rotate
25312          * Fire when rotate the image
25313          * @param {Roo.bootstrap.UploadCropbox} this
25314          * @param {String} pos
25315          */
25316         "rotate" : true,
25317         /**
25318          * @event inspect
25319          * Fire when inspect the file
25320          * @param {Roo.bootstrap.UploadCropbox} this
25321          * @param {Object} file
25322          */
25323         "inspect" : true,
25324         /**
25325          * @event upload
25326          * Fire when xhr upload the file
25327          * @param {Roo.bootstrap.UploadCropbox} this
25328          * @param {Object} data
25329          */
25330         "upload" : true,
25331         /**
25332          * @event arrange
25333          * Fire when arrange the file data
25334          * @param {Roo.bootstrap.UploadCropbox} this
25335          * @param {Object} formData
25336          */
25337         "arrange" : true
25338     });
25339     
25340     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25341 };
25342
25343 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25344     
25345     emptyText : 'Click to upload image',
25346     rotateNotify : 'Image is too small to rotate',
25347     errorTimeout : 3000,
25348     scale : 0,
25349     baseScale : 1,
25350     rotate : 0,
25351     dragable : false,
25352     pinching : false,
25353     mouseX : 0,
25354     mouseY : 0,
25355     cropData : false,
25356     minWidth : 300,
25357     minHeight : 300,
25358     file : false,
25359     exif : {},
25360     baseRotate : 1,
25361     cropType : 'image/jpeg',
25362     buttons : false,
25363     canvasLoaded : false,
25364     isDocument : false,
25365     method : 'POST',
25366     paramName : 'imageUpload',
25367     loadMask : true,
25368     loadingText : 'Loading...',
25369     maskEl : false,
25370     
25371     getAutoCreate : function()
25372     {
25373         var cfg = {
25374             tag : 'div',
25375             cls : 'roo-upload-cropbox',
25376             cn : [
25377                 {
25378                     tag : 'input',
25379                     cls : 'roo-upload-cropbox-selector',
25380                     type : 'file'
25381                 },
25382                 {
25383                     tag : 'div',
25384                     cls : 'roo-upload-cropbox-body',
25385                     style : 'cursor:pointer',
25386                     cn : [
25387                         {
25388                             tag : 'div',
25389                             cls : 'roo-upload-cropbox-preview'
25390                         },
25391                         {
25392                             tag : 'div',
25393                             cls : 'roo-upload-cropbox-thumb'
25394                         },
25395                         {
25396                             tag : 'div',
25397                             cls : 'roo-upload-cropbox-empty-notify',
25398                             html : this.emptyText
25399                         },
25400                         {
25401                             tag : 'div',
25402                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25403                             html : this.rotateNotify
25404                         }
25405                     ]
25406                 },
25407                 {
25408                     tag : 'div',
25409                     cls : 'roo-upload-cropbox-footer',
25410                     cn : {
25411                         tag : 'div',
25412                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25413                         cn : []
25414                     }
25415                 }
25416             ]
25417         };
25418         
25419         return cfg;
25420     },
25421     
25422     onRender : function(ct, position)
25423     {
25424         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25425         
25426         if (this.buttons.length) {
25427             
25428             Roo.each(this.buttons, function(bb) {
25429                 
25430                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25431                 
25432                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25433                 
25434             }, this);
25435         }
25436         
25437         if(this.loadMask){
25438             this.maskEl = this.el;
25439         }
25440     },
25441     
25442     initEvents : function()
25443     {
25444         this.urlAPI = (window.createObjectURL && window) || 
25445                                 (window.URL && URL.revokeObjectURL && URL) || 
25446                                 (window.webkitURL && webkitURL);
25447                         
25448         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25449         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25450         
25451         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25452         this.selectorEl.hide();
25453         
25454         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25455         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25456         
25457         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25458         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25459         this.thumbEl.hide();
25460         
25461         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25462         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25463         
25464         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25465         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25466         this.errorEl.hide();
25467         
25468         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25469         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25470         this.footerEl.hide();
25471         
25472         this.setThumbBoxSize();
25473         
25474         this.bind();
25475         
25476         this.resize();
25477         
25478         this.fireEvent('initial', this);
25479     },
25480
25481     bind : function()
25482     {
25483         var _this = this;
25484         
25485         window.addEventListener("resize", function() { _this.resize(); } );
25486         
25487         this.bodyEl.on('click', this.beforeSelectFile, this);
25488         
25489         if(Roo.isTouch){
25490             this.bodyEl.on('touchstart', this.onTouchStart, this);
25491             this.bodyEl.on('touchmove', this.onTouchMove, this);
25492             this.bodyEl.on('touchend', this.onTouchEnd, this);
25493         }
25494         
25495         if(!Roo.isTouch){
25496             this.bodyEl.on('mousedown', this.onMouseDown, this);
25497             this.bodyEl.on('mousemove', this.onMouseMove, this);
25498             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25499             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25500             Roo.get(document).on('mouseup', this.onMouseUp, this);
25501         }
25502         
25503         this.selectorEl.on('change', this.onFileSelected, this);
25504     },
25505     
25506     reset : function()
25507     {    
25508         this.scale = 0;
25509         this.baseScale = 1;
25510         this.rotate = 0;
25511         this.baseRotate = 1;
25512         this.dragable = false;
25513         this.pinching = false;
25514         this.mouseX = 0;
25515         this.mouseY = 0;
25516         this.cropData = false;
25517         this.notifyEl.dom.innerHTML = this.emptyText;
25518         
25519         this.selectorEl.dom.value = '';
25520         
25521     },
25522     
25523     resize : function()
25524     {
25525         if(this.fireEvent('resize', this) != false){
25526             this.setThumbBoxPosition();
25527             this.setCanvasPosition();
25528         }
25529     },
25530     
25531     onFooterButtonClick : function(e, el, o, type)
25532     {
25533         switch (type) {
25534             case 'rotate-left' :
25535                 this.onRotateLeft(e);
25536                 break;
25537             case 'rotate-right' :
25538                 this.onRotateRight(e);
25539                 break;
25540             case 'picture' :
25541                 this.beforeSelectFile(e);
25542                 break;
25543             case 'trash' :
25544                 this.trash(e);
25545                 break;
25546             case 'crop' :
25547                 this.crop(e);
25548                 break;
25549             case 'download' :
25550                 this.download(e);
25551                 break;
25552             default :
25553                 break;
25554         }
25555         
25556         this.fireEvent('footerbuttonclick', this, type);
25557     },
25558     
25559     beforeSelectFile : function(e)
25560     {
25561         e.preventDefault();
25562         
25563         if(this.fireEvent('beforeselectfile', this) != false){
25564             this.selectorEl.dom.click();
25565         }
25566     },
25567     
25568     onFileSelected : function(e)
25569     {
25570         e.preventDefault();
25571         
25572         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25573             return;
25574         }
25575         
25576         var file = this.selectorEl.dom.files[0];
25577         
25578         if(this.fireEvent('inspect', this, file) != false){
25579             this.prepare(file);
25580         }
25581         
25582     },
25583     
25584     trash : function(e)
25585     {
25586         this.fireEvent('trash', this);
25587     },
25588     
25589     download : function(e)
25590     {
25591         this.fireEvent('download', this);
25592     },
25593     
25594     loadCanvas : function(src)
25595     {   
25596         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25597             
25598             this.reset();
25599             
25600             this.imageEl = document.createElement('img');
25601             
25602             var _this = this;
25603             
25604             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25605             
25606             this.imageEl.src = src;
25607         }
25608     },
25609     
25610     onLoadCanvas : function()
25611     {   
25612         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25613         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25614         
25615         this.bodyEl.un('click', this.beforeSelectFile, this);
25616         
25617         this.notifyEl.hide();
25618         this.thumbEl.show();
25619         this.footerEl.show();
25620         
25621         this.baseRotateLevel();
25622         
25623         if(this.isDocument){
25624             this.setThumbBoxSize();
25625         }
25626         
25627         this.setThumbBoxPosition();
25628         
25629         this.baseScaleLevel();
25630         
25631         this.draw();
25632         
25633         this.resize();
25634         
25635         this.canvasLoaded = true;
25636         
25637         if(this.loadMask){
25638             this.maskEl.unmask();
25639         }
25640         
25641     },
25642     
25643     setCanvasPosition : function()
25644     {   
25645         if(!this.canvasEl){
25646             return;
25647         }
25648         
25649         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25650         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25651         
25652         this.previewEl.setLeft(pw);
25653         this.previewEl.setTop(ph);
25654         
25655     },
25656     
25657     onMouseDown : function(e)
25658     {   
25659         e.stopEvent();
25660         
25661         this.dragable = true;
25662         this.pinching = false;
25663         
25664         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25665             this.dragable = false;
25666             return;
25667         }
25668         
25669         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25670         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25671         
25672     },
25673     
25674     onMouseMove : function(e)
25675     {   
25676         e.stopEvent();
25677         
25678         if(!this.canvasLoaded){
25679             return;
25680         }
25681         
25682         if (!this.dragable){
25683             return;
25684         }
25685         
25686         var minX = Math.ceil(this.thumbEl.getLeft(true));
25687         var minY = Math.ceil(this.thumbEl.getTop(true));
25688         
25689         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25690         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25691         
25692         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25693         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25694         
25695         x = x - this.mouseX;
25696         y = y - this.mouseY;
25697         
25698         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25699         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25700         
25701         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25702         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25703         
25704         this.previewEl.setLeft(bgX);
25705         this.previewEl.setTop(bgY);
25706         
25707         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25708         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25709     },
25710     
25711     onMouseUp : function(e)
25712     {   
25713         e.stopEvent();
25714         
25715         this.dragable = false;
25716     },
25717     
25718     onMouseWheel : function(e)
25719     {   
25720         e.stopEvent();
25721         
25722         this.startScale = this.scale;
25723         
25724         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25725         
25726         if(!this.zoomable()){
25727             this.scale = this.startScale;
25728             return;
25729         }
25730         
25731         this.draw();
25732         
25733         return;
25734     },
25735     
25736     zoomable : function()
25737     {
25738         var minScale = this.thumbEl.getWidth() / this.minWidth;
25739         
25740         if(this.minWidth < this.minHeight){
25741             minScale = this.thumbEl.getHeight() / this.minHeight;
25742         }
25743         
25744         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25745         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25746         
25747         if(
25748                 this.isDocument &&
25749                 (this.rotate == 0 || this.rotate == 180) && 
25750                 (
25751                     width > this.imageEl.OriginWidth || 
25752                     height > this.imageEl.OriginHeight ||
25753                     (width < this.minWidth && height < this.minHeight)
25754                 )
25755         ){
25756             return false;
25757         }
25758         
25759         if(
25760                 this.isDocument &&
25761                 (this.rotate == 90 || this.rotate == 270) && 
25762                 (
25763                     width > this.imageEl.OriginWidth || 
25764                     height > this.imageEl.OriginHeight ||
25765                     (width < this.minHeight && height < this.minWidth)
25766                 )
25767         ){
25768             return false;
25769         }
25770         
25771         if(
25772                 !this.isDocument &&
25773                 (this.rotate == 0 || this.rotate == 180) && 
25774                 (
25775                     width < this.minWidth || 
25776                     width > this.imageEl.OriginWidth || 
25777                     height < this.minHeight || 
25778                     height > this.imageEl.OriginHeight
25779                 )
25780         ){
25781             return false;
25782         }
25783         
25784         if(
25785                 !this.isDocument &&
25786                 (this.rotate == 90 || this.rotate == 270) && 
25787                 (
25788                     width < this.minHeight || 
25789                     width > this.imageEl.OriginWidth || 
25790                     height < this.minWidth || 
25791                     height > this.imageEl.OriginHeight
25792                 )
25793         ){
25794             return false;
25795         }
25796         
25797         return true;
25798         
25799     },
25800     
25801     onRotateLeft : function(e)
25802     {   
25803         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25804             
25805             var minScale = this.thumbEl.getWidth() / this.minWidth;
25806             
25807             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25808             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25809             
25810             this.startScale = this.scale;
25811             
25812             while (this.getScaleLevel() < minScale){
25813             
25814                 this.scale = this.scale + 1;
25815                 
25816                 if(!this.zoomable()){
25817                     break;
25818                 }
25819                 
25820                 if(
25821                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25822                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25823                 ){
25824                     continue;
25825                 }
25826                 
25827                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25828
25829                 this.draw();
25830                 
25831                 return;
25832             }
25833             
25834             this.scale = this.startScale;
25835             
25836             this.onRotateFail();
25837             
25838             return false;
25839         }
25840         
25841         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25842
25843         if(this.isDocument){
25844             this.setThumbBoxSize();
25845             this.setThumbBoxPosition();
25846             this.setCanvasPosition();
25847         }
25848         
25849         this.draw();
25850         
25851         this.fireEvent('rotate', this, 'left');
25852         
25853     },
25854     
25855     onRotateRight : function(e)
25856     {
25857         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25858             
25859             var minScale = this.thumbEl.getWidth() / this.minWidth;
25860         
25861             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25862             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25863             
25864             this.startScale = this.scale;
25865             
25866             while (this.getScaleLevel() < minScale){
25867             
25868                 this.scale = this.scale + 1;
25869                 
25870                 if(!this.zoomable()){
25871                     break;
25872                 }
25873                 
25874                 if(
25875                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25876                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25877                 ){
25878                     continue;
25879                 }
25880                 
25881                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25882
25883                 this.draw();
25884                 
25885                 return;
25886             }
25887             
25888             this.scale = this.startScale;
25889             
25890             this.onRotateFail();
25891             
25892             return false;
25893         }
25894         
25895         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25896
25897         if(this.isDocument){
25898             this.setThumbBoxSize();
25899             this.setThumbBoxPosition();
25900             this.setCanvasPosition();
25901         }
25902         
25903         this.draw();
25904         
25905         this.fireEvent('rotate', this, 'right');
25906     },
25907     
25908     onRotateFail : function()
25909     {
25910         this.errorEl.show(true);
25911         
25912         var _this = this;
25913         
25914         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25915     },
25916     
25917     draw : function()
25918     {
25919         this.previewEl.dom.innerHTML = '';
25920         
25921         var canvasEl = document.createElement("canvas");
25922         
25923         var contextEl = canvasEl.getContext("2d");
25924         
25925         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25926         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25927         var center = this.imageEl.OriginWidth / 2;
25928         
25929         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25930             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25931             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25932             center = this.imageEl.OriginHeight / 2;
25933         }
25934         
25935         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25936         
25937         contextEl.translate(center, center);
25938         contextEl.rotate(this.rotate * Math.PI / 180);
25939
25940         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25941         
25942         this.canvasEl = document.createElement("canvas");
25943         
25944         this.contextEl = this.canvasEl.getContext("2d");
25945         
25946         switch (this.rotate) {
25947             case 0 :
25948                 
25949                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25950                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25951                 
25952                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25953                 
25954                 break;
25955             case 90 : 
25956                 
25957                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25958                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25959                 
25960                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25961                     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);
25962                     break;
25963                 }
25964                 
25965                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25966                 
25967                 break;
25968             case 180 :
25969                 
25970                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25971                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25972                 
25973                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25974                     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);
25975                     break;
25976                 }
25977                 
25978                 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);
25979                 
25980                 break;
25981             case 270 :
25982                 
25983                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25984                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25985         
25986                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25987                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25988                     break;
25989                 }
25990                 
25991                 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);
25992                 
25993                 break;
25994             default : 
25995                 break;
25996         }
25997         
25998         this.previewEl.appendChild(this.canvasEl);
25999         
26000         this.setCanvasPosition();
26001     },
26002     
26003     crop : function()
26004     {
26005         if(!this.canvasLoaded){
26006             return;
26007         }
26008         
26009         var imageCanvas = document.createElement("canvas");
26010         
26011         var imageContext = imageCanvas.getContext("2d");
26012         
26013         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26014         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26015         
26016         var center = imageCanvas.width / 2;
26017         
26018         imageContext.translate(center, center);
26019         
26020         imageContext.rotate(this.rotate * Math.PI / 180);
26021         
26022         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26023         
26024         var canvas = document.createElement("canvas");
26025         
26026         var context = canvas.getContext("2d");
26027                 
26028         canvas.width = this.minWidth;
26029         canvas.height = this.minHeight;
26030
26031         switch (this.rotate) {
26032             case 0 :
26033                 
26034                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26035                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26036                 
26037                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26038                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26039                 
26040                 var targetWidth = this.minWidth - 2 * x;
26041                 var targetHeight = this.minHeight - 2 * y;
26042                 
26043                 var scale = 1;
26044                 
26045                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26046                     scale = targetWidth / width;
26047                 }
26048                 
26049                 if(x > 0 && y == 0){
26050                     scale = targetHeight / height;
26051                 }
26052                 
26053                 if(x > 0 && y > 0){
26054                     scale = targetWidth / width;
26055                     
26056                     if(width < height){
26057                         scale = targetHeight / height;
26058                     }
26059                 }
26060                 
26061                 context.scale(scale, scale);
26062                 
26063                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26064                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26065
26066                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26067                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26068
26069                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26070                 
26071                 break;
26072             case 90 : 
26073                 
26074                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26075                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26076                 
26077                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26078                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26079                 
26080                 var targetWidth = this.minWidth - 2 * x;
26081                 var targetHeight = this.minHeight - 2 * y;
26082                 
26083                 var scale = 1;
26084                 
26085                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26086                     scale = targetWidth / width;
26087                 }
26088                 
26089                 if(x > 0 && y == 0){
26090                     scale = targetHeight / height;
26091                 }
26092                 
26093                 if(x > 0 && y > 0){
26094                     scale = targetWidth / width;
26095                     
26096                     if(width < height){
26097                         scale = targetHeight / height;
26098                     }
26099                 }
26100                 
26101                 context.scale(scale, scale);
26102                 
26103                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26104                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26105
26106                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26107                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26108                 
26109                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26110                 
26111                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26112                 
26113                 break;
26114             case 180 :
26115                 
26116                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26117                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26118                 
26119                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26120                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26121                 
26122                 var targetWidth = this.minWidth - 2 * x;
26123                 var targetHeight = this.minHeight - 2 * y;
26124                 
26125                 var scale = 1;
26126                 
26127                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26128                     scale = targetWidth / width;
26129                 }
26130                 
26131                 if(x > 0 && y == 0){
26132                     scale = targetHeight / height;
26133                 }
26134                 
26135                 if(x > 0 && y > 0){
26136                     scale = targetWidth / width;
26137                     
26138                     if(width < height){
26139                         scale = targetHeight / height;
26140                     }
26141                 }
26142                 
26143                 context.scale(scale, scale);
26144                 
26145                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26146                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26147
26148                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26149                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26150
26151                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26152                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26153                 
26154                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26155                 
26156                 break;
26157             case 270 :
26158                 
26159                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26160                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26161                 
26162                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26163                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26164                 
26165                 var targetWidth = this.minWidth - 2 * x;
26166                 var targetHeight = this.minHeight - 2 * y;
26167                 
26168                 var scale = 1;
26169                 
26170                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26171                     scale = targetWidth / width;
26172                 }
26173                 
26174                 if(x > 0 && y == 0){
26175                     scale = targetHeight / height;
26176                 }
26177                 
26178                 if(x > 0 && y > 0){
26179                     scale = targetWidth / width;
26180                     
26181                     if(width < height){
26182                         scale = targetHeight / height;
26183                     }
26184                 }
26185                 
26186                 context.scale(scale, scale);
26187                 
26188                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26189                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26190
26191                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26192                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26193                 
26194                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26195                 
26196                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26197                 
26198                 break;
26199             default : 
26200                 break;
26201         }
26202         
26203         this.cropData = canvas.toDataURL(this.cropType);
26204         
26205         if(this.fireEvent('crop', this, this.cropData) !== false){
26206             this.process(this.file, this.cropData);
26207         }
26208         
26209         return;
26210         
26211     },
26212     
26213     setThumbBoxSize : function()
26214     {
26215         var width, height;
26216         
26217         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26218             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26219             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26220             
26221             this.minWidth = width;
26222             this.minHeight = height;
26223             
26224             if(this.rotate == 90 || this.rotate == 270){
26225                 this.minWidth = height;
26226                 this.minHeight = width;
26227             }
26228         }
26229         
26230         height = 300;
26231         width = Math.ceil(this.minWidth * height / this.minHeight);
26232         
26233         if(this.minWidth > this.minHeight){
26234             width = 300;
26235             height = Math.ceil(this.minHeight * width / this.minWidth);
26236         }
26237         
26238         this.thumbEl.setStyle({
26239             width : width + 'px',
26240             height : height + 'px'
26241         });
26242
26243         return;
26244             
26245     },
26246     
26247     setThumbBoxPosition : function()
26248     {
26249         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26250         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26251         
26252         this.thumbEl.setLeft(x);
26253         this.thumbEl.setTop(y);
26254         
26255     },
26256     
26257     baseRotateLevel : function()
26258     {
26259         this.baseRotate = 1;
26260         
26261         if(
26262                 typeof(this.exif) != 'undefined' &&
26263                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26264                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26265         ){
26266             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26267         }
26268         
26269         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26270         
26271     },
26272     
26273     baseScaleLevel : function()
26274     {
26275         var width, height;
26276         
26277         if(this.isDocument){
26278             
26279             if(this.baseRotate == 6 || this.baseRotate == 8){
26280             
26281                 height = this.thumbEl.getHeight();
26282                 this.baseScale = height / this.imageEl.OriginWidth;
26283
26284                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26285                     width = this.thumbEl.getWidth();
26286                     this.baseScale = width / this.imageEl.OriginHeight;
26287                 }
26288
26289                 return;
26290             }
26291
26292             height = this.thumbEl.getHeight();
26293             this.baseScale = height / this.imageEl.OriginHeight;
26294
26295             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26296                 width = this.thumbEl.getWidth();
26297                 this.baseScale = width / this.imageEl.OriginWidth;
26298             }
26299
26300             return;
26301         }
26302         
26303         if(this.baseRotate == 6 || this.baseRotate == 8){
26304             
26305             width = this.thumbEl.getHeight();
26306             this.baseScale = width / this.imageEl.OriginHeight;
26307             
26308             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26309                 height = this.thumbEl.getWidth();
26310                 this.baseScale = height / this.imageEl.OriginHeight;
26311             }
26312             
26313             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26314                 height = this.thumbEl.getWidth();
26315                 this.baseScale = height / this.imageEl.OriginHeight;
26316                 
26317                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26318                     width = this.thumbEl.getHeight();
26319                     this.baseScale = width / this.imageEl.OriginWidth;
26320                 }
26321             }
26322             
26323             return;
26324         }
26325         
26326         width = this.thumbEl.getWidth();
26327         this.baseScale = width / this.imageEl.OriginWidth;
26328         
26329         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26330             height = this.thumbEl.getHeight();
26331             this.baseScale = height / this.imageEl.OriginHeight;
26332         }
26333         
26334         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26335             
26336             height = this.thumbEl.getHeight();
26337             this.baseScale = height / this.imageEl.OriginHeight;
26338             
26339             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26340                 width = this.thumbEl.getWidth();
26341                 this.baseScale = width / this.imageEl.OriginWidth;
26342             }
26343             
26344         }
26345         
26346         return;
26347     },
26348     
26349     getScaleLevel : function()
26350     {
26351         return this.baseScale * Math.pow(1.1, this.scale);
26352     },
26353     
26354     onTouchStart : function(e)
26355     {
26356         if(!this.canvasLoaded){
26357             this.beforeSelectFile(e);
26358             return;
26359         }
26360         
26361         var touches = e.browserEvent.touches;
26362         
26363         if(!touches){
26364             return;
26365         }
26366         
26367         if(touches.length == 1){
26368             this.onMouseDown(e);
26369             return;
26370         }
26371         
26372         if(touches.length != 2){
26373             return;
26374         }
26375         
26376         var coords = [];
26377         
26378         for(var i = 0, finger; finger = touches[i]; i++){
26379             coords.push(finger.pageX, finger.pageY);
26380         }
26381         
26382         var x = Math.pow(coords[0] - coords[2], 2);
26383         var y = Math.pow(coords[1] - coords[3], 2);
26384         
26385         this.startDistance = Math.sqrt(x + y);
26386         
26387         this.startScale = this.scale;
26388         
26389         this.pinching = true;
26390         this.dragable = false;
26391         
26392     },
26393     
26394     onTouchMove : function(e)
26395     {
26396         if(!this.pinching && !this.dragable){
26397             return;
26398         }
26399         
26400         var touches = e.browserEvent.touches;
26401         
26402         if(!touches){
26403             return;
26404         }
26405         
26406         if(this.dragable){
26407             this.onMouseMove(e);
26408             return;
26409         }
26410         
26411         var coords = [];
26412         
26413         for(var i = 0, finger; finger = touches[i]; i++){
26414             coords.push(finger.pageX, finger.pageY);
26415         }
26416         
26417         var x = Math.pow(coords[0] - coords[2], 2);
26418         var y = Math.pow(coords[1] - coords[3], 2);
26419         
26420         this.endDistance = Math.sqrt(x + y);
26421         
26422         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26423         
26424         if(!this.zoomable()){
26425             this.scale = this.startScale;
26426             return;
26427         }
26428         
26429         this.draw();
26430         
26431     },
26432     
26433     onTouchEnd : function(e)
26434     {
26435         this.pinching = false;
26436         this.dragable = false;
26437         
26438     },
26439     
26440     process : function(file, crop)
26441     {
26442         if(this.loadMask){
26443             this.maskEl.mask(this.loadingText);
26444         }
26445         
26446         this.xhr = new XMLHttpRequest();
26447         
26448         file.xhr = this.xhr;
26449
26450         this.xhr.open(this.method, this.url, true);
26451         
26452         var headers = {
26453             "Accept": "application/json",
26454             "Cache-Control": "no-cache",
26455             "X-Requested-With": "XMLHttpRequest"
26456         };
26457         
26458         for (var headerName in headers) {
26459             var headerValue = headers[headerName];
26460             if (headerValue) {
26461                 this.xhr.setRequestHeader(headerName, headerValue);
26462             }
26463         }
26464         
26465         var _this = this;
26466         
26467         this.xhr.onload = function()
26468         {
26469             _this.xhrOnLoad(_this.xhr);
26470         }
26471         
26472         this.xhr.onerror = function()
26473         {
26474             _this.xhrOnError(_this.xhr);
26475         }
26476         
26477         var formData = new FormData();
26478
26479         formData.append('returnHTML', 'NO');
26480         
26481         if(crop){
26482             formData.append('crop', crop);
26483         }
26484         
26485         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26486             formData.append(this.paramName, file, file.name);
26487         }
26488         
26489         if(typeof(file.filename) != 'undefined'){
26490             formData.append('filename', file.filename);
26491         }
26492         
26493         if(typeof(file.mimetype) != 'undefined'){
26494             formData.append('mimetype', file.mimetype);
26495         }
26496         
26497         if(this.fireEvent('arrange', this, formData) != false){
26498             this.xhr.send(formData);
26499         };
26500     },
26501     
26502     xhrOnLoad : function(xhr)
26503     {
26504         if(this.loadMask){
26505             this.maskEl.unmask();
26506         }
26507         
26508         if (xhr.readyState !== 4) {
26509             this.fireEvent('exception', this, xhr);
26510             return;
26511         }
26512
26513         var response = Roo.decode(xhr.responseText);
26514         
26515         if(!response.success){
26516             this.fireEvent('exception', this, xhr);
26517             return;
26518         }
26519         
26520         var response = Roo.decode(xhr.responseText);
26521         
26522         this.fireEvent('upload', this, response);
26523         
26524     },
26525     
26526     xhrOnError : function()
26527     {
26528         if(this.loadMask){
26529             this.maskEl.unmask();
26530         }
26531         
26532         Roo.log('xhr on error');
26533         
26534         var response = Roo.decode(xhr.responseText);
26535           
26536         Roo.log(response);
26537         
26538     },
26539     
26540     prepare : function(file)
26541     {   
26542         if(this.loadMask){
26543             this.maskEl.mask(this.loadingText);
26544         }
26545         
26546         this.file = false;
26547         this.exif = {};
26548         
26549         if(typeof(file) === 'string'){
26550             this.loadCanvas(file);
26551             return;
26552         }
26553         
26554         if(!file || !this.urlAPI){
26555             return;
26556         }
26557         
26558         this.file = file;
26559         this.cropType = file.type;
26560         
26561         var _this = this;
26562         
26563         if(this.fireEvent('prepare', this, this.file) != false){
26564             
26565             var reader = new FileReader();
26566             
26567             reader.onload = function (e) {
26568                 if (e.target.error) {
26569                     Roo.log(e.target.error);
26570                     return;
26571                 }
26572                 
26573                 var buffer = e.target.result,
26574                     dataView = new DataView(buffer),
26575                     offset = 2,
26576                     maxOffset = dataView.byteLength - 4,
26577                     markerBytes,
26578                     markerLength;
26579                 
26580                 if (dataView.getUint16(0) === 0xffd8) {
26581                     while (offset < maxOffset) {
26582                         markerBytes = dataView.getUint16(offset);
26583                         
26584                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26585                             markerLength = dataView.getUint16(offset + 2) + 2;
26586                             if (offset + markerLength > dataView.byteLength) {
26587                                 Roo.log('Invalid meta data: Invalid segment size.');
26588                                 break;
26589                             }
26590                             
26591                             if(markerBytes == 0xffe1){
26592                                 _this.parseExifData(
26593                                     dataView,
26594                                     offset,
26595                                     markerLength
26596                                 );
26597                             }
26598                             
26599                             offset += markerLength;
26600                             
26601                             continue;
26602                         }
26603                         
26604                         break;
26605                     }
26606                     
26607                 }
26608                 
26609                 var url = _this.urlAPI.createObjectURL(_this.file);
26610                 
26611                 _this.loadCanvas(url);
26612                 
26613                 return;
26614             }
26615             
26616             reader.readAsArrayBuffer(this.file);
26617             
26618         }
26619         
26620     },
26621     
26622     parseExifData : function(dataView, offset, length)
26623     {
26624         var tiffOffset = offset + 10,
26625             littleEndian,
26626             dirOffset;
26627     
26628         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26629             // No Exif data, might be XMP data instead
26630             return;
26631         }
26632         
26633         // Check for the ASCII code for "Exif" (0x45786966):
26634         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26635             // No Exif data, might be XMP data instead
26636             return;
26637         }
26638         if (tiffOffset + 8 > dataView.byteLength) {
26639             Roo.log('Invalid Exif data: Invalid segment size.');
26640             return;
26641         }
26642         // Check for the two null bytes:
26643         if (dataView.getUint16(offset + 8) !== 0x0000) {
26644             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26645             return;
26646         }
26647         // Check the byte alignment:
26648         switch (dataView.getUint16(tiffOffset)) {
26649         case 0x4949:
26650             littleEndian = true;
26651             break;
26652         case 0x4D4D:
26653             littleEndian = false;
26654             break;
26655         default:
26656             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26657             return;
26658         }
26659         // Check for the TIFF tag marker (0x002A):
26660         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26661             Roo.log('Invalid Exif data: Missing TIFF marker.');
26662             return;
26663         }
26664         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26665         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26666         
26667         this.parseExifTags(
26668             dataView,
26669             tiffOffset,
26670             tiffOffset + dirOffset,
26671             littleEndian
26672         );
26673     },
26674     
26675     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26676     {
26677         var tagsNumber,
26678             dirEndOffset,
26679             i;
26680         if (dirOffset + 6 > dataView.byteLength) {
26681             Roo.log('Invalid Exif data: Invalid directory offset.');
26682             return;
26683         }
26684         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26685         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26686         if (dirEndOffset + 4 > dataView.byteLength) {
26687             Roo.log('Invalid Exif data: Invalid directory size.');
26688             return;
26689         }
26690         for (i = 0; i < tagsNumber; i += 1) {
26691             this.parseExifTag(
26692                 dataView,
26693                 tiffOffset,
26694                 dirOffset + 2 + 12 * i, // tag offset
26695                 littleEndian
26696             );
26697         }
26698         // Return the offset to the next directory:
26699         return dataView.getUint32(dirEndOffset, littleEndian);
26700     },
26701     
26702     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26703     {
26704         var tag = dataView.getUint16(offset, littleEndian);
26705         
26706         this.exif[tag] = this.getExifValue(
26707             dataView,
26708             tiffOffset,
26709             offset,
26710             dataView.getUint16(offset + 2, littleEndian), // tag type
26711             dataView.getUint32(offset + 4, littleEndian), // tag length
26712             littleEndian
26713         );
26714     },
26715     
26716     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26717     {
26718         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26719             tagSize,
26720             dataOffset,
26721             values,
26722             i,
26723             str,
26724             c;
26725     
26726         if (!tagType) {
26727             Roo.log('Invalid Exif data: Invalid tag type.');
26728             return;
26729         }
26730         
26731         tagSize = tagType.size * length;
26732         // Determine if the value is contained in the dataOffset bytes,
26733         // or if the value at the dataOffset is a pointer to the actual data:
26734         dataOffset = tagSize > 4 ?
26735                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26736         if (dataOffset + tagSize > dataView.byteLength) {
26737             Roo.log('Invalid Exif data: Invalid data offset.');
26738             return;
26739         }
26740         if (length === 1) {
26741             return tagType.getValue(dataView, dataOffset, littleEndian);
26742         }
26743         values = [];
26744         for (i = 0; i < length; i += 1) {
26745             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26746         }
26747         
26748         if (tagType.ascii) {
26749             str = '';
26750             // Concatenate the chars:
26751             for (i = 0; i < values.length; i += 1) {
26752                 c = values[i];
26753                 // Ignore the terminating NULL byte(s):
26754                 if (c === '\u0000') {
26755                     break;
26756                 }
26757                 str += c;
26758             }
26759             return str;
26760         }
26761         return values;
26762     }
26763     
26764 });
26765
26766 Roo.apply(Roo.bootstrap.UploadCropbox, {
26767     tags : {
26768         'Orientation': 0x0112
26769     },
26770     
26771     Orientation: {
26772             1: 0, //'top-left',
26773 //            2: 'top-right',
26774             3: 180, //'bottom-right',
26775 //            4: 'bottom-left',
26776 //            5: 'left-top',
26777             6: 90, //'right-top',
26778 //            7: 'right-bottom',
26779             8: 270 //'left-bottom'
26780     },
26781     
26782     exifTagTypes : {
26783         // byte, 8-bit unsigned int:
26784         1: {
26785             getValue: function (dataView, dataOffset) {
26786                 return dataView.getUint8(dataOffset);
26787             },
26788             size: 1
26789         },
26790         // ascii, 8-bit byte:
26791         2: {
26792             getValue: function (dataView, dataOffset) {
26793                 return String.fromCharCode(dataView.getUint8(dataOffset));
26794             },
26795             size: 1,
26796             ascii: true
26797         },
26798         // short, 16 bit int:
26799         3: {
26800             getValue: function (dataView, dataOffset, littleEndian) {
26801                 return dataView.getUint16(dataOffset, littleEndian);
26802             },
26803             size: 2
26804         },
26805         // long, 32 bit int:
26806         4: {
26807             getValue: function (dataView, dataOffset, littleEndian) {
26808                 return dataView.getUint32(dataOffset, littleEndian);
26809             },
26810             size: 4
26811         },
26812         // rational = two long values, first is numerator, second is denominator:
26813         5: {
26814             getValue: function (dataView, dataOffset, littleEndian) {
26815                 return dataView.getUint32(dataOffset, littleEndian) /
26816                     dataView.getUint32(dataOffset + 4, littleEndian);
26817             },
26818             size: 8
26819         },
26820         // slong, 32 bit signed int:
26821         9: {
26822             getValue: function (dataView, dataOffset, littleEndian) {
26823                 return dataView.getInt32(dataOffset, littleEndian);
26824             },
26825             size: 4
26826         },
26827         // srational, two slongs, first is numerator, second is denominator:
26828         10: {
26829             getValue: function (dataView, dataOffset, littleEndian) {
26830                 return dataView.getInt32(dataOffset, littleEndian) /
26831                     dataView.getInt32(dataOffset + 4, littleEndian);
26832             },
26833             size: 8
26834         }
26835     },
26836     
26837     footer : {
26838         STANDARD : [
26839             {
26840                 tag : 'div',
26841                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26842                 action : 'rotate-left',
26843                 cn : [
26844                     {
26845                         tag : 'button',
26846                         cls : 'btn btn-default',
26847                         html : '<i class="fa fa-undo"></i>'
26848                     }
26849                 ]
26850             },
26851             {
26852                 tag : 'div',
26853                 cls : 'btn-group roo-upload-cropbox-picture',
26854                 action : 'picture',
26855                 cn : [
26856                     {
26857                         tag : 'button',
26858                         cls : 'btn btn-default',
26859                         html : '<i class="fa fa-picture-o"></i>'
26860                     }
26861                 ]
26862             },
26863             {
26864                 tag : 'div',
26865                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26866                 action : 'rotate-right',
26867                 cn : [
26868                     {
26869                         tag : 'button',
26870                         cls : 'btn btn-default',
26871                         html : '<i class="fa fa-repeat"></i>'
26872                     }
26873                 ]
26874             }
26875         ],
26876         DOCUMENT : [
26877             {
26878                 tag : 'div',
26879                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26880                 action : 'rotate-left',
26881                 cn : [
26882                     {
26883                         tag : 'button',
26884                         cls : 'btn btn-default',
26885                         html : '<i class="fa fa-undo"></i>'
26886                     }
26887                 ]
26888             },
26889             {
26890                 tag : 'div',
26891                 cls : 'btn-group roo-upload-cropbox-download',
26892                 action : 'download',
26893                 cn : [
26894                     {
26895                         tag : 'button',
26896                         cls : 'btn btn-default',
26897                         html : '<i class="fa fa-download"></i>'
26898                     }
26899                 ]
26900             },
26901             {
26902                 tag : 'div',
26903                 cls : 'btn-group roo-upload-cropbox-crop',
26904                 action : 'crop',
26905                 cn : [
26906                     {
26907                         tag : 'button',
26908                         cls : 'btn btn-default',
26909                         html : '<i class="fa fa-crop"></i>'
26910                     }
26911                 ]
26912             },
26913             {
26914                 tag : 'div',
26915                 cls : 'btn-group roo-upload-cropbox-trash',
26916                 action : 'trash',
26917                 cn : [
26918                     {
26919                         tag : 'button',
26920                         cls : 'btn btn-default',
26921                         html : '<i class="fa fa-trash"></i>'
26922                     }
26923                 ]
26924             },
26925             {
26926                 tag : 'div',
26927                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26928                 action : 'rotate-right',
26929                 cn : [
26930                     {
26931                         tag : 'button',
26932                         cls : 'btn btn-default',
26933                         html : '<i class="fa fa-repeat"></i>'
26934                     }
26935                 ]
26936             }
26937         ],
26938         ROTATOR : [
26939             {
26940                 tag : 'div',
26941                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26942                 action : 'rotate-left',
26943                 cn : [
26944                     {
26945                         tag : 'button',
26946                         cls : 'btn btn-default',
26947                         html : '<i class="fa fa-undo"></i>'
26948                     }
26949                 ]
26950             },
26951             {
26952                 tag : 'div',
26953                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26954                 action : 'rotate-right',
26955                 cn : [
26956                     {
26957                         tag : 'button',
26958                         cls : 'btn btn-default',
26959                         html : '<i class="fa fa-repeat"></i>'
26960                     }
26961                 ]
26962             }
26963         ]
26964     }
26965 });
26966
26967 /*
26968 * Licence: LGPL
26969 */
26970
26971 /**
26972  * @class Roo.bootstrap.DocumentManager
26973  * @extends Roo.bootstrap.Component
26974  * Bootstrap DocumentManager class
26975  * @cfg {String} paramName default 'imageUpload'
26976  * @cfg {String} method default POST
26977  * @cfg {String} url action url
26978  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26979  * @cfg {Boolean} multiple multiple upload default true
26980  * @cfg {Number} thumbSize default 300
26981  * @cfg {String} fieldLabel
26982  * @cfg {Number} labelWidth default 4
26983  * @cfg {String} labelAlign (left|top) default left
26984  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26985  * 
26986  * @constructor
26987  * Create a new DocumentManager
26988  * @param {Object} config The config object
26989  */
26990
26991 Roo.bootstrap.DocumentManager = function(config){
26992     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26993     
26994     this.addEvents({
26995         /**
26996          * @event initial
26997          * Fire when initial the DocumentManager
26998          * @param {Roo.bootstrap.DocumentManager} this
26999          */
27000         "initial" : true,
27001         /**
27002          * @event inspect
27003          * inspect selected file
27004          * @param {Roo.bootstrap.DocumentManager} this
27005          * @param {File} file
27006          */
27007         "inspect" : true,
27008         /**
27009          * @event exception
27010          * Fire when xhr load exception
27011          * @param {Roo.bootstrap.DocumentManager} this
27012          * @param {XMLHttpRequest} xhr
27013          */
27014         "exception" : true,
27015         /**
27016          * @event prepare
27017          * prepare the form data
27018          * @param {Roo.bootstrap.DocumentManager} this
27019          * @param {Object} formData
27020          */
27021         "prepare" : true,
27022         /**
27023          * @event remove
27024          * Fire when remove the file
27025          * @param {Roo.bootstrap.DocumentManager} this
27026          * @param {Object} file
27027          */
27028         "remove" : true,
27029         /**
27030          * @event refresh
27031          * Fire after refresh the file
27032          * @param {Roo.bootstrap.DocumentManager} this
27033          */
27034         "refresh" : true,
27035         /**
27036          * @event click
27037          * Fire after click the image
27038          * @param {Roo.bootstrap.DocumentManager} this
27039          * @param {Object} file
27040          */
27041         "click" : true,
27042         /**
27043          * @event edit
27044          * Fire when upload a image and editable set to true
27045          * @param {Roo.bootstrap.DocumentManager} this
27046          * @param {Object} file
27047          */
27048         "edit" : true,
27049         /**
27050          * @event beforeselectfile
27051          * Fire before select file
27052          * @param {Roo.bootstrap.DocumentManager} this
27053          */
27054         "beforeselectfile" : true,
27055         /**
27056          * @event process
27057          * Fire before process file
27058          * @param {Roo.bootstrap.DocumentManager} this
27059          * @param {Object} file
27060          */
27061         "process" : true
27062         
27063     });
27064 };
27065
27066 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27067     
27068     boxes : 0,
27069     inputName : '',
27070     thumbSize : 300,
27071     multiple : true,
27072     files : [],
27073     method : 'POST',
27074     url : '',
27075     paramName : 'imageUpload',
27076     fieldLabel : '',
27077     labelWidth : 4,
27078     labelAlign : 'left',
27079     editable : true,
27080     delegates : [],
27081     
27082     
27083     xhr : false, 
27084     
27085     getAutoCreate : function()
27086     {   
27087         var managerWidget = {
27088             tag : 'div',
27089             cls : 'roo-document-manager',
27090             cn : [
27091                 {
27092                     tag : 'input',
27093                     cls : 'roo-document-manager-selector',
27094                     type : 'file'
27095                 },
27096                 {
27097                     tag : 'div',
27098                     cls : 'roo-document-manager-uploader',
27099                     cn : [
27100                         {
27101                             tag : 'div',
27102                             cls : 'roo-document-manager-upload-btn',
27103                             html : '<i class="fa fa-plus"></i>'
27104                         }
27105                     ]
27106                     
27107                 }
27108             ]
27109         };
27110         
27111         var content = [
27112             {
27113                 tag : 'div',
27114                 cls : 'column col-md-12',
27115                 cn : managerWidget
27116             }
27117         ];
27118         
27119         if(this.fieldLabel.length){
27120             
27121             content = [
27122                 {
27123                     tag : 'div',
27124                     cls : 'column col-md-12',
27125                     html : this.fieldLabel
27126                 },
27127                 {
27128                     tag : 'div',
27129                     cls : 'column col-md-12',
27130                     cn : managerWidget
27131                 }
27132             ];
27133
27134             if(this.labelAlign == 'left'){
27135                 content = [
27136                     {
27137                         tag : 'div',
27138                         cls : 'column col-md-' + this.labelWidth,
27139                         html : this.fieldLabel
27140                     },
27141                     {
27142                         tag : 'div',
27143                         cls : 'column col-md-' + (12 - this.labelWidth),
27144                         cn : managerWidget
27145                     }
27146                 ];
27147                 
27148             }
27149         }
27150         
27151         var cfg = {
27152             tag : 'div',
27153             cls : 'row clearfix',
27154             cn : content
27155         };
27156         
27157         return cfg;
27158         
27159     },
27160     
27161     initEvents : function()
27162     {
27163         this.managerEl = this.el.select('.roo-document-manager', true).first();
27164         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27165         
27166         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27167         this.selectorEl.hide();
27168         
27169         if(this.multiple){
27170             this.selectorEl.attr('multiple', 'multiple');
27171         }
27172         
27173         this.selectorEl.on('change', this.onFileSelected, this);
27174         
27175         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27176         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27177         
27178         this.uploader.on('click', this.onUploaderClick, this);
27179         
27180         this.renderProgressDialog();
27181         
27182         var _this = this;
27183         
27184         window.addEventListener("resize", function() { _this.refresh(); } );
27185         
27186         this.fireEvent('initial', this);
27187     },
27188     
27189     renderProgressDialog : function()
27190     {
27191         var _this = this;
27192         
27193         this.progressDialog = new Roo.bootstrap.Modal({
27194             cls : 'roo-document-manager-progress-dialog',
27195             allow_close : false,
27196             title : '',
27197             buttons : [
27198                 {
27199                     name  :'cancel',
27200                     weight : 'danger',
27201                     html : 'Cancel'
27202                 }
27203             ], 
27204             listeners : { 
27205                 btnclick : function() {
27206                     _this.uploadCancel();
27207                     this.hide();
27208                 }
27209             }
27210         });
27211          
27212         this.progressDialog.render(Roo.get(document.body));
27213          
27214         this.progress = new Roo.bootstrap.Progress({
27215             cls : 'roo-document-manager-progress',
27216             active : true,
27217             striped : true
27218         });
27219         
27220         this.progress.render(this.progressDialog.getChildContainer());
27221         
27222         this.progressBar = new Roo.bootstrap.ProgressBar({
27223             cls : 'roo-document-manager-progress-bar',
27224             aria_valuenow : 0,
27225             aria_valuemin : 0,
27226             aria_valuemax : 12,
27227             panel : 'success'
27228         });
27229         
27230         this.progressBar.render(this.progress.getChildContainer());
27231     },
27232     
27233     onUploaderClick : function(e)
27234     {
27235         e.preventDefault();
27236      
27237         if(this.fireEvent('beforeselectfile', this) != false){
27238             this.selectorEl.dom.click();
27239         }
27240         
27241     },
27242     
27243     onFileSelected : function(e)
27244     {
27245         e.preventDefault();
27246         
27247         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27248             return;
27249         }
27250         
27251         Roo.each(this.selectorEl.dom.files, function(file){
27252             if(this.fireEvent('inspect', this, file) != false){
27253                 this.files.push(file);
27254             }
27255         }, this);
27256         
27257         this.queue();
27258         
27259     },
27260     
27261     queue : function()
27262     {
27263         this.selectorEl.dom.value = '';
27264         
27265         if(!this.files.length){
27266             return;
27267         }
27268         
27269         if(this.boxes > 0 && this.files.length > this.boxes){
27270             this.files = this.files.slice(0, this.boxes);
27271         }
27272         
27273         this.uploader.show();
27274         
27275         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27276             this.uploader.hide();
27277         }
27278         
27279         var _this = this;
27280         
27281         var files = [];
27282         
27283         var docs = [];
27284         
27285         Roo.each(this.files, function(file){
27286             
27287             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27288                 var f = this.renderPreview(file);
27289                 files.push(f);
27290                 return;
27291             }
27292             
27293             if(file.type.indexOf('image') != -1){
27294                 this.delegates.push(
27295                     (function(){
27296                         _this.process(file);
27297                     }).createDelegate(this)
27298                 );
27299         
27300                 return;
27301             }
27302             
27303             docs.push(
27304                 (function(){
27305                     _this.process(file);
27306                 }).createDelegate(this)
27307             );
27308             
27309         }, this);
27310         
27311         this.files = files;
27312         
27313         this.delegates = this.delegates.concat(docs);
27314         
27315         if(!this.delegates.length){
27316             this.refresh();
27317             return;
27318         }
27319         
27320         this.progressBar.aria_valuemax = this.delegates.length;
27321         
27322         this.arrange();
27323         
27324         return;
27325     },
27326     
27327     arrange : function()
27328     {
27329         if(!this.delegates.length){
27330             this.progressDialog.hide();
27331             this.refresh();
27332             return;
27333         }
27334         
27335         var delegate = this.delegates.shift();
27336         
27337         this.progressDialog.show();
27338         
27339         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27340         
27341         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27342         
27343         delegate();
27344     },
27345     
27346     refresh : function()
27347     {
27348         this.uploader.show();
27349         
27350         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27351             this.uploader.hide();
27352         }
27353         
27354         Roo.isTouch ? this.closable(false) : this.closable(true);
27355         
27356         this.fireEvent('refresh', this);
27357     },
27358     
27359     onRemove : function(e, el, o)
27360     {
27361         e.preventDefault();
27362         
27363         this.fireEvent('remove', this, o);
27364         
27365     },
27366     
27367     remove : function(o)
27368     {
27369         var files = [];
27370         
27371         Roo.each(this.files, function(file){
27372             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27373                 files.push(file);
27374                 return;
27375             }
27376
27377             o.target.remove();
27378
27379         }, this);
27380         
27381         this.files = files;
27382         
27383         this.refresh();
27384     },
27385     
27386     clear : function()
27387     {
27388         Roo.each(this.files, function(file){
27389             if(!file.target){
27390                 return;
27391             }
27392             
27393             file.target.remove();
27394
27395         }, this);
27396         
27397         this.files = [];
27398         
27399         this.refresh();
27400     },
27401     
27402     onClick : function(e, el, o)
27403     {
27404         e.preventDefault();
27405         
27406         this.fireEvent('click', this, o);
27407         
27408     },
27409     
27410     closable : function(closable)
27411     {
27412         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27413             
27414             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27415             
27416             if(closable){
27417                 el.show();
27418                 return;
27419             }
27420             
27421             el.hide();
27422             
27423         }, this);
27424     },
27425     
27426     xhrOnLoad : function(xhr)
27427     {
27428         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27429             el.remove();
27430         }, this);
27431         
27432         if (xhr.readyState !== 4) {
27433             this.arrange();
27434             this.fireEvent('exception', this, xhr);
27435             return;
27436         }
27437
27438         var response = Roo.decode(xhr.responseText);
27439         
27440         if(!response.success){
27441             this.arrange();
27442             this.fireEvent('exception', this, xhr);
27443             return;
27444         }
27445         
27446         var file = this.renderPreview(response.data);
27447         
27448         this.files.push(file);
27449         
27450         this.arrange();
27451         
27452     },
27453     
27454     xhrOnError : function(xhr)
27455     {
27456         Roo.log('xhr on error');
27457         
27458         var response = Roo.decode(xhr.responseText);
27459           
27460         Roo.log(response);
27461         
27462         this.arrange();
27463     },
27464     
27465     process : function(file)
27466     {
27467         if(this.fireEvent('process', this, file) !== false){
27468             if(this.editable && file.type.indexOf('image') != -1){
27469                 this.fireEvent('edit', this, file);
27470                 return;
27471             }
27472
27473             this.uploadStart(file, false);
27474
27475             return;
27476         }
27477         
27478     },
27479     
27480     uploadStart : function(file, crop)
27481     {
27482         this.xhr = new XMLHttpRequest();
27483         
27484         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27485             this.arrange();
27486             return;
27487         }
27488         
27489         file.xhr = this.xhr;
27490             
27491         this.managerEl.createChild({
27492             tag : 'div',
27493             cls : 'roo-document-manager-loading',
27494             cn : [
27495                 {
27496                     tag : 'div',
27497                     tooltip : file.name,
27498                     cls : 'roo-document-manager-thumb',
27499                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27500                 }
27501             ]
27502
27503         });
27504
27505         this.xhr.open(this.method, this.url, true);
27506         
27507         var headers = {
27508             "Accept": "application/json",
27509             "Cache-Control": "no-cache",
27510             "X-Requested-With": "XMLHttpRequest"
27511         };
27512         
27513         for (var headerName in headers) {
27514             var headerValue = headers[headerName];
27515             if (headerValue) {
27516                 this.xhr.setRequestHeader(headerName, headerValue);
27517             }
27518         }
27519         
27520         var _this = this;
27521         
27522         this.xhr.onload = function()
27523         {
27524             _this.xhrOnLoad(_this.xhr);
27525         }
27526         
27527         this.xhr.onerror = function()
27528         {
27529             _this.xhrOnError(_this.xhr);
27530         }
27531         
27532         var formData = new FormData();
27533
27534         formData.append('returnHTML', 'NO');
27535         
27536         if(crop){
27537             formData.append('crop', crop);
27538         }
27539         
27540         formData.append(this.paramName, file, file.name);
27541         
27542         if(this.fireEvent('prepare', this, formData) != false){
27543             this.xhr.send(formData);
27544         };
27545     },
27546     
27547     uploadCancel : function()
27548     {
27549         if (this.xhr) {
27550             this.xhr.abort();
27551         }
27552         
27553         
27554         this.delegates = [];
27555         
27556         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27557             el.remove();
27558         }, this);
27559         
27560         this.arrange();
27561     },
27562     
27563     renderPreview : function(file)
27564     {
27565         if(typeof(file.target) != 'undefined' && file.target){
27566             return file;
27567         }
27568         
27569         var previewEl = this.managerEl.createChild({
27570             tag : 'div',
27571             cls : 'roo-document-manager-preview',
27572             cn : [
27573                 {
27574                     tag : 'div',
27575                     tooltip : file.filename,
27576                     cls : 'roo-document-manager-thumb',
27577                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27578                 },
27579                 {
27580                     tag : 'button',
27581                     cls : 'close',
27582                     html : '<i class="fa fa-times-circle"></i>'
27583                 }
27584             ]
27585         });
27586
27587         var close = previewEl.select('button.close', true).first();
27588
27589         close.on('click', this.onRemove, this, file);
27590
27591         file.target = previewEl;
27592
27593         var image = previewEl.select('img', true).first();
27594         
27595         var _this = this;
27596         
27597         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27598         
27599         image.on('click', this.onClick, this, file);
27600         
27601         return file;
27602         
27603     },
27604     
27605     onPreviewLoad : function(file, image)
27606     {
27607         if(typeof(file.target) == 'undefined' || !file.target){
27608             return;
27609         }
27610         
27611         var width = image.dom.naturalWidth || image.dom.width;
27612         var height = image.dom.naturalHeight || image.dom.height;
27613         
27614         if(width > height){
27615             file.target.addClass('wide');
27616             return;
27617         }
27618         
27619         file.target.addClass('tall');
27620         return;
27621         
27622     },
27623     
27624     uploadFromSource : function(file, crop)
27625     {
27626         this.xhr = new XMLHttpRequest();
27627         
27628         this.managerEl.createChild({
27629             tag : 'div',
27630             cls : 'roo-document-manager-loading',
27631             cn : [
27632                 {
27633                     tag : 'div',
27634                     tooltip : file.name,
27635                     cls : 'roo-document-manager-thumb',
27636                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27637                 }
27638             ]
27639
27640         });
27641
27642         this.xhr.open(this.method, this.url, true);
27643         
27644         var headers = {
27645             "Accept": "application/json",
27646             "Cache-Control": "no-cache",
27647             "X-Requested-With": "XMLHttpRequest"
27648         };
27649         
27650         for (var headerName in headers) {
27651             var headerValue = headers[headerName];
27652             if (headerValue) {
27653                 this.xhr.setRequestHeader(headerName, headerValue);
27654             }
27655         }
27656         
27657         var _this = this;
27658         
27659         this.xhr.onload = function()
27660         {
27661             _this.xhrOnLoad(_this.xhr);
27662         }
27663         
27664         this.xhr.onerror = function()
27665         {
27666             _this.xhrOnError(_this.xhr);
27667         }
27668         
27669         var formData = new FormData();
27670
27671         formData.append('returnHTML', 'NO');
27672         
27673         formData.append('crop', crop);
27674         
27675         if(typeof(file.filename) != 'undefined'){
27676             formData.append('filename', file.filename);
27677         }
27678         
27679         if(typeof(file.mimetype) != 'undefined'){
27680             formData.append('mimetype', file.mimetype);
27681         }
27682         
27683         if(this.fireEvent('prepare', this, formData) != false){
27684             this.xhr.send(formData);
27685         };
27686     }
27687 });
27688
27689 /*
27690 * Licence: LGPL
27691 */
27692
27693 /**
27694  * @class Roo.bootstrap.DocumentViewer
27695  * @extends Roo.bootstrap.Component
27696  * Bootstrap DocumentViewer class
27697  * 
27698  * @constructor
27699  * Create a new DocumentViewer
27700  * @param {Object} config The config object
27701  */
27702
27703 Roo.bootstrap.DocumentViewer = function(config){
27704     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27705     
27706     this.addEvents({
27707         /**
27708          * @event initial
27709          * Fire after initEvent
27710          * @param {Roo.bootstrap.DocumentViewer} this
27711          */
27712         "initial" : true,
27713         /**
27714          * @event click
27715          * Fire after click
27716          * @param {Roo.bootstrap.DocumentViewer} this
27717          */
27718         "click" : true,
27719         /**
27720          * @event trash
27721          * Fire after trash button
27722          * @param {Roo.bootstrap.DocumentViewer} this
27723          */
27724         "trash" : true
27725         
27726     });
27727 };
27728
27729 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27730     
27731     getAutoCreate : function()
27732     {
27733         var cfg = {
27734             tag : 'div',
27735             cls : 'roo-document-viewer',
27736             cn : [
27737                 {
27738                     tag : 'div',
27739                     cls : 'roo-document-viewer-body',
27740                     cn : [
27741                         {
27742                             tag : 'div',
27743                             cls : 'roo-document-viewer-thumb',
27744                             cn : [
27745                                 {
27746                                     tag : 'img',
27747                                     cls : 'roo-document-viewer-image'
27748                                 }
27749                             ]
27750                         }
27751                     ]
27752                 },
27753                 {
27754                     tag : 'div',
27755                     cls : 'roo-document-viewer-footer',
27756                     cn : {
27757                         tag : 'div',
27758                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27759                         cn : [
27760                             {
27761                                 tag : 'div',
27762                                 cls : 'btn-group',
27763                                 cn : [
27764                                     {
27765                                         tag : 'button',
27766                                         cls : 'btn btn-default roo-document-viewer-trash',
27767                                         html : '<i class="fa fa-trash"></i>'
27768                                     }
27769                                 ]
27770                             }
27771                         ]
27772                     }
27773                 }
27774             ]
27775         };
27776         
27777         return cfg;
27778     },
27779     
27780     initEvents : function()
27781     {
27782         
27783         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27784         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27785         
27786         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27787         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27788         
27789         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27790         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27791         
27792         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27793         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27794         
27795         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27796         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27797         
27798         this.bodyEl.on('click', this.onClick, this);
27799         
27800         this.trashBtn.on('click', this.onTrash, this);
27801         
27802     },
27803     
27804     initial : function()
27805     {
27806 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27807         
27808         
27809         this.fireEvent('initial', this);
27810         
27811     },
27812     
27813     onClick : function(e)
27814     {
27815         e.preventDefault();
27816         
27817         this.fireEvent('click', this);
27818     },
27819     
27820     onTrash : function(e)
27821     {
27822         e.preventDefault();
27823         
27824         this.fireEvent('trash', this);
27825     }
27826     
27827 });
27828 /*
27829  * - LGPL
27830  *
27831  * nav progress bar
27832  * 
27833  */
27834
27835 /**
27836  * @class Roo.bootstrap.NavProgressBar
27837  * @extends Roo.bootstrap.Component
27838  * Bootstrap NavProgressBar class
27839  * 
27840  * @constructor
27841  * Create a new nav progress bar
27842  * @param {Object} config The config object
27843  */
27844
27845 Roo.bootstrap.NavProgressBar = function(config){
27846     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27847
27848     this.bullets = this.bullets || [];
27849    
27850 //    Roo.bootstrap.NavProgressBar.register(this);
27851      this.addEvents({
27852         /**
27853              * @event changed
27854              * Fires when the active item changes
27855              * @param {Roo.bootstrap.NavProgressBar} this
27856              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27857              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27858          */
27859         'changed': true
27860      });
27861     
27862 };
27863
27864 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27865     
27866     bullets : [],
27867     barItems : [],
27868     
27869     getAutoCreate : function()
27870     {
27871         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27872         
27873         cfg = {
27874             tag : 'div',
27875             cls : 'roo-navigation-bar-group',
27876             cn : [
27877                 {
27878                     tag : 'div',
27879                     cls : 'roo-navigation-top-bar'
27880                 },
27881                 {
27882                     tag : 'div',
27883                     cls : 'roo-navigation-bullets-bar',
27884                     cn : [
27885                         {
27886                             tag : 'ul',
27887                             cls : 'roo-navigation-bar'
27888                         }
27889                     ]
27890                 },
27891                 
27892                 {
27893                     tag : 'div',
27894                     cls : 'roo-navigation-bottom-bar'
27895                 }
27896             ]
27897             
27898         };
27899         
27900         return cfg;
27901         
27902     },
27903     
27904     initEvents: function() 
27905     {
27906         
27907     },
27908     
27909     onRender : function(ct, position) 
27910     {
27911         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27912         
27913         if(this.bullets.length){
27914             Roo.each(this.bullets, function(b){
27915                this.addItem(b);
27916             }, this);
27917         }
27918         
27919         this.format();
27920         
27921     },
27922     
27923     addItem : function(cfg)
27924     {
27925         var item = new Roo.bootstrap.NavProgressItem(cfg);
27926         
27927         item.parentId = this.id;
27928         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27929         
27930         if(cfg.html){
27931             var top = new Roo.bootstrap.Element({
27932                 tag : 'div',
27933                 cls : 'roo-navigation-bar-text'
27934             });
27935             
27936             var bottom = new Roo.bootstrap.Element({
27937                 tag : 'div',
27938                 cls : 'roo-navigation-bar-text'
27939             });
27940             
27941             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27942             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27943             
27944             var topText = new Roo.bootstrap.Element({
27945                 tag : 'span',
27946                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27947             });
27948             
27949             var bottomText = new Roo.bootstrap.Element({
27950                 tag : 'span',
27951                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27952             });
27953             
27954             topText.onRender(top.el, null);
27955             bottomText.onRender(bottom.el, null);
27956             
27957             item.topEl = top;
27958             item.bottomEl = bottom;
27959         }
27960         
27961         this.barItems.push(item);
27962         
27963         return item;
27964     },
27965     
27966     getActive : function()
27967     {
27968         var active = false;
27969         
27970         Roo.each(this.barItems, function(v){
27971             
27972             if (!v.isActive()) {
27973                 return;
27974             }
27975             
27976             active = v;
27977             return false;
27978             
27979         });
27980         
27981         return active;
27982     },
27983     
27984     setActiveItem : function(item)
27985     {
27986         var prev = false;
27987         
27988         Roo.each(this.barItems, function(v){
27989             if (v.rid == item.rid) {
27990                 return ;
27991             }
27992             
27993             if (v.isActive()) {
27994                 v.setActive(false);
27995                 prev = v;
27996             }
27997         });
27998
27999         item.setActive(true);
28000         
28001         this.fireEvent('changed', this, item, prev);
28002     },
28003     
28004     getBarItem: function(rid)
28005     {
28006         var ret = false;
28007         
28008         Roo.each(this.barItems, function(e) {
28009             if (e.rid != rid) {
28010                 return;
28011             }
28012             
28013             ret =  e;
28014             return false;
28015         });
28016         
28017         return ret;
28018     },
28019     
28020     indexOfItem : function(item)
28021     {
28022         var index = false;
28023         
28024         Roo.each(this.barItems, function(v, i){
28025             
28026             if (v.rid != item.rid) {
28027                 return;
28028             }
28029             
28030             index = i;
28031             return false
28032         });
28033         
28034         return index;
28035     },
28036     
28037     setActiveNext : function()
28038     {
28039         var i = this.indexOfItem(this.getActive());
28040         
28041         if (i > this.barItems.length) {
28042             return;
28043         }
28044         
28045         this.setActiveItem(this.barItems[i+1]);
28046     },
28047     
28048     setActivePrev : function()
28049     {
28050         var i = this.indexOfItem(this.getActive());
28051         
28052         if (i  < 1) {
28053             return;
28054         }
28055         
28056         this.setActiveItem(this.barItems[i-1]);
28057     },
28058     
28059     format : function()
28060     {
28061         if(!this.barItems.length){
28062             return;
28063         }
28064      
28065         var width = 100 / this.barItems.length;
28066         
28067         Roo.each(this.barItems, function(i){
28068             i.el.setStyle('width', width + '%');
28069             i.topEl.el.setStyle('width', width + '%');
28070             i.bottomEl.el.setStyle('width', width + '%');
28071         }, this);
28072         
28073     }
28074     
28075 });
28076 /*
28077  * - LGPL
28078  *
28079  * Nav Progress Item
28080  * 
28081  */
28082
28083 /**
28084  * @class Roo.bootstrap.NavProgressItem
28085  * @extends Roo.bootstrap.Component
28086  * Bootstrap NavProgressItem class
28087  * @cfg {String} rid the reference id
28088  * @cfg {Boolean} active (true|false) Is item active default false
28089  * @cfg {Boolean} disabled (true|false) Is item active default false
28090  * @cfg {String} html
28091  * @cfg {String} position (top|bottom) text position default bottom
28092  * @cfg {String} icon show icon instead of number
28093  * 
28094  * @constructor
28095  * Create a new NavProgressItem
28096  * @param {Object} config The config object
28097  */
28098 Roo.bootstrap.NavProgressItem = function(config){
28099     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28100     this.addEvents({
28101         // raw events
28102         /**
28103          * @event click
28104          * The raw click event for the entire grid.
28105          * @param {Roo.bootstrap.NavProgressItem} this
28106          * @param {Roo.EventObject} e
28107          */
28108         "click" : true
28109     });
28110    
28111 };
28112
28113 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28114     
28115     rid : '',
28116     active : false,
28117     disabled : false,
28118     html : '',
28119     position : 'bottom',
28120     icon : false,
28121     
28122     getAutoCreate : function()
28123     {
28124         var iconCls = 'roo-navigation-bar-item-icon';
28125         
28126         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28127         
28128         var cfg = {
28129             tag: 'li',
28130             cls: 'roo-navigation-bar-item',
28131             cn : [
28132                 {
28133                     tag : 'i',
28134                     cls : iconCls
28135                 }
28136             ]
28137         };
28138         
28139         if(this.active){
28140             cfg.cls += ' active';
28141         }
28142         if(this.disabled){
28143             cfg.cls += ' disabled';
28144         }
28145         
28146         return cfg;
28147     },
28148     
28149     disable : function()
28150     {
28151         this.setDisabled(true);
28152     },
28153     
28154     enable : function()
28155     {
28156         this.setDisabled(false);
28157     },
28158     
28159     initEvents: function() 
28160     {
28161         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28162         
28163         this.iconEl.on('click', this.onClick, this);
28164     },
28165     
28166     onClick : function(e)
28167     {
28168         e.preventDefault();
28169         
28170         if(this.disabled){
28171             return;
28172         }
28173         
28174         if(this.fireEvent('click', this, e) === false){
28175             return;
28176         };
28177         
28178         this.parent().setActiveItem(this);
28179     },
28180     
28181     isActive: function () 
28182     {
28183         return this.active;
28184     },
28185     
28186     setActive : function(state)
28187     {
28188         if(this.active == state){
28189             return;
28190         }
28191         
28192         this.active = state;
28193         
28194         if (state) {
28195             this.el.addClass('active');
28196             return;
28197         }
28198         
28199         this.el.removeClass('active');
28200         
28201         return;
28202     },
28203     
28204     setDisabled : function(state)
28205     {
28206         if(this.disabled == state){
28207             return;
28208         }
28209         
28210         this.disabled = state;
28211         
28212         if (state) {
28213             this.el.addClass('disabled');
28214             return;
28215         }
28216         
28217         this.el.removeClass('disabled');
28218     },
28219     
28220     tooltipEl : function()
28221     {
28222         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28223     }
28224 });
28225  
28226
28227  /*
28228  * - LGPL
28229  *
28230  * FieldLabel
28231  * 
28232  */
28233
28234 /**
28235  * @class Roo.bootstrap.FieldLabel
28236  * @extends Roo.bootstrap.Component
28237  * Bootstrap FieldLabel class
28238  * @cfg {String} html contents of the element
28239  * @cfg {String} tag tag of the element default label
28240  * @cfg {String} cls class of the element
28241  * @cfg {String} target label target 
28242  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28243  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28244  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28245  * @cfg {String} iconTooltip default "This field is required"
28246  * 
28247  * @constructor
28248  * Create a new FieldLabel
28249  * @param {Object} config The config object
28250  */
28251
28252 Roo.bootstrap.FieldLabel = function(config){
28253     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28254     
28255     this.addEvents({
28256             /**
28257              * @event invalid
28258              * Fires after the field has been marked as invalid.
28259              * @param {Roo.form.FieldLabel} this
28260              * @param {String} msg The validation message
28261              */
28262             invalid : true,
28263             /**
28264              * @event valid
28265              * Fires after the field has been validated with no errors.
28266              * @param {Roo.form.FieldLabel} this
28267              */
28268             valid : true
28269         });
28270 };
28271
28272 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28273     
28274     tag: 'label',
28275     cls: '',
28276     html: '',
28277     target: '',
28278     allowBlank : true,
28279     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28280     validClass : 'text-success fa fa-lg fa-check',
28281     iconTooltip : 'This field is required',
28282     
28283     getAutoCreate : function(){
28284         
28285         var cfg = {
28286             tag : this.tag,
28287             cls : 'roo-bootstrap-field-label ' + this.cls,
28288             for : this.target,
28289             cn : [
28290                 {
28291                     tag : 'i',
28292                     cls : '',
28293                     tooltip : this.iconTooltip
28294                 },
28295                 {
28296                     tag : 'span',
28297                     html : this.html
28298                 }
28299             ] 
28300         };
28301         
28302         return cfg;
28303     },
28304     
28305     initEvents: function() 
28306     {
28307         Roo.bootstrap.Element.superclass.initEvents.call(this);
28308         
28309         this.iconEl = this.el.select('i', true).first();
28310         
28311         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28312         
28313         Roo.bootstrap.FieldLabel.register(this);
28314     },
28315     
28316     /**
28317      * Mark this field as valid
28318      */
28319     markValid : function()
28320     {
28321         this.iconEl.show();
28322         
28323         this.iconEl.removeClass(this.invalidClass);
28324         
28325         this.iconEl.addClass(this.validClass);
28326         
28327         this.fireEvent('valid', this);
28328     },
28329     
28330     /**
28331      * Mark this field as invalid
28332      * @param {String} msg The validation message
28333      */
28334     markInvalid : function(msg)
28335     {
28336         this.iconEl.show();
28337         
28338         this.iconEl.removeClass(this.validClass);
28339         
28340         this.iconEl.addClass(this.invalidClass);
28341         
28342         this.fireEvent('invalid', this, msg);
28343     }
28344     
28345    
28346 });
28347
28348 Roo.apply(Roo.bootstrap.FieldLabel, {
28349     
28350     groups: {},
28351     
28352      /**
28353     * register a FieldLabel Group
28354     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28355     */
28356     register : function(label)
28357     {
28358         if(this.groups.hasOwnProperty(label.target)){
28359             return;
28360         }
28361      
28362         this.groups[label.target] = label;
28363         
28364     },
28365     /**
28366     * fetch a FieldLabel Group based on the target
28367     * @param {string} target
28368     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28369     */
28370     get: function(target) {
28371         if (typeof(this.groups[target]) == 'undefined') {
28372             return false;
28373         }
28374         
28375         return this.groups[target] ;
28376     }
28377 });
28378
28379  
28380
28381  /*
28382  * - LGPL
28383  *
28384  * page DateSplitField.
28385  * 
28386  */
28387
28388
28389 /**
28390  * @class Roo.bootstrap.DateSplitField
28391  * @extends Roo.bootstrap.Component
28392  * Bootstrap DateSplitField class
28393  * @cfg {string} fieldLabel - the label associated
28394  * @cfg {Number} labelWidth set the width of label (0-12)
28395  * @cfg {String} labelAlign (top|left)
28396  * @cfg {Boolean} dayAllowBlank (true|false) default false
28397  * @cfg {Boolean} monthAllowBlank (true|false) default false
28398  * @cfg {Boolean} yearAllowBlank (true|false) default false
28399  * @cfg {string} dayPlaceholder 
28400  * @cfg {string} monthPlaceholder
28401  * @cfg {string} yearPlaceholder
28402  * @cfg {string} dayFormat default 'd'
28403  * @cfg {string} monthFormat default 'm'
28404  * @cfg {string} yearFormat default 'Y'
28405
28406  *     
28407  * @constructor
28408  * Create a new DateSplitField
28409  * @param {Object} config The config object
28410  */
28411
28412 Roo.bootstrap.DateSplitField = function(config){
28413     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28414     
28415     this.addEvents({
28416         // raw events
28417          /**
28418          * @event years
28419          * getting the data of years
28420          * @param {Roo.bootstrap.DateSplitField} this
28421          * @param {Object} years
28422          */
28423         "years" : true,
28424         /**
28425          * @event days
28426          * getting the data of days
28427          * @param {Roo.bootstrap.DateSplitField} this
28428          * @param {Object} days
28429          */
28430         "days" : true,
28431         /**
28432          * @event invalid
28433          * Fires after the field has been marked as invalid.
28434          * @param {Roo.form.Field} this
28435          * @param {String} msg The validation message
28436          */
28437         invalid : true,
28438        /**
28439          * @event valid
28440          * Fires after the field has been validated with no errors.
28441          * @param {Roo.form.Field} this
28442          */
28443         valid : true
28444     });
28445 };
28446
28447 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28448     
28449     fieldLabel : '',
28450     labelAlign : 'top',
28451     labelWidth : 3,
28452     dayAllowBlank : false,
28453     monthAllowBlank : false,
28454     yearAllowBlank : false,
28455     dayPlaceholder : '',
28456     monthPlaceholder : '',
28457     yearPlaceholder : '',
28458     dayFormat : 'd',
28459     monthFormat : 'm',
28460     yearFormat : 'Y',
28461     isFormField : true,
28462     
28463     getAutoCreate : function()
28464     {
28465         var cfg = {
28466             tag : 'div',
28467             cls : 'row roo-date-split-field-group',
28468             cn : [
28469                 {
28470                     tag : 'input',
28471                     type : 'hidden',
28472                     cls : 'form-hidden-field roo-date-split-field-group-value',
28473                     name : this.name
28474                 }
28475             ]
28476         };
28477         
28478         if(this.fieldLabel){
28479             cfg.cn.push({
28480                 tag : 'div',
28481                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28482                 cn : [
28483                     {
28484                         tag : 'label',
28485                         html : this.fieldLabel
28486                     }
28487                 ]
28488             });
28489         }
28490         
28491         Roo.each(['day', 'month', 'year'], function(t){
28492             cfg.cn.push({
28493                 tag : 'div',
28494                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28495             });
28496         }, this);
28497         
28498         return cfg;
28499     },
28500     
28501     inputEl: function ()
28502     {
28503         return this.el.select('.roo-date-split-field-group-value', true).first();
28504     },
28505     
28506     onRender : function(ct, position) 
28507     {
28508         var _this = this;
28509         
28510         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28511         
28512         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28513         
28514         this.dayField = new Roo.bootstrap.ComboBox({
28515             allowBlank : this.dayAllowBlank,
28516             alwaysQuery : true,
28517             displayField : 'value',
28518             editable : false,
28519             fieldLabel : '',
28520             forceSelection : true,
28521             mode : 'local',
28522             placeholder : this.dayPlaceholder,
28523             selectOnFocus : true,
28524             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28525             triggerAction : 'all',
28526             typeAhead : true,
28527             valueField : 'value',
28528             store : new Roo.data.SimpleStore({
28529                 data : (function() {    
28530                     var days = [];
28531                     _this.fireEvent('days', _this, days);
28532                     return days;
28533                 })(),
28534                 fields : [ 'value' ]
28535             }),
28536             listeners : {
28537                 select : function (_self, record, index)
28538                 {
28539                     _this.setValue(_this.getValue());
28540                 }
28541             }
28542         });
28543
28544         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28545         
28546         this.monthField = new Roo.bootstrap.MonthField({
28547             after : '<i class=\"fa fa-calendar\"></i>',
28548             allowBlank : this.monthAllowBlank,
28549             placeholder : this.monthPlaceholder,
28550             readOnly : true,
28551             listeners : {
28552                 render : function (_self)
28553                 {
28554                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28555                         e.preventDefault();
28556                         _self.focus();
28557                     });
28558                 },
28559                 select : function (_self, oldvalue, newvalue)
28560                 {
28561                     _this.setValue(_this.getValue());
28562                 }
28563             }
28564         });
28565         
28566         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28567         
28568         this.yearField = new Roo.bootstrap.ComboBox({
28569             allowBlank : this.yearAllowBlank,
28570             alwaysQuery : true,
28571             displayField : 'value',
28572             editable : false,
28573             fieldLabel : '',
28574             forceSelection : true,
28575             mode : 'local',
28576             placeholder : this.yearPlaceholder,
28577             selectOnFocus : true,
28578             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28579             triggerAction : 'all',
28580             typeAhead : true,
28581             valueField : 'value',
28582             store : new Roo.data.SimpleStore({
28583                 data : (function() {
28584                     var years = [];
28585                     _this.fireEvent('years', _this, years);
28586                     return years;
28587                 })(),
28588                 fields : [ 'value' ]
28589             }),
28590             listeners : {
28591                 select : function (_self, record, index)
28592                 {
28593                     _this.setValue(_this.getValue());
28594                 }
28595             }
28596         });
28597
28598         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28599     },
28600     
28601     setValue : function(v, format)
28602     {
28603         this.inputEl.dom.value = v;
28604         
28605         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28606         
28607         var d = Date.parseDate(v, f);
28608         
28609         if(!d){
28610             this.validate();
28611             return;
28612         }
28613         
28614         this.setDay(d.format(this.dayFormat));
28615         this.setMonth(d.format(this.monthFormat));
28616         this.setYear(d.format(this.yearFormat));
28617         
28618         this.validate();
28619         
28620         return;
28621     },
28622     
28623     setDay : function(v)
28624     {
28625         this.dayField.setValue(v);
28626         this.inputEl.dom.value = this.getValue();
28627         this.validate();
28628         return;
28629     },
28630     
28631     setMonth : function(v)
28632     {
28633         this.monthField.setValue(v, true);
28634         this.inputEl.dom.value = this.getValue();
28635         this.validate();
28636         return;
28637     },
28638     
28639     setYear : function(v)
28640     {
28641         this.yearField.setValue(v);
28642         this.inputEl.dom.value = this.getValue();
28643         this.validate();
28644         return;
28645     },
28646     
28647     getDay : function()
28648     {
28649         return this.dayField.getValue();
28650     },
28651     
28652     getMonth : function()
28653     {
28654         return this.monthField.getValue();
28655     },
28656     
28657     getYear : function()
28658     {
28659         return this.yearField.getValue();
28660     },
28661     
28662     getValue : function()
28663     {
28664         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28665         
28666         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28667         
28668         return date;
28669     },
28670     
28671     reset : function()
28672     {
28673         this.setDay('');
28674         this.setMonth('');
28675         this.setYear('');
28676         this.inputEl.dom.value = '';
28677         this.validate();
28678         return;
28679     },
28680     
28681     validate : function()
28682     {
28683         var d = this.dayField.validate();
28684         var m = this.monthField.validate();
28685         var y = this.yearField.validate();
28686         
28687         var valid = true;
28688         
28689         if(
28690                 (!this.dayAllowBlank && !d) ||
28691                 (!this.monthAllowBlank && !m) ||
28692                 (!this.yearAllowBlank && !y)
28693         ){
28694             valid = false;
28695         }
28696         
28697         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28698             return valid;
28699         }
28700         
28701         if(valid){
28702             this.markValid();
28703             return valid;
28704         }
28705         
28706         this.markInvalid();
28707         
28708         return valid;
28709     },
28710     
28711     markValid : function()
28712     {
28713         
28714         var label = this.el.select('label', true).first();
28715         var icon = this.el.select('i.fa-star', true).first();
28716
28717         if(label && icon){
28718             icon.remove();
28719         }
28720         
28721         this.fireEvent('valid', this);
28722     },
28723     
28724      /**
28725      * Mark this field as invalid
28726      * @param {String} msg The validation message
28727      */
28728     markInvalid : function(msg)
28729     {
28730         
28731         var label = this.el.select('label', true).first();
28732         var icon = this.el.select('i.fa-star', true).first();
28733
28734         if(label && !icon){
28735             this.el.select('.roo-date-split-field-label', true).createChild({
28736                 tag : 'i',
28737                 cls : 'text-danger fa fa-lg fa-star',
28738                 tooltip : 'This field is required',
28739                 style : 'margin-right:5px;'
28740             }, label, true);
28741         }
28742         
28743         this.fireEvent('invalid', this, msg);
28744     },
28745     
28746     clearInvalid : function()
28747     {
28748         var label = this.el.select('label', true).first();
28749         var icon = this.el.select('i.fa-star', true).first();
28750
28751         if(label && icon){
28752             icon.remove();
28753         }
28754         
28755         this.fireEvent('valid', this);
28756     },
28757     
28758     getName: function()
28759     {
28760         return this.name;
28761     }
28762     
28763 });
28764
28765  /**
28766  *
28767  * This is based on 
28768  * http://masonry.desandro.com
28769  *
28770  * The idea is to render all the bricks based on vertical width...
28771  *
28772  * The original code extends 'outlayer' - we might need to use that....
28773  * 
28774  */
28775
28776
28777 /**
28778  * @class Roo.bootstrap.LayoutMasonry
28779  * @extends Roo.bootstrap.Component
28780  * Bootstrap Layout Masonry class
28781  * 
28782  * @constructor
28783  * Create a new Element
28784  * @param {Object} config The config object
28785  */
28786
28787 Roo.bootstrap.LayoutMasonry = function(config){
28788     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28789     
28790     this.bricks = [];
28791     
28792 };
28793
28794 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28795     
28796     /**
28797      * @cfg {Boolean} isLayoutInstant = no animation?
28798      */   
28799     isLayoutInstant : false, // needed?
28800    
28801     /**
28802      * @cfg {Number} boxWidth  width of the columns
28803      */   
28804     boxWidth : 450,
28805     
28806       /**
28807      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28808      */   
28809     boxHeight : 0,
28810     
28811     /**
28812      * @cfg {Number} padWidth padding below box..
28813      */   
28814     padWidth : 10, 
28815     
28816     /**
28817      * @cfg {Number} gutter gutter width..
28818      */   
28819     gutter : 10,
28820     
28821      /**
28822      * @cfg {Number} maxCols maximum number of columns
28823      */   
28824     
28825     maxCols: 0,
28826     
28827     /**
28828      * @cfg {Boolean} isAutoInitial defalut true
28829      */   
28830     isAutoInitial : true, 
28831     
28832     containerWidth: 0,
28833     
28834     /**
28835      * @cfg {Boolean} isHorizontal defalut false
28836      */   
28837     isHorizontal : false, 
28838
28839     currentSize : null,
28840     
28841     tag: 'div',
28842     
28843     cls: '',
28844     
28845     bricks: null, //CompositeElement
28846     
28847     cols : 1,
28848     
28849     _isLayoutInited : false,
28850     
28851 //    isAlternative : false, // only use for vertical layout...
28852     
28853     /**
28854      * @cfg {Number} alternativePadWidth padding below box..
28855      */   
28856     alternativePadWidth : 50, 
28857     
28858     getAutoCreate : function(){
28859         
28860         var cfg = {
28861             tag: this.tag,
28862             cls: 'blog-masonary-wrapper ' + this.cls,
28863             cn : {
28864                 cls : 'mas-boxes masonary'
28865             }
28866         };
28867         
28868         return cfg;
28869     },
28870     
28871     getChildContainer: function( )
28872     {
28873         if (this.boxesEl) {
28874             return this.boxesEl;
28875         }
28876         
28877         this.boxesEl = this.el.select('.mas-boxes').first();
28878         
28879         return this.boxesEl;
28880     },
28881     
28882     
28883     initEvents : function()
28884     {
28885         var _this = this;
28886         
28887         if(this.isAutoInitial){
28888             Roo.log('hook children rendered');
28889             this.on('childrenrendered', function() {
28890                 Roo.log('children rendered');
28891                 _this.initial();
28892             } ,this);
28893         }
28894     },
28895     
28896     initial : function()
28897     {
28898         this.currentSize = this.el.getBox(true);
28899         
28900         Roo.EventManager.onWindowResize(this.resize, this); 
28901
28902         if(!this.isAutoInitial){
28903             this.layout();
28904             return;
28905         }
28906         
28907         this.layout();
28908         
28909         return;
28910         //this.layout.defer(500,this);
28911         
28912     },
28913     
28914     resize : function()
28915     {
28916         Roo.log('resize');
28917         
28918         var cs = this.el.getBox(true);
28919         
28920         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28921             Roo.log("no change in with or X");
28922             return;
28923         }
28924         
28925         this.currentSize = cs;
28926         
28927         this.layout();
28928         
28929     },
28930     
28931     layout : function()
28932     {   
28933         this._resetLayout();
28934         
28935         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28936         
28937         this.layoutItems( isInstant );
28938       
28939         this._isLayoutInited = true;
28940         
28941     },
28942     
28943     _resetLayout : function()
28944     {
28945         if(this.isHorizontal){
28946             this.horizontalMeasureColumns();
28947             return;
28948         }
28949         
28950         this.verticalMeasureColumns();
28951         
28952     },
28953     
28954     verticalMeasureColumns : function()
28955     {
28956         this.getContainerWidth();
28957         
28958 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28959 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28960 //            return;
28961 //        }
28962         
28963         var boxWidth = this.boxWidth + this.padWidth;
28964         
28965         if(this.containerWidth < this.boxWidth){
28966             boxWidth = this.containerWidth
28967         }
28968         
28969         var containerWidth = this.containerWidth;
28970         
28971         var cols = Math.floor(containerWidth / boxWidth);
28972         
28973         this.cols = Math.max( cols, 1 );
28974         
28975         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28976         
28977         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28978         
28979         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28980         
28981         this.colWidth = boxWidth + avail - this.padWidth;
28982         
28983         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28984         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28985     },
28986     
28987     horizontalMeasureColumns : function()
28988     {
28989         this.getContainerWidth();
28990         
28991         var boxWidth = this.boxWidth;
28992         
28993         if(this.containerWidth < boxWidth){
28994             boxWidth = this.containerWidth;
28995         }
28996         
28997         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28998         
28999         this.el.setHeight(boxWidth);
29000         
29001     },
29002     
29003     getContainerWidth : function()
29004     {
29005         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29006     },
29007     
29008     layoutItems : function( isInstant )
29009     {
29010         var items = Roo.apply([], this.bricks);
29011         
29012         if(this.isHorizontal){
29013             this._horizontalLayoutItems( items , isInstant );
29014             return;
29015         }
29016         
29017 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29018 //            this._verticalAlternativeLayoutItems( items , isInstant );
29019 //            return;
29020 //        }
29021         
29022         this._verticalLayoutItems( items , isInstant );
29023         
29024     },
29025     
29026     _verticalLayoutItems : function ( items , isInstant)
29027     {
29028         if ( !items || !items.length ) {
29029             return;
29030         }
29031         
29032         var standard = [
29033             ['xs', 'xs', 'xs', 'tall'],
29034             ['xs', 'xs', 'tall'],
29035             ['xs', 'xs', 'sm'],
29036             ['xs', 'xs', 'xs'],
29037             ['xs', 'tall'],
29038             ['xs', 'sm'],
29039             ['xs', 'xs'],
29040             ['xs'],
29041             
29042             ['sm', 'xs', 'xs'],
29043             ['sm', 'xs'],
29044             ['sm'],
29045             
29046             ['tall', 'xs', 'xs', 'xs'],
29047             ['tall', 'xs', 'xs'],
29048             ['tall', 'xs'],
29049             ['tall']
29050             
29051         ];
29052         
29053         var queue = [];
29054         
29055         var boxes = [];
29056         
29057         var box = [];
29058         
29059         Roo.each(items, function(item, k){
29060             
29061             switch (item.size) {
29062                 // these layouts take up a full box,
29063                 case 'md' :
29064                 case 'md-left' :
29065                 case 'md-right' :
29066                 case 'wide' :
29067                     
29068                     if(box.length){
29069                         boxes.push(box);
29070                         box = [];
29071                     }
29072                     
29073                     boxes.push([item]);
29074                     
29075                     break;
29076                     
29077                 case 'xs' :
29078                 case 'sm' :
29079                 case 'tall' :
29080                     
29081                     box.push(item);
29082                     
29083                     break;
29084                 default :
29085                     break;
29086                     
29087             }
29088             
29089         }, this);
29090         
29091         if(box.length){
29092             boxes.push(box);
29093             box = [];
29094         }
29095         
29096         var filterPattern = function(box, length)
29097         {
29098             if(!box.length){
29099                 return;
29100             }
29101             
29102             var match = false;
29103             
29104             var pattern = box.slice(0, length);
29105             
29106             var format = [];
29107             
29108             Roo.each(pattern, function(i){
29109                 format.push(i.size);
29110             }, this);
29111             
29112             Roo.each(standard, function(s){
29113                 
29114                 if(String(s) != String(format)){
29115                     return;
29116                 }
29117                 
29118                 match = true;
29119                 return false;
29120                 
29121             }, this);
29122             
29123             if(!match && length == 1){
29124                 return;
29125             }
29126             
29127             if(!match){
29128                 filterPattern(box, length - 1);
29129                 return;
29130             }
29131                 
29132             queue.push(pattern);
29133
29134             box = box.slice(length, box.length);
29135
29136             filterPattern(box, 4);
29137
29138             return;
29139             
29140         }
29141         
29142         Roo.each(boxes, function(box, k){
29143             
29144             if(!box.length){
29145                 return;
29146             }
29147             
29148             if(box.length == 1){
29149                 queue.push(box);
29150                 return;
29151             }
29152             
29153             filterPattern(box, 4);
29154             
29155         }, this);
29156         
29157         this._processVerticalLayoutQueue( queue, isInstant );
29158         
29159     },
29160     
29161 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29162 //    {
29163 //        if ( !items || !items.length ) {
29164 //            return;
29165 //        }
29166 //
29167 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29168 //        
29169 //    },
29170     
29171     _horizontalLayoutItems : function ( items , isInstant)
29172     {
29173         if ( !items || !items.length || items.length < 3) {
29174             return;
29175         }
29176         
29177         items.reverse();
29178         
29179         var eItems = items.slice(0, 3);
29180         
29181         items = items.slice(3, items.length);
29182         
29183         var standard = [
29184             ['xs', 'xs', 'xs', 'wide'],
29185             ['xs', 'xs', 'wide'],
29186             ['xs', 'xs', 'sm'],
29187             ['xs', 'xs', 'xs'],
29188             ['xs', 'wide'],
29189             ['xs', 'sm'],
29190             ['xs', 'xs'],
29191             ['xs'],
29192             
29193             ['sm', 'xs', 'xs'],
29194             ['sm', 'xs'],
29195             ['sm'],
29196             
29197             ['wide', 'xs', 'xs', 'xs'],
29198             ['wide', 'xs', 'xs'],
29199             ['wide', 'xs'],
29200             ['wide'],
29201             
29202             ['wide-thin']
29203         ];
29204         
29205         var queue = [];
29206         
29207         var boxes = [];
29208         
29209         var box = [];
29210         
29211         Roo.each(items, function(item, k){
29212             
29213             switch (item.size) {
29214                 case 'md' :
29215                 case 'md-left' :
29216                 case 'md-right' :
29217                 case 'tall' :
29218                     
29219                     if(box.length){
29220                         boxes.push(box);
29221                         box = [];
29222                     }
29223                     
29224                     boxes.push([item]);
29225                     
29226                     break;
29227                     
29228                 case 'xs' :
29229                 case 'sm' :
29230                 case 'wide' :
29231                 case 'wide-thin' :
29232                     
29233                     box.push(item);
29234                     
29235                     break;
29236                 default :
29237                     break;
29238                     
29239             }
29240             
29241         }, this);
29242         
29243         if(box.length){
29244             boxes.push(box);
29245             box = [];
29246         }
29247         
29248         var filterPattern = function(box, length)
29249         {
29250             if(!box.length){
29251                 return;
29252             }
29253             
29254             var match = false;
29255             
29256             var pattern = box.slice(0, length);
29257             
29258             var format = [];
29259             
29260             Roo.each(pattern, function(i){
29261                 format.push(i.size);
29262             }, this);
29263             
29264             Roo.each(standard, function(s){
29265                 
29266                 if(String(s) != String(format)){
29267                     return;
29268                 }
29269                 
29270                 match = true;
29271                 return false;
29272                 
29273             }, this);
29274             
29275             if(!match && length == 1){
29276                 return;
29277             }
29278             
29279             if(!match){
29280                 filterPattern(box, length - 1);
29281                 return;
29282             }
29283                 
29284             queue.push(pattern);
29285
29286             box = box.slice(length, box.length);
29287
29288             filterPattern(box, 4);
29289
29290             return;
29291             
29292         }
29293         
29294         Roo.each(boxes, function(box, k){
29295             
29296             if(!box.length){
29297                 return;
29298             }
29299             
29300             if(box.length == 1){
29301                 queue.push(box);
29302                 return;
29303             }
29304             
29305             filterPattern(box, 4);
29306             
29307         }, this);
29308         
29309         
29310         var prune = [];
29311         
29312         var pos = this.el.getBox(true);
29313         
29314         var minX = pos.x;
29315         
29316         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29317         
29318         var hit_end = false;
29319         
29320         Roo.each(queue, function(box){
29321             
29322             if(hit_end){
29323                 
29324                 Roo.each(box, function(b){
29325                 
29326                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29327                     b.el.hide();
29328
29329                 }, this);
29330
29331                 return;
29332             }
29333             
29334             var mx = 0;
29335             
29336             Roo.each(box, function(b){
29337                 
29338                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29339                 b.el.show();
29340
29341                 mx = Math.max(mx, b.x);
29342                 
29343             }, this);
29344             
29345             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29346             
29347             if(maxX < minX){
29348                 
29349                 Roo.each(box, function(b){
29350                 
29351                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29352                     b.el.hide();
29353                     
29354                 }, this);
29355                 
29356                 hit_end = true;
29357                 
29358                 return;
29359             }
29360             
29361             prune.push(box);
29362             
29363         }, this);
29364         
29365         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29366     },
29367     
29368     /** Sets position of item in DOM
29369     * @param {Element} item
29370     * @param {Number} x - horizontal position
29371     * @param {Number} y - vertical position
29372     * @param {Boolean} isInstant - disables transitions
29373     */
29374     _processVerticalLayoutQueue : function( queue, isInstant )
29375     {
29376         var pos = this.el.getBox(true);
29377         var x = pos.x;
29378         var y = pos.y;
29379         var maxY = [];
29380         
29381         for (var i = 0; i < this.cols; i++){
29382             maxY[i] = pos.y;
29383         }
29384         
29385         Roo.each(queue, function(box, k){
29386             
29387             var col = k % this.cols;
29388             
29389             Roo.each(box, function(b,kk){
29390                 
29391                 b.el.position('absolute');
29392                 
29393                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29394                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29395                 
29396                 if(b.size == 'md-left' || b.size == 'md-right'){
29397                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29398                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29399                 }
29400                 
29401                 b.el.setWidth(width);
29402                 b.el.setHeight(height);
29403                 // iframe?
29404                 b.el.select('iframe',true).setSize(width,height);
29405                 
29406             }, this);
29407             
29408             for (var i = 0; i < this.cols; i++){
29409                 
29410                 if(maxY[i] < maxY[col]){
29411                     col = i;
29412                     continue;
29413                 }
29414                 
29415                 col = Math.min(col, i);
29416                 
29417             }
29418             
29419             x = pos.x + col * (this.colWidth + this.padWidth);
29420             
29421             y = maxY[col];
29422             
29423             var positions = [];
29424             
29425             switch (box.length){
29426                 case 1 :
29427                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29428                     break;
29429                 case 2 :
29430                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29431                     break;
29432                 case 3 :
29433                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29434                     break;
29435                 case 4 :
29436                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29437                     break;
29438                 default :
29439                     break;
29440             }
29441             
29442             Roo.each(box, function(b,kk){
29443                 
29444                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29445                 
29446                 var sz = b.el.getSize();
29447                 
29448                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29449                 
29450             }, this);
29451             
29452         }, this);
29453         
29454         var mY = 0;
29455         
29456         for (var i = 0; i < this.cols; i++){
29457             mY = Math.max(mY, maxY[i]);
29458         }
29459         
29460         this.el.setHeight(mY - pos.y);
29461         
29462     },
29463     
29464 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29465 //    {
29466 //        var pos = this.el.getBox(true);
29467 //        var x = pos.x;
29468 //        var y = pos.y;
29469 //        var maxX = pos.right;
29470 //        
29471 //        var maxHeight = 0;
29472 //        
29473 //        Roo.each(items, function(item, k){
29474 //            
29475 //            var c = k % 2;
29476 //            
29477 //            item.el.position('absolute');
29478 //                
29479 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29480 //
29481 //            item.el.setWidth(width);
29482 //
29483 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29484 //
29485 //            item.el.setHeight(height);
29486 //            
29487 //            if(c == 0){
29488 //                item.el.setXY([x, y], isInstant ? false : true);
29489 //            } else {
29490 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29491 //            }
29492 //            
29493 //            y = y + height + this.alternativePadWidth;
29494 //            
29495 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29496 //            
29497 //        }, this);
29498 //        
29499 //        this.el.setHeight(maxHeight);
29500 //        
29501 //    },
29502     
29503     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29504     {
29505         var pos = this.el.getBox(true);
29506         
29507         var minX = pos.x;
29508         var minY = pos.y;
29509         
29510         var maxX = pos.right;
29511         
29512         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29513         
29514         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29515         
29516         Roo.each(queue, function(box, k){
29517             
29518             Roo.each(box, function(b, kk){
29519                 
29520                 b.el.position('absolute');
29521                 
29522                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29523                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29524                 
29525                 if(b.size == 'md-left' || b.size == 'md-right'){
29526                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29527                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29528                 }
29529                 
29530                 b.el.setWidth(width);
29531                 b.el.setHeight(height);
29532                 
29533             }, this);
29534             
29535             if(!box.length){
29536                 return;
29537             }
29538             
29539             var positions = [];
29540             
29541             switch (box.length){
29542                 case 1 :
29543                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29544                     break;
29545                 case 2 :
29546                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29547                     break;
29548                 case 3 :
29549                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29550                     break;
29551                 case 4 :
29552                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29553                     break;
29554                 default :
29555                     break;
29556             }
29557             
29558             Roo.each(box, function(b,kk){
29559                 
29560                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29561                 
29562                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29563                 
29564             }, this);
29565             
29566         }, this);
29567         
29568     },
29569     
29570     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29571     {
29572         Roo.each(eItems, function(b,k){
29573             
29574             b.size = (k == 0) ? 'sm' : 'xs';
29575             b.x = (k == 0) ? 2 : 1;
29576             b.y = (k == 0) ? 2 : 1;
29577             
29578             b.el.position('absolute');
29579             
29580             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29581                 
29582             b.el.setWidth(width);
29583             
29584             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29585             
29586             b.el.setHeight(height);
29587             
29588         }, this);
29589
29590         var positions = [];
29591         
29592         positions.push({
29593             x : maxX - this.unitWidth * 2 - this.gutter,
29594             y : minY
29595         });
29596         
29597         positions.push({
29598             x : maxX - this.unitWidth,
29599             y : minY + (this.unitWidth + this.gutter) * 2
29600         });
29601         
29602         positions.push({
29603             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29604             y : minY
29605         });
29606         
29607         Roo.each(eItems, function(b,k){
29608             
29609             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29610
29611         }, this);
29612         
29613     },
29614     
29615     getVerticalOneBoxColPositions : function(x, y, box)
29616     {
29617         var pos = [];
29618         
29619         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29620         
29621         if(box[0].size == 'md-left'){
29622             rand = 0;
29623         }
29624         
29625         if(box[0].size == 'md-right'){
29626             rand = 1;
29627         }
29628         
29629         pos.push({
29630             x : x + (this.unitWidth + this.gutter) * rand,
29631             y : y
29632         });
29633         
29634         return pos;
29635     },
29636     
29637     getVerticalTwoBoxColPositions : function(x, y, box)
29638     {
29639         var pos = [];
29640         
29641         if(box[0].size == 'xs'){
29642             
29643             pos.push({
29644                 x : x,
29645                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29646             });
29647
29648             pos.push({
29649                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29650                 y : y
29651             });
29652             
29653             return pos;
29654             
29655         }
29656         
29657         pos.push({
29658             x : x,
29659             y : y
29660         });
29661
29662         pos.push({
29663             x : x + (this.unitWidth + this.gutter) * 2,
29664             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29665         });
29666         
29667         return pos;
29668         
29669     },
29670     
29671     getVerticalThreeBoxColPositions : function(x, y, box)
29672     {
29673         var pos = [];
29674         
29675         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29676             
29677             pos.push({
29678                 x : x,
29679                 y : y
29680             });
29681
29682             pos.push({
29683                 x : x + (this.unitWidth + this.gutter) * 1,
29684                 y : y
29685             });
29686             
29687             pos.push({
29688                 x : x + (this.unitWidth + this.gutter) * 2,
29689                 y : y
29690             });
29691             
29692             return pos;
29693             
29694         }
29695         
29696         if(box[0].size == 'xs' && box[1].size == 'xs'){
29697             
29698             pos.push({
29699                 x : x,
29700                 y : y
29701             });
29702
29703             pos.push({
29704                 x : x,
29705                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29706             });
29707             
29708             pos.push({
29709                 x : x + (this.unitWidth + this.gutter) * 1,
29710                 y : y
29711             });
29712             
29713             return pos;
29714             
29715         }
29716         
29717         pos.push({
29718             x : x,
29719             y : y
29720         });
29721
29722         pos.push({
29723             x : x + (this.unitWidth + this.gutter) * 2,
29724             y : y
29725         });
29726
29727         pos.push({
29728             x : x + (this.unitWidth + this.gutter) * 2,
29729             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29730         });
29731             
29732         return pos;
29733         
29734     },
29735     
29736     getVerticalFourBoxColPositions : function(x, y, box)
29737     {
29738         var pos = [];
29739         
29740         if(box[0].size == 'xs'){
29741             
29742             pos.push({
29743                 x : x,
29744                 y : y
29745             });
29746
29747             pos.push({
29748                 x : x,
29749                 y : y + (this.unitHeight + this.gutter) * 1
29750             });
29751             
29752             pos.push({
29753                 x : x,
29754                 y : y + (this.unitHeight + this.gutter) * 2
29755             });
29756             
29757             pos.push({
29758                 x : x + (this.unitWidth + this.gutter) * 1,
29759                 y : y
29760             });
29761             
29762             return pos;
29763             
29764         }
29765         
29766         pos.push({
29767             x : x,
29768             y : y
29769         });
29770
29771         pos.push({
29772             x : x + (this.unitWidth + this.gutter) * 2,
29773             y : y
29774         });
29775
29776         pos.push({
29777             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29778             y : y + (this.unitHeight + this.gutter) * 1
29779         });
29780
29781         pos.push({
29782             x : x + (this.unitWidth + this.gutter) * 2,
29783             y : y + (this.unitWidth + this.gutter) * 2
29784         });
29785
29786         return pos;
29787         
29788     },
29789     
29790     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29791     {
29792         var pos = [];
29793         
29794         if(box[0].size == 'md-left'){
29795             pos.push({
29796                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29797                 y : minY
29798             });
29799             
29800             return pos;
29801         }
29802         
29803         if(box[0].size == 'md-right'){
29804             pos.push({
29805                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29806                 y : minY + (this.unitWidth + this.gutter) * 1
29807             });
29808             
29809             return pos;
29810         }
29811         
29812         var rand = Math.floor(Math.random() * (4 - box[0].y));
29813         
29814         pos.push({
29815             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29816             y : minY + (this.unitWidth + this.gutter) * rand
29817         });
29818         
29819         return pos;
29820         
29821     },
29822     
29823     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29824     {
29825         var pos = [];
29826         
29827         if(box[0].size == 'xs'){
29828             
29829             pos.push({
29830                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29831                 y : minY
29832             });
29833
29834             pos.push({
29835                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29836                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29837             });
29838             
29839             return pos;
29840             
29841         }
29842         
29843         pos.push({
29844             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29845             y : minY
29846         });
29847
29848         pos.push({
29849             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29850             y : minY + (this.unitWidth + this.gutter) * 2
29851         });
29852         
29853         return pos;
29854         
29855     },
29856     
29857     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29858     {
29859         var pos = [];
29860         
29861         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29862             
29863             pos.push({
29864                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29865                 y : minY
29866             });
29867
29868             pos.push({
29869                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29870                 y : minY + (this.unitWidth + this.gutter) * 1
29871             });
29872             
29873             pos.push({
29874                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29875                 y : minY + (this.unitWidth + this.gutter) * 2
29876             });
29877             
29878             return pos;
29879             
29880         }
29881         
29882         if(box[0].size == 'xs' && box[1].size == 'xs'){
29883             
29884             pos.push({
29885                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29886                 y : minY
29887             });
29888
29889             pos.push({
29890                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29891                 y : minY
29892             });
29893             
29894             pos.push({
29895                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29896                 y : minY + (this.unitWidth + this.gutter) * 1
29897             });
29898             
29899             return pos;
29900             
29901         }
29902         
29903         pos.push({
29904             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29905             y : minY
29906         });
29907
29908         pos.push({
29909             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29910             y : minY + (this.unitWidth + this.gutter) * 2
29911         });
29912
29913         pos.push({
29914             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29915             y : minY + (this.unitWidth + this.gutter) * 2
29916         });
29917             
29918         return pos;
29919         
29920     },
29921     
29922     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29923     {
29924         var pos = [];
29925         
29926         if(box[0].size == 'xs'){
29927             
29928             pos.push({
29929                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29930                 y : minY
29931             });
29932
29933             pos.push({
29934                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29935                 y : minY
29936             });
29937             
29938             pos.push({
29939                 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),
29940                 y : minY
29941             });
29942             
29943             pos.push({
29944                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29945                 y : minY + (this.unitWidth + this.gutter) * 1
29946             });
29947             
29948             return pos;
29949             
29950         }
29951         
29952         pos.push({
29953             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29954             y : minY
29955         });
29956         
29957         pos.push({
29958             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29959             y : minY + (this.unitWidth + this.gutter) * 2
29960         });
29961         
29962         pos.push({
29963             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29964             y : minY + (this.unitWidth + this.gutter) * 2
29965         });
29966         
29967         pos.push({
29968             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),
29969             y : minY + (this.unitWidth + this.gutter) * 2
29970         });
29971
29972         return pos;
29973         
29974     }
29975     
29976 });
29977
29978  
29979
29980  /**
29981  *
29982  * This is based on 
29983  * http://masonry.desandro.com
29984  *
29985  * The idea is to render all the bricks based on vertical width...
29986  *
29987  * The original code extends 'outlayer' - we might need to use that....
29988  * 
29989  */
29990
29991
29992 /**
29993  * @class Roo.bootstrap.LayoutMasonryAuto
29994  * @extends Roo.bootstrap.Component
29995  * Bootstrap Layout Masonry class
29996  * 
29997  * @constructor
29998  * Create a new Element
29999  * @param {Object} config The config object
30000  */
30001
30002 Roo.bootstrap.LayoutMasonryAuto = function(config){
30003     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30004 };
30005
30006 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30007     
30008       /**
30009      * @cfg {Boolean} isFitWidth  - resize the width..
30010      */   
30011     isFitWidth : false,  // options..
30012     /**
30013      * @cfg {Boolean} isOriginLeft = left align?
30014      */   
30015     isOriginLeft : true,
30016     /**
30017      * @cfg {Boolean} isOriginTop = top align?
30018      */   
30019     isOriginTop : false,
30020     /**
30021      * @cfg {Boolean} isLayoutInstant = no animation?
30022      */   
30023     isLayoutInstant : false, // needed?
30024     /**
30025      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30026      */   
30027     isResizingContainer : true,
30028     /**
30029      * @cfg {Number} columnWidth  width of the columns 
30030      */   
30031     
30032     columnWidth : 0,
30033     
30034     /**
30035      * @cfg {Number} maxCols maximum number of columns
30036      */   
30037     
30038     maxCols: 0,
30039     /**
30040      * @cfg {Number} padHeight padding below box..
30041      */   
30042     
30043     padHeight : 10, 
30044     
30045     /**
30046      * @cfg {Boolean} isAutoInitial defalut true
30047      */   
30048     
30049     isAutoInitial : true, 
30050     
30051     // private?
30052     gutter : 0,
30053     
30054     containerWidth: 0,
30055     initialColumnWidth : 0,
30056     currentSize : null,
30057     
30058     colYs : null, // array.
30059     maxY : 0,
30060     padWidth: 10,
30061     
30062     
30063     tag: 'div',
30064     cls: '',
30065     bricks: null, //CompositeElement
30066     cols : 0, // array?
30067     // element : null, // wrapped now this.el
30068     _isLayoutInited : null, 
30069     
30070     
30071     getAutoCreate : function(){
30072         
30073         var cfg = {
30074             tag: this.tag,
30075             cls: 'blog-masonary-wrapper ' + this.cls,
30076             cn : {
30077                 cls : 'mas-boxes masonary'
30078             }
30079         };
30080         
30081         return cfg;
30082     },
30083     
30084     getChildContainer: function( )
30085     {
30086         if (this.boxesEl) {
30087             return this.boxesEl;
30088         }
30089         
30090         this.boxesEl = this.el.select('.mas-boxes').first();
30091         
30092         return this.boxesEl;
30093     },
30094     
30095     
30096     initEvents : function()
30097     {
30098         var _this = this;
30099         
30100         if(this.isAutoInitial){
30101             Roo.log('hook children rendered');
30102             this.on('childrenrendered', function() {
30103                 Roo.log('children rendered');
30104                 _this.initial();
30105             } ,this);
30106         }
30107         
30108     },
30109     
30110     initial : function()
30111     {
30112         this.reloadItems();
30113
30114         this.currentSize = this.el.getBox(true);
30115
30116         /// was window resize... - let's see if this works..
30117         Roo.EventManager.onWindowResize(this.resize, this); 
30118
30119         if(!this.isAutoInitial){
30120             this.layout();
30121             return;
30122         }
30123         
30124         this.layout.defer(500,this);
30125     },
30126     
30127     reloadItems: function()
30128     {
30129         this.bricks = this.el.select('.masonry-brick', true);
30130         
30131         this.bricks.each(function(b) {
30132             //Roo.log(b.getSize());
30133             if (!b.attr('originalwidth')) {
30134                 b.attr('originalwidth',  b.getSize().width);
30135             }
30136             
30137         });
30138         
30139         Roo.log(this.bricks.elements.length);
30140     },
30141     
30142     resize : function()
30143     {
30144         Roo.log('resize');
30145         var cs = this.el.getBox(true);
30146         
30147         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30148             Roo.log("no change in with or X");
30149             return;
30150         }
30151         this.currentSize = cs;
30152         this.layout();
30153     },
30154     
30155     layout : function()
30156     {
30157          Roo.log('layout');
30158         this._resetLayout();
30159         //this._manageStamps();
30160       
30161         // don't animate first layout
30162         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30163         this.layoutItems( isInstant );
30164       
30165         // flag for initalized
30166         this._isLayoutInited = true;
30167     },
30168     
30169     layoutItems : function( isInstant )
30170     {
30171         //var items = this._getItemsForLayout( this.items );
30172         // original code supports filtering layout items.. we just ignore it..
30173         
30174         this._layoutItems( this.bricks , isInstant );
30175       
30176         this._postLayout();
30177     },
30178     _layoutItems : function ( items , isInstant)
30179     {
30180        //this.fireEvent( 'layout', this, items );
30181     
30182
30183         if ( !items || !items.elements.length ) {
30184           // no items, emit event with empty array
30185             return;
30186         }
30187
30188         var queue = [];
30189         items.each(function(item) {
30190             Roo.log("layout item");
30191             Roo.log(item);
30192             // get x/y object from method
30193             var position = this._getItemLayoutPosition( item );
30194             // enqueue
30195             position.item = item;
30196             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30197             queue.push( position );
30198         }, this);
30199       
30200         this._processLayoutQueue( queue );
30201     },
30202     /** Sets position of item in DOM
30203     * @param {Element} item
30204     * @param {Number} x - horizontal position
30205     * @param {Number} y - vertical position
30206     * @param {Boolean} isInstant - disables transitions
30207     */
30208     _processLayoutQueue : function( queue )
30209     {
30210         for ( var i=0, len = queue.length; i < len; i++ ) {
30211             var obj = queue[i];
30212             obj.item.position('absolute');
30213             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30214         }
30215     },
30216       
30217     
30218     /**
30219     * Any logic you want to do after each layout,
30220     * i.e. size the container
30221     */
30222     _postLayout : function()
30223     {
30224         this.resizeContainer();
30225     },
30226     
30227     resizeContainer : function()
30228     {
30229         if ( !this.isResizingContainer ) {
30230             return;
30231         }
30232         var size = this._getContainerSize();
30233         if ( size ) {
30234             this.el.setSize(size.width,size.height);
30235             this.boxesEl.setSize(size.width,size.height);
30236         }
30237     },
30238     
30239     
30240     
30241     _resetLayout : function()
30242     {
30243         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30244         this.colWidth = this.el.getWidth();
30245         //this.gutter = this.el.getWidth(); 
30246         
30247         this.measureColumns();
30248
30249         // reset column Y
30250         var i = this.cols;
30251         this.colYs = [];
30252         while (i--) {
30253             this.colYs.push( 0 );
30254         }
30255     
30256         this.maxY = 0;
30257     },
30258
30259     measureColumns : function()
30260     {
30261         this.getContainerWidth();
30262       // if columnWidth is 0, default to outerWidth of first item
30263         if ( !this.columnWidth ) {
30264             var firstItem = this.bricks.first();
30265             Roo.log(firstItem);
30266             this.columnWidth  = this.containerWidth;
30267             if (firstItem && firstItem.attr('originalwidth') ) {
30268                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30269             }
30270             // columnWidth fall back to item of first element
30271             Roo.log("set column width?");
30272                         this.initialColumnWidth = this.columnWidth  ;
30273
30274             // if first elem has no width, default to size of container
30275             
30276         }
30277         
30278         
30279         if (this.initialColumnWidth) {
30280             this.columnWidth = this.initialColumnWidth;
30281         }
30282         
30283         
30284             
30285         // column width is fixed at the top - however if container width get's smaller we should
30286         // reduce it...
30287         
30288         // this bit calcs how man columns..
30289             
30290         var columnWidth = this.columnWidth += this.gutter;
30291       
30292         // calculate columns
30293         var containerWidth = this.containerWidth + this.gutter;
30294         
30295         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30296         // fix rounding errors, typically with gutters
30297         var excess = columnWidth - containerWidth % columnWidth;
30298         
30299         
30300         // if overshoot is less than a pixel, round up, otherwise floor it
30301         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30302         cols = Math[ mathMethod ]( cols );
30303         this.cols = Math.max( cols, 1 );
30304         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30305         
30306          // padding positioning..
30307         var totalColWidth = this.cols * this.columnWidth;
30308         var padavail = this.containerWidth - totalColWidth;
30309         // so for 2 columns - we need 3 'pads'
30310         
30311         var padNeeded = (1+this.cols) * this.padWidth;
30312         
30313         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30314         
30315         this.columnWidth += padExtra
30316         //this.padWidth = Math.floor(padavail /  ( this.cols));
30317         
30318         // adjust colum width so that padding is fixed??
30319         
30320         // we have 3 columns ... total = width * 3
30321         // we have X left over... that should be used by 
30322         
30323         //if (this.expandC) {
30324             
30325         //}
30326         
30327         
30328         
30329     },
30330     
30331     getContainerWidth : function()
30332     {
30333        /* // container is parent if fit width
30334         var container = this.isFitWidth ? this.element.parentNode : this.element;
30335         // check that this.size and size are there
30336         // IE8 triggers resize on body size change, so they might not be
30337         
30338         var size = getSize( container );  //FIXME
30339         this.containerWidth = size && size.innerWidth; //FIXME
30340         */
30341          
30342         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30343         
30344     },
30345     
30346     _getItemLayoutPosition : function( item )  // what is item?
30347     {
30348         // we resize the item to our columnWidth..
30349       
30350         item.setWidth(this.columnWidth);
30351         item.autoBoxAdjust  = false;
30352         
30353         var sz = item.getSize();
30354  
30355         // how many columns does this brick span
30356         var remainder = this.containerWidth % this.columnWidth;
30357         
30358         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30359         // round if off by 1 pixel, otherwise use ceil
30360         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30361         colSpan = Math.min( colSpan, this.cols );
30362         
30363         // normally this should be '1' as we dont' currently allow multi width columns..
30364         
30365         var colGroup = this._getColGroup( colSpan );
30366         // get the minimum Y value from the columns
30367         var minimumY = Math.min.apply( Math, colGroup );
30368         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30369         
30370         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30371          
30372         // position the brick
30373         var position = {
30374             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30375             y: this.currentSize.y + minimumY + this.padHeight
30376         };
30377         
30378         Roo.log(position);
30379         // apply setHeight to necessary columns
30380         var setHeight = minimumY + sz.height + this.padHeight;
30381         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30382         
30383         var setSpan = this.cols + 1 - colGroup.length;
30384         for ( var i = 0; i < setSpan; i++ ) {
30385           this.colYs[ shortColIndex + i ] = setHeight ;
30386         }
30387       
30388         return position;
30389     },
30390     
30391     /**
30392      * @param {Number} colSpan - number of columns the element spans
30393      * @returns {Array} colGroup
30394      */
30395     _getColGroup : function( colSpan )
30396     {
30397         if ( colSpan < 2 ) {
30398           // if brick spans only one column, use all the column Ys
30399           return this.colYs;
30400         }
30401       
30402         var colGroup = [];
30403         // how many different places could this brick fit horizontally
30404         var groupCount = this.cols + 1 - colSpan;
30405         // for each group potential horizontal position
30406         for ( var i = 0; i < groupCount; i++ ) {
30407           // make an array of colY values for that one group
30408           var groupColYs = this.colYs.slice( i, i + colSpan );
30409           // and get the max value of the array
30410           colGroup[i] = Math.max.apply( Math, groupColYs );
30411         }
30412         return colGroup;
30413     },
30414     /*
30415     _manageStamp : function( stamp )
30416     {
30417         var stampSize =  stamp.getSize();
30418         var offset = stamp.getBox();
30419         // get the columns that this stamp affects
30420         var firstX = this.isOriginLeft ? offset.x : offset.right;
30421         var lastX = firstX + stampSize.width;
30422         var firstCol = Math.floor( firstX / this.columnWidth );
30423         firstCol = Math.max( 0, firstCol );
30424         
30425         var lastCol = Math.floor( lastX / this.columnWidth );
30426         // lastCol should not go over if multiple of columnWidth #425
30427         lastCol -= lastX % this.columnWidth ? 0 : 1;
30428         lastCol = Math.min( this.cols - 1, lastCol );
30429         
30430         // set colYs to bottom of the stamp
30431         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30432             stampSize.height;
30433             
30434         for ( var i = firstCol; i <= lastCol; i++ ) {
30435           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30436         }
30437     },
30438     */
30439     
30440     _getContainerSize : function()
30441     {
30442         this.maxY = Math.max.apply( Math, this.colYs );
30443         var size = {
30444             height: this.maxY
30445         };
30446       
30447         if ( this.isFitWidth ) {
30448             size.width = this._getContainerFitWidth();
30449         }
30450       
30451         return size;
30452     },
30453     
30454     _getContainerFitWidth : function()
30455     {
30456         var unusedCols = 0;
30457         // count unused columns
30458         var i = this.cols;
30459         while ( --i ) {
30460           if ( this.colYs[i] !== 0 ) {
30461             break;
30462           }
30463           unusedCols++;
30464         }
30465         // fit container to columns that have been used
30466         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30467     },
30468     
30469     needsResizeLayout : function()
30470     {
30471         var previousWidth = this.containerWidth;
30472         this.getContainerWidth();
30473         return previousWidth !== this.containerWidth;
30474     }
30475  
30476 });
30477
30478  
30479
30480  /*
30481  * - LGPL
30482  *
30483  * element
30484  * 
30485  */
30486
30487 /**
30488  * @class Roo.bootstrap.MasonryBrick
30489  * @extends Roo.bootstrap.Component
30490  * Bootstrap MasonryBrick class
30491  * 
30492  * @constructor
30493  * Create a new MasonryBrick
30494  * @param {Object} config The config object
30495  */
30496
30497 Roo.bootstrap.MasonryBrick = function(config){
30498     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30499     
30500     this.addEvents({
30501         // raw events
30502         /**
30503          * @event click
30504          * When a MasonryBrick is clcik
30505          * @param {Roo.bootstrap.MasonryBrick} this
30506          * @param {Roo.EventObject} e
30507          */
30508         "click" : true
30509     });
30510 };
30511
30512 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30513     
30514     /**
30515      * @cfg {String} title
30516      */   
30517     title : '',
30518     /**
30519      * @cfg {String} html
30520      */   
30521     html : '',
30522     /**
30523      * @cfg {String} bgimage
30524      */   
30525     bgimage : '',
30526     /**
30527      * @cfg {String} videourl
30528      */   
30529     videourl : '',
30530     /**
30531      * @cfg {String} cls
30532      */   
30533     cls : '',
30534     /**
30535      * @cfg {String} href
30536      */   
30537     href : '',
30538     /**
30539      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30540      */   
30541     size : 'xs',
30542     
30543     /**
30544      * @cfg {String} (center|bottom) placetitle
30545      */   
30546     placetitle : '',
30547     
30548     getAutoCreate : function()
30549     {
30550         var cls = 'masonry-brick';
30551         
30552         if(this.href.length){
30553             cls += ' masonry-brick-link';
30554         }
30555         
30556         if(this.bgimage.length){
30557             cls += ' masonry-brick-image';
30558         }
30559         
30560         if(this.size){
30561             cls += ' masonry-' + this.size + '-brick';
30562         }
30563         
30564         if(this.placetitle.length){
30565             
30566             switch (this.placetitle) {
30567                 case 'center' :
30568                     cls += ' masonry-center-title';
30569                     break;
30570                 case 'bottom' :
30571                     cls += ' masonry-bottom-title';
30572                     break;
30573                 default:
30574                     break;
30575             }
30576             
30577         } else {
30578             if(!this.html.length && !this.bgimage.length){
30579                 cls += ' masonry-center-title';
30580             }
30581
30582             if(!this.html.length && this.bgimage.length){
30583                 cls += ' masonry-bottom-title';
30584             }
30585         }
30586         
30587         if(this.cls){
30588             cls += ' ' + this.cls;
30589         }
30590         
30591         var cfg = {
30592             tag: (this.href.length) ? 'a' : 'div',
30593             cls: cls,
30594             cn: [
30595                 {
30596                     tag: 'div',
30597                     cls: 'masonry-brick-paragraph',
30598                     cn: []
30599                 }
30600             ]
30601         };
30602         
30603         if(this.href.length){
30604             cfg.href = this.href;
30605         }
30606         
30607         var cn = cfg.cn[0].cn;
30608         
30609         if(this.title.length){
30610             cn.push({
30611                 tag: 'h4',
30612                 cls: 'masonry-brick-title',
30613                 html: this.title
30614             });
30615         }
30616         
30617         if(this.html.length){
30618             cn.push({
30619                 tag: 'p',
30620                 cls: 'masonry-brick-text',
30621                 html: this.html
30622             });
30623         }  
30624         if (!this.title.length && !this.html.length) {
30625             cfg.cn[0].cls += ' hide';
30626         }
30627         
30628         if(this.bgimage.length){
30629             cfg.cn.push({
30630                 tag: 'img',
30631                 cls: 'masonry-brick-image-view',
30632                 src: this.bgimage
30633             });
30634         }
30635         if(this.videourl.length){
30636             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30637             // youtube support only?
30638             cfg.cn.push({
30639                 tag: 'iframe',
30640                 cls: 'masonry-brick-image-view',
30641                 src: vurl,
30642                 frameborder : 0,
30643                 allowfullscreen : true
30644             });
30645             
30646             
30647         }
30648         return cfg;
30649         
30650     },
30651     
30652     initEvents: function() 
30653     {
30654         switch (this.size) {
30655             case 'xs' :
30656 //                this.intSize = 1;
30657                 this.x = 1;
30658                 this.y = 1;
30659                 break;
30660             case 'sm' :
30661 //                this.intSize = 2;
30662                 this.x = 2;
30663                 this.y = 2;
30664                 break;
30665             case 'md' :
30666             case 'md-left' :
30667             case 'md-right' :
30668 //                this.intSize = 3;
30669                 this.x = 3;
30670                 this.y = 3;
30671                 break;
30672             case 'tall' :
30673 //                this.intSize = 3;
30674                 this.x = 2;
30675                 this.y = 3;
30676                 break;
30677             case 'wide' :
30678 //                this.intSize = 3;
30679                 this.x = 3;
30680                 this.y = 2;
30681                 break;
30682             case 'wide-thin' :
30683 //                this.intSize = 3;
30684                 this.x = 3;
30685                 this.y = 1;
30686                 break;
30687                         
30688             default :
30689                 break;
30690         }
30691         
30692         
30693         
30694         if(Roo.isTouch){
30695             this.el.on('touchstart', this.onTouchStart, this);
30696             this.el.on('touchmove', this.onTouchMove, this);
30697             this.el.on('touchend', this.onTouchEnd, this);
30698             this.el.on('contextmenu', this.onContextMenu, this);
30699         } else {
30700             this.el.on('mouseenter'  ,this.enter, this);
30701             this.el.on('mouseleave', this.leave, this);
30702         }
30703         
30704         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30705             this.parent().bricks.push(this);   
30706         }
30707         
30708     },
30709     
30710     onClick: function(e, el)
30711     {
30712         if(!Roo.isTouch){
30713             return;
30714         }
30715         
30716         var time = this.endTimer - this.startTimer;
30717         
30718         //alert(time);
30719         
30720         if(time < 1000){
30721             return;
30722         }
30723         
30724         e.preventDefault();
30725     },
30726     
30727     enter: function(e, el)
30728     {
30729         e.preventDefault();
30730         
30731         if(this.bgimage.length && this.html.length){
30732             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30733         }
30734     },
30735     
30736     leave: function(e, el)
30737     {
30738         e.preventDefault();
30739         
30740         if(this.bgimage.length && this.html.length){
30741             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30742         }
30743     },
30744     
30745     onTouchStart: function(e, el)
30746     {
30747 //        e.preventDefault();
30748         
30749         this.touchmoved = false;
30750         
30751         if(!this.bgimage.length || !this.html.length){
30752             return;
30753         }
30754         
30755         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30756         
30757         this.timer = new Date().getTime();
30758         
30759     },
30760     
30761     onTouchMove: function(e, el)
30762     {
30763         this.touchmoved = true;
30764     },
30765     
30766     onContextMenu : function(e,el)
30767     {
30768         e.preventDefault();
30769         e.stopPropagation();
30770         return false;
30771     },
30772     
30773     onTouchEnd: function(e, el)
30774     {
30775 //        e.preventDefault();
30776         
30777         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30778         
30779             this.leave(e,el);
30780             
30781             return;
30782         }
30783         
30784         if(!this.bgimage.length || !this.html.length){
30785             
30786             if(this.href.length){
30787                 window.location.href = this.href;
30788             }
30789             
30790             return;
30791         }
30792         
30793         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30794         
30795         window.location.href = this.href;
30796     }
30797     
30798 });
30799
30800  
30801
30802  /*
30803  * - LGPL
30804  *
30805  * element
30806  * 
30807  */
30808
30809 /**
30810  * @class Roo.bootstrap.Brick
30811  * @extends Roo.bootstrap.Component
30812  * Bootstrap Brick class
30813  * 
30814  * @constructor
30815  * Create a new Brick
30816  * @param {Object} config The config object
30817  */
30818
30819 Roo.bootstrap.Brick = function(config){
30820     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30821     
30822     this.addEvents({
30823         // raw events
30824         /**
30825          * @event click
30826          * When a Brick is click
30827          * @param {Roo.bootstrap.Brick} this
30828          * @param {Roo.EventObject} e
30829          */
30830         "click" : true
30831     });
30832 };
30833
30834 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30835     
30836     /**
30837      * @cfg {String} title
30838      */   
30839     title : '',
30840     /**
30841      * @cfg {String} html
30842      */   
30843     html : '',
30844     /**
30845      * @cfg {String} bgimage
30846      */   
30847     bgimage : '',
30848     /**
30849      * @cfg {String} cls
30850      */   
30851     cls : '',
30852     /**
30853      * @cfg {String} href
30854      */   
30855     href : '',
30856     /**
30857      * @cfg {String} video
30858      */   
30859     video : '',
30860     /**
30861      * @cfg {Boolean} square
30862      */   
30863     square : true,
30864     
30865     getAutoCreate : function()
30866     {
30867         var cls = 'roo-brick';
30868         
30869         if(this.href.length){
30870             cls += ' roo-brick-link';
30871         }
30872         
30873         if(this.bgimage.length){
30874             cls += ' roo-brick-image';
30875         }
30876         
30877         if(!this.html.length && !this.bgimage.length){
30878             cls += ' roo-brick-center-title';
30879         }
30880         
30881         if(!this.html.length && this.bgimage.length){
30882             cls += ' roo-brick-bottom-title';
30883         }
30884         
30885         if(this.cls){
30886             cls += ' ' + this.cls;
30887         }
30888         
30889         var cfg = {
30890             tag: (this.href.length) ? 'a' : 'div',
30891             cls: cls,
30892             cn: [
30893                 {
30894                     tag: 'div',
30895                     cls: 'roo-brick-paragraph',
30896                     cn: []
30897                 }
30898             ]
30899         };
30900         
30901         if(this.href.length){
30902             cfg.href = this.href;
30903         }
30904         
30905         var cn = cfg.cn[0].cn;
30906         
30907         if(this.title.length){
30908             cn.push({
30909                 tag: 'h4',
30910                 cls: 'roo-brick-title',
30911                 html: this.title
30912             });
30913         }
30914         
30915         if(this.html.length){
30916             cn.push({
30917                 tag: 'p',
30918                 cls: 'roo-brick-text',
30919                 html: this.html
30920             });
30921         } else {
30922             cn.cls += ' hide';
30923         }
30924         
30925         if(this.bgimage.length){
30926             cfg.cn.push({
30927                 tag: 'img',
30928                 cls: 'roo-brick-image-view',
30929                 src: this.bgimage
30930             });
30931         }
30932         
30933         return cfg;
30934     },
30935     
30936     initEvents: function() 
30937     {
30938         if(this.title.length || this.html.length){
30939             this.el.on('mouseenter'  ,this.enter, this);
30940             this.el.on('mouseleave', this.leave, this);
30941         }
30942         
30943         
30944         Roo.EventManager.onWindowResize(this.resize, this); 
30945         
30946         this.resize();
30947     },
30948     
30949     resize : function()
30950     {
30951         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30952         
30953         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30954 //        paragraph.setHeight(paragraph.getWidth());
30955         
30956         if(this.bgimage.length){
30957             var image = this.el.select('.roo-brick-image-view', true).first();
30958             image.setWidth(paragraph.getWidth());
30959             image.setHeight(paragraph.getWidth());
30960         }
30961         
30962     },
30963     
30964     enter: function(e, el)
30965     {
30966         e.preventDefault();
30967         
30968         if(this.bgimage.length){
30969             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30970             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30971         }
30972     },
30973     
30974     leave: function(e, el)
30975     {
30976         e.preventDefault();
30977         
30978         if(this.bgimage.length){
30979             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30980             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30981         }
30982     }
30983     
30984 });
30985
30986  
30987
30988  /*
30989  * Based on:
30990  * Ext JS Library 1.1.1
30991  * Copyright(c) 2006-2007, Ext JS, LLC.
30992  *
30993  * Originally Released Under LGPL - original licence link has changed is not relivant.
30994  *
30995  * Fork - LGPL
30996  * <script type="text/javascript">
30997  */
30998
30999
31000 /**
31001  * @class Roo.bootstrap.SplitBar
31002  * @extends Roo.util.Observable
31003  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31004  * <br><br>
31005  * Usage:
31006  * <pre><code>
31007 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31008                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31009 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31010 split.minSize = 100;
31011 split.maxSize = 600;
31012 split.animate = true;
31013 split.on('moved', splitterMoved);
31014 </code></pre>
31015  * @constructor
31016  * Create a new SplitBar
31017  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31018  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31019  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31020  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31021                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31022                         position of the SplitBar).
31023  */
31024 Roo.bootstrap.SplitBar = function(cfg){
31025     
31026     /** @private */
31027     
31028     //{
31029     //  dragElement : elm
31030     //  resizingElement: el,
31031         // optional..
31032     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31033     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31034         // existingProxy ???
31035     //}
31036     
31037     this.el = Roo.get(cfg.dragElement, true);
31038     this.el.dom.unselectable = "on";
31039     /** @private */
31040     this.resizingEl = Roo.get(cfg.resizingElement, true);
31041
31042     /**
31043      * @private
31044      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31045      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31046      * @type Number
31047      */
31048     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31049     
31050     /**
31051      * The minimum size of the resizing element. (Defaults to 0)
31052      * @type Number
31053      */
31054     this.minSize = 0;
31055     
31056     /**
31057      * The maximum size of the resizing element. (Defaults to 2000)
31058      * @type Number
31059      */
31060     this.maxSize = 2000;
31061     
31062     /**
31063      * Whether to animate the transition to the new size
31064      * @type Boolean
31065      */
31066     this.animate = false;
31067     
31068     /**
31069      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31070      * @type Boolean
31071      */
31072     this.useShim = false;
31073     
31074     /** @private */
31075     this.shim = null;
31076     
31077     if(!cfg.existingProxy){
31078         /** @private */
31079         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31080     }else{
31081         this.proxy = Roo.get(cfg.existingProxy).dom;
31082     }
31083     /** @private */
31084     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31085     
31086     /** @private */
31087     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31088     
31089     /** @private */
31090     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31091     
31092     /** @private */
31093     this.dragSpecs = {};
31094     
31095     /**
31096      * @private The adapter to use to positon and resize elements
31097      */
31098     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31099     this.adapter.init(this);
31100     
31101     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31102         /** @private */
31103         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31104         this.el.addClass("roo-splitbar-h");
31105     }else{
31106         /** @private */
31107         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31108         this.el.addClass("roo-splitbar-v");
31109     }
31110     
31111     this.addEvents({
31112         /**
31113          * @event resize
31114          * Fires when the splitter is moved (alias for {@link #event-moved})
31115          * @param {Roo.bootstrap.SplitBar} this
31116          * @param {Number} newSize the new width or height
31117          */
31118         "resize" : true,
31119         /**
31120          * @event moved
31121          * Fires when the splitter is moved
31122          * @param {Roo.bootstrap.SplitBar} this
31123          * @param {Number} newSize the new width or height
31124          */
31125         "moved" : true,
31126         /**
31127          * @event beforeresize
31128          * Fires before the splitter is dragged
31129          * @param {Roo.bootstrap.SplitBar} this
31130          */
31131         "beforeresize" : true,
31132
31133         "beforeapply" : true
31134     });
31135
31136     Roo.util.Observable.call(this);
31137 };
31138
31139 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31140     onStartProxyDrag : function(x, y){
31141         this.fireEvent("beforeresize", this);
31142         if(!this.overlay){
31143             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31144             o.unselectable();
31145             o.enableDisplayMode("block");
31146             // all splitbars share the same overlay
31147             Roo.bootstrap.SplitBar.prototype.overlay = o;
31148         }
31149         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31150         this.overlay.show();
31151         Roo.get(this.proxy).setDisplayed("block");
31152         var size = this.adapter.getElementSize(this);
31153         this.activeMinSize = this.getMinimumSize();;
31154         this.activeMaxSize = this.getMaximumSize();;
31155         var c1 = size - this.activeMinSize;
31156         var c2 = Math.max(this.activeMaxSize - size, 0);
31157         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31158             this.dd.resetConstraints();
31159             this.dd.setXConstraint(
31160                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31161                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31162             );
31163             this.dd.setYConstraint(0, 0);
31164         }else{
31165             this.dd.resetConstraints();
31166             this.dd.setXConstraint(0, 0);
31167             this.dd.setYConstraint(
31168                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31169                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31170             );
31171          }
31172         this.dragSpecs.startSize = size;
31173         this.dragSpecs.startPoint = [x, y];
31174         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31175     },
31176     
31177     /** 
31178      * @private Called after the drag operation by the DDProxy
31179      */
31180     onEndProxyDrag : function(e){
31181         Roo.get(this.proxy).setDisplayed(false);
31182         var endPoint = Roo.lib.Event.getXY(e);
31183         if(this.overlay){
31184             this.overlay.hide();
31185         }
31186         var newSize;
31187         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31188             newSize = this.dragSpecs.startSize + 
31189                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31190                     endPoint[0] - this.dragSpecs.startPoint[0] :
31191                     this.dragSpecs.startPoint[0] - endPoint[0]
31192                 );
31193         }else{
31194             newSize = this.dragSpecs.startSize + 
31195                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31196                     endPoint[1] - this.dragSpecs.startPoint[1] :
31197                     this.dragSpecs.startPoint[1] - endPoint[1]
31198                 );
31199         }
31200         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31201         if(newSize != this.dragSpecs.startSize){
31202             if(this.fireEvent('beforeapply', this, newSize) !== false){
31203                 this.adapter.setElementSize(this, newSize);
31204                 this.fireEvent("moved", this, newSize);
31205                 this.fireEvent("resize", this, newSize);
31206             }
31207         }
31208     },
31209     
31210     /**
31211      * Get the adapter this SplitBar uses
31212      * @return The adapter object
31213      */
31214     getAdapter : function(){
31215         return this.adapter;
31216     },
31217     
31218     /**
31219      * Set the adapter this SplitBar uses
31220      * @param {Object} adapter A SplitBar adapter object
31221      */
31222     setAdapter : function(adapter){
31223         this.adapter = adapter;
31224         this.adapter.init(this);
31225     },
31226     
31227     /**
31228      * Gets the minimum size for the resizing element
31229      * @return {Number} The minimum size
31230      */
31231     getMinimumSize : function(){
31232         return this.minSize;
31233     },
31234     
31235     /**
31236      * Sets the minimum size for the resizing element
31237      * @param {Number} minSize The minimum size
31238      */
31239     setMinimumSize : function(minSize){
31240         this.minSize = minSize;
31241     },
31242     
31243     /**
31244      * Gets the maximum size for the resizing element
31245      * @return {Number} The maximum size
31246      */
31247     getMaximumSize : function(){
31248         return this.maxSize;
31249     },
31250     
31251     /**
31252      * Sets the maximum size for the resizing element
31253      * @param {Number} maxSize The maximum size
31254      */
31255     setMaximumSize : function(maxSize){
31256         this.maxSize = maxSize;
31257     },
31258     
31259     /**
31260      * Sets the initialize size for the resizing element
31261      * @param {Number} size The initial size
31262      */
31263     setCurrentSize : function(size){
31264         var oldAnimate = this.animate;
31265         this.animate = false;
31266         this.adapter.setElementSize(this, size);
31267         this.animate = oldAnimate;
31268     },
31269     
31270     /**
31271      * Destroy this splitbar. 
31272      * @param {Boolean} removeEl True to remove the element
31273      */
31274     destroy : function(removeEl){
31275         if(this.shim){
31276             this.shim.remove();
31277         }
31278         this.dd.unreg();
31279         this.proxy.parentNode.removeChild(this.proxy);
31280         if(removeEl){
31281             this.el.remove();
31282         }
31283     }
31284 });
31285
31286 /**
31287  * @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.
31288  */
31289 Roo.bootstrap.SplitBar.createProxy = function(dir){
31290     var proxy = new Roo.Element(document.createElement("div"));
31291     proxy.unselectable();
31292     var cls = 'roo-splitbar-proxy';
31293     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31294     document.body.appendChild(proxy.dom);
31295     return proxy.dom;
31296 };
31297
31298 /** 
31299  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31300  * Default Adapter. It assumes the splitter and resizing element are not positioned
31301  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31302  */
31303 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31304 };
31305
31306 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31307     // do nothing for now
31308     init : function(s){
31309     
31310     },
31311     /**
31312      * Called before drag operations to get the current size of the resizing element. 
31313      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31314      */
31315      getElementSize : function(s){
31316         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31317             return s.resizingEl.getWidth();
31318         }else{
31319             return s.resizingEl.getHeight();
31320         }
31321     },
31322     
31323     /**
31324      * Called after drag operations to set the size of the resizing element.
31325      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31326      * @param {Number} newSize The new size to set
31327      * @param {Function} onComplete A function to be invoked when resizing is complete
31328      */
31329     setElementSize : function(s, newSize, onComplete){
31330         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31331             if(!s.animate){
31332                 s.resizingEl.setWidth(newSize);
31333                 if(onComplete){
31334                     onComplete(s, newSize);
31335                 }
31336             }else{
31337                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31338             }
31339         }else{
31340             
31341             if(!s.animate){
31342                 s.resizingEl.setHeight(newSize);
31343                 if(onComplete){
31344                     onComplete(s, newSize);
31345                 }
31346             }else{
31347                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31348             }
31349         }
31350     }
31351 };
31352
31353 /** 
31354  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31355  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31356  * Adapter that  moves the splitter element to align with the resized sizing element. 
31357  * Used with an absolute positioned SplitBar.
31358  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31359  * document.body, make sure you assign an id to the body element.
31360  */
31361 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31362     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31363     this.container = Roo.get(container);
31364 };
31365
31366 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31367     init : function(s){
31368         this.basic.init(s);
31369     },
31370     
31371     getElementSize : function(s){
31372         return this.basic.getElementSize(s);
31373     },
31374     
31375     setElementSize : function(s, newSize, onComplete){
31376         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31377     },
31378     
31379     moveSplitter : function(s){
31380         var yes = Roo.bootstrap.SplitBar;
31381         switch(s.placement){
31382             case yes.LEFT:
31383                 s.el.setX(s.resizingEl.getRight());
31384                 break;
31385             case yes.RIGHT:
31386                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31387                 break;
31388             case yes.TOP:
31389                 s.el.setY(s.resizingEl.getBottom());
31390                 break;
31391             case yes.BOTTOM:
31392                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31393                 break;
31394         }
31395     }
31396 };
31397
31398 /**
31399  * Orientation constant - Create a vertical SplitBar
31400  * @static
31401  * @type Number
31402  */
31403 Roo.bootstrap.SplitBar.VERTICAL = 1;
31404
31405 /**
31406  * Orientation constant - Create a horizontal SplitBar
31407  * @static
31408  * @type Number
31409  */
31410 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31411
31412 /**
31413  * Placement constant - The resizing element is to the left of the splitter element
31414  * @static
31415  * @type Number
31416  */
31417 Roo.bootstrap.SplitBar.LEFT = 1;
31418
31419 /**
31420  * Placement constant - The resizing element is to the right of the splitter element
31421  * @static
31422  * @type Number
31423  */
31424 Roo.bootstrap.SplitBar.RIGHT = 2;
31425
31426 /**
31427  * Placement constant - The resizing element is positioned above the splitter element
31428  * @static
31429  * @type Number
31430  */
31431 Roo.bootstrap.SplitBar.TOP = 3;
31432
31433 /**
31434  * Placement constant - The resizing element is positioned under splitter element
31435  * @static
31436  * @type Number
31437  */
31438 Roo.bootstrap.SplitBar.BOTTOM = 4;
31439 Roo.namespace("Roo.bootstrap.layout");/*
31440  * Based on:
31441  * Ext JS Library 1.1.1
31442  * Copyright(c) 2006-2007, Ext JS, LLC.
31443  *
31444  * Originally Released Under LGPL - original licence link has changed is not relivant.
31445  *
31446  * Fork - LGPL
31447  * <script type="text/javascript">
31448  */
31449  
31450 /**
31451  * @class Roo.bootstrap.layout.Manager
31452  * @extends Roo.bootstrap.Component
31453  * Base class for layout managers.
31454  */
31455 Roo.bootstrap.layout.Manager = function(config)
31456 {
31457     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31458     
31459     
31460      
31461     
31462     
31463     /** false to disable window resize monitoring @type Boolean */
31464     this.monitorWindowResize = true;
31465     this.regions = {};
31466     this.addEvents({
31467         /**
31468          * @event layout
31469          * Fires when a layout is performed. 
31470          * @param {Roo.LayoutManager} this
31471          */
31472         "layout" : true,
31473         /**
31474          * @event regionresized
31475          * Fires when the user resizes a region. 
31476          * @param {Roo.LayoutRegion} region The resized region
31477          * @param {Number} newSize The new size (width for east/west, height for north/south)
31478          */
31479         "regionresized" : true,
31480         /**
31481          * @event regioncollapsed
31482          * Fires when a region is collapsed. 
31483          * @param {Roo.LayoutRegion} region The collapsed region
31484          */
31485         "regioncollapsed" : true,
31486         /**
31487          * @event regionexpanded
31488          * Fires when a region is expanded.  
31489          * @param {Roo.LayoutRegion} region The expanded region
31490          */
31491         "regionexpanded" : true
31492     });
31493     this.updating = false;
31494     
31495     if (config.el) {
31496         this.el = Roo.get(config.el);
31497         this.initEvents();
31498     }
31499     
31500 };
31501
31502 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31503     
31504     
31505     regions : null,
31506     
31507     monitorWindowResize : true,
31508     
31509     
31510     updating : false,
31511     
31512     
31513     onRender : function(ct, position)
31514     {
31515         if(!this.el){
31516             this.el = Roo.get(ct);
31517             this.initEvents();
31518         }
31519     },
31520     
31521     
31522     initEvents: function()
31523     {
31524         
31525         
31526         // ie scrollbar fix
31527         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31528             document.body.scroll = "no";
31529         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31530             this.el.position('relative');
31531         }
31532         this.id = this.el.id;
31533         this.el.addClass("roo-layout-container");
31534         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31535         if(this.el.dom != document.body ) {
31536             this.el.on('resize', this.layout,this);
31537             this.el.on('show', this.layout,this);
31538         }
31539
31540     },
31541     
31542     /**
31543      * Returns true if this layout is currently being updated
31544      * @return {Boolean}
31545      */
31546     isUpdating : function(){
31547         return this.updating; 
31548     },
31549     
31550     /**
31551      * Suspend the LayoutManager from doing auto-layouts while
31552      * making multiple add or remove calls
31553      */
31554     beginUpdate : function(){
31555         this.updating = true;    
31556     },
31557     
31558     /**
31559      * Restore auto-layouts and optionally disable the manager from performing a layout
31560      * @param {Boolean} noLayout true to disable a layout update 
31561      */
31562     endUpdate : function(noLayout){
31563         this.updating = false;
31564         if(!noLayout){
31565             this.layout();
31566         }    
31567     },
31568     
31569     layout: function(){
31570         // abstract...
31571     },
31572     
31573     onRegionResized : function(region, newSize){
31574         this.fireEvent("regionresized", region, newSize);
31575         this.layout();
31576     },
31577     
31578     onRegionCollapsed : function(region){
31579         this.fireEvent("regioncollapsed", region);
31580     },
31581     
31582     onRegionExpanded : function(region){
31583         this.fireEvent("regionexpanded", region);
31584     },
31585         
31586     /**
31587      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31588      * performs box-model adjustments.
31589      * @return {Object} The size as an object {width: (the width), height: (the height)}
31590      */
31591     getViewSize : function()
31592     {
31593         var size;
31594         if(this.el.dom != document.body){
31595             size = this.el.getSize();
31596         }else{
31597             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31598         }
31599         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31600         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31601         return size;
31602     },
31603     
31604     /**
31605      * Returns the Element this layout is bound to.
31606      * @return {Roo.Element}
31607      */
31608     getEl : function(){
31609         return this.el;
31610     },
31611     
31612     /**
31613      * Returns the specified region.
31614      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31615      * @return {Roo.LayoutRegion}
31616      */
31617     getRegion : function(target){
31618         return this.regions[target.toLowerCase()];
31619     },
31620     
31621     onWindowResize : function(){
31622         if(this.monitorWindowResize){
31623             this.layout();
31624         }
31625     }
31626 });/*
31627  * Based on:
31628  * Ext JS Library 1.1.1
31629  * Copyright(c) 2006-2007, Ext JS, LLC.
31630  *
31631  * Originally Released Under LGPL - original licence link has changed is not relivant.
31632  *
31633  * Fork - LGPL
31634  * <script type="text/javascript">
31635  */
31636 /**
31637  * @class Roo.bootstrap.layout.Border
31638  * @extends Roo.bootstrap.layout.Manager
31639  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31640  * please see: examples/bootstrap/nested.html<br><br>
31641  
31642 <b>The container the layout is rendered into can be either the body element or any other element.
31643 If it is not the body element, the container needs to either be an absolute positioned element,
31644 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31645 the container size if it is not the body element.</b>
31646
31647 * @constructor
31648 * Create a new Border
31649 * @param {Object} config Configuration options
31650  */
31651 Roo.bootstrap.layout.Border = function(config){
31652     config = config || {};
31653     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31654     
31655     
31656     
31657     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31658         if(config[region]){
31659             config[region].region = region;
31660             this.addRegion(config[region]);
31661         }
31662     },this);
31663     
31664 };
31665
31666 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31667
31668 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31669     /**
31670      * Creates and adds a new region if it doesn't already exist.
31671      * @param {String} target The target region key (north, south, east, west or center).
31672      * @param {Object} config The regions config object
31673      * @return {BorderLayoutRegion} The new region
31674      */
31675     addRegion : function(config)
31676     {
31677         if(!this.regions[config.region]){
31678             var r = this.factory(config);
31679             this.bindRegion(r);
31680         }
31681         return this.regions[config.region];
31682     },
31683
31684     // private (kinda)
31685     bindRegion : function(r){
31686         this.regions[r.config.region] = r;
31687         
31688         r.on("visibilitychange",    this.layout, this);
31689         r.on("paneladded",          this.layout, this);
31690         r.on("panelremoved",        this.layout, this);
31691         r.on("invalidated",         this.layout, this);
31692         r.on("resized",             this.onRegionResized, this);
31693         r.on("collapsed",           this.onRegionCollapsed, this);
31694         r.on("expanded",            this.onRegionExpanded, this);
31695     },
31696
31697     /**
31698      * Performs a layout update.
31699      */
31700     layout : function()
31701     {
31702         if(this.updating) {
31703             return;
31704         }
31705         var size = this.getViewSize();
31706         var w = size.width;
31707         var h = size.height;
31708         var centerW = w;
31709         var centerH = h;
31710         var centerY = 0;
31711         var centerX = 0;
31712         //var x = 0, y = 0;
31713
31714         var rs = this.regions;
31715         var north = rs["north"];
31716         var south = rs["south"]; 
31717         var west = rs["west"];
31718         var east = rs["east"];
31719         var center = rs["center"];
31720         //if(this.hideOnLayout){ // not supported anymore
31721             //c.el.setStyle("display", "none");
31722         //}
31723         if(north && north.isVisible()){
31724             var b = north.getBox();
31725             var m = north.getMargins();
31726             b.width = w - (m.left+m.right);
31727             b.x = m.left;
31728             b.y = m.top;
31729             centerY = b.height + b.y + m.bottom;
31730             centerH -= centerY;
31731             north.updateBox(this.safeBox(b));
31732         }
31733         if(south && south.isVisible()){
31734             var b = south.getBox();
31735             var m = south.getMargins();
31736             b.width = w - (m.left+m.right);
31737             b.x = m.left;
31738             var totalHeight = (b.height + m.top + m.bottom);
31739             b.y = h - totalHeight + m.top;
31740             centerH -= totalHeight;
31741             south.updateBox(this.safeBox(b));
31742         }
31743         if(west && west.isVisible()){
31744             var b = west.getBox();
31745             var m = west.getMargins();
31746             b.height = centerH - (m.top+m.bottom);
31747             b.x = m.left;
31748             b.y = centerY + m.top;
31749             var totalWidth = (b.width + m.left + m.right);
31750             centerX += totalWidth;
31751             centerW -= totalWidth;
31752             west.updateBox(this.safeBox(b));
31753         }
31754         if(east && east.isVisible()){
31755             var b = east.getBox();
31756             var m = east.getMargins();
31757             b.height = centerH - (m.top+m.bottom);
31758             var totalWidth = (b.width + m.left + m.right);
31759             b.x = w - totalWidth + m.left;
31760             b.y = centerY + m.top;
31761             centerW -= totalWidth;
31762             east.updateBox(this.safeBox(b));
31763         }
31764         if(center){
31765             var m = center.getMargins();
31766             var centerBox = {
31767                 x: centerX + m.left,
31768                 y: centerY + m.top,
31769                 width: centerW - (m.left+m.right),
31770                 height: centerH - (m.top+m.bottom)
31771             };
31772             //if(this.hideOnLayout){
31773                 //center.el.setStyle("display", "block");
31774             //}
31775             center.updateBox(this.safeBox(centerBox));
31776         }
31777         this.el.repaint();
31778         this.fireEvent("layout", this);
31779     },
31780
31781     // private
31782     safeBox : function(box){
31783         box.width = Math.max(0, box.width);
31784         box.height = Math.max(0, box.height);
31785         return box;
31786     },
31787
31788     /**
31789      * Adds a ContentPanel (or subclass) to this layout.
31790      * @param {String} target The target region key (north, south, east, west or center).
31791      * @param {Roo.ContentPanel} panel The panel to add
31792      * @return {Roo.ContentPanel} The added panel
31793      */
31794     add : function(target, panel){
31795          
31796         target = target.toLowerCase();
31797         return this.regions[target].add(panel);
31798     },
31799
31800     /**
31801      * Remove a ContentPanel (or subclass) to this layout.
31802      * @param {String} target The target region key (north, south, east, west or center).
31803      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31804      * @return {Roo.ContentPanel} The removed panel
31805      */
31806     remove : function(target, panel){
31807         target = target.toLowerCase();
31808         return this.regions[target].remove(panel);
31809     },
31810
31811     /**
31812      * Searches all regions for a panel with the specified id
31813      * @param {String} panelId
31814      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31815      */
31816     findPanel : function(panelId){
31817         var rs = this.regions;
31818         for(var target in rs){
31819             if(typeof rs[target] != "function"){
31820                 var p = rs[target].getPanel(panelId);
31821                 if(p){
31822                     return p;
31823                 }
31824             }
31825         }
31826         return null;
31827     },
31828
31829     /**
31830      * Searches all regions for a panel with the specified id and activates (shows) it.
31831      * @param {String/ContentPanel} panelId The panels id or the panel itself
31832      * @return {Roo.ContentPanel} The shown panel or null
31833      */
31834     showPanel : function(panelId) {
31835       var rs = this.regions;
31836       for(var target in rs){
31837          var r = rs[target];
31838          if(typeof r != "function"){
31839             if(r.hasPanel(panelId)){
31840                return r.showPanel(panelId);
31841             }
31842          }
31843       }
31844       return null;
31845    },
31846
31847    /**
31848      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31849      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31850      */
31851    /*
31852     restoreState : function(provider){
31853         if(!provider){
31854             provider = Roo.state.Manager;
31855         }
31856         var sm = new Roo.LayoutStateManager();
31857         sm.init(this, provider);
31858     },
31859 */
31860  
31861  
31862     /**
31863      * Adds a xtype elements to the layout.
31864      * <pre><code>
31865
31866 layout.addxtype({
31867        xtype : 'ContentPanel',
31868        region: 'west',
31869        items: [ .... ]
31870    }
31871 );
31872
31873 layout.addxtype({
31874         xtype : 'NestedLayoutPanel',
31875         region: 'west',
31876         layout: {
31877            center: { },
31878            west: { }   
31879         },
31880         items : [ ... list of content panels or nested layout panels.. ]
31881    }
31882 );
31883 </code></pre>
31884      * @param {Object} cfg Xtype definition of item to add.
31885      */
31886     addxtype : function(cfg)
31887     {
31888         // basically accepts a pannel...
31889         // can accept a layout region..!?!?
31890         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31891         
31892         
31893         // theory?  children can only be panels??
31894         
31895         //if (!cfg.xtype.match(/Panel$/)) {
31896         //    return false;
31897         //}
31898         var ret = false;
31899         
31900         if (typeof(cfg.region) == 'undefined') {
31901             Roo.log("Failed to add Panel, region was not set");
31902             Roo.log(cfg);
31903             return false;
31904         }
31905         var region = cfg.region;
31906         delete cfg.region;
31907         
31908           
31909         var xitems = [];
31910         if (cfg.items) {
31911             xitems = cfg.items;
31912             delete cfg.items;
31913         }
31914         var nb = false;
31915         
31916         switch(cfg.xtype) 
31917         {
31918             case 'Content':  // ContentPanel (el, cfg)
31919             case 'Scroll':  // ContentPanel (el, cfg)
31920             case 'View': 
31921                 cfg.autoCreate = true;
31922                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31923                 //} else {
31924                 //    var el = this.el.createChild();
31925                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31926                 //}
31927                 
31928                 this.add(region, ret);
31929                 break;
31930             
31931             /*
31932             case 'TreePanel': // our new panel!
31933                 cfg.el = this.el.createChild();
31934                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31935                 this.add(region, ret);
31936                 break;
31937             */
31938             
31939             case 'Nest': 
31940                 // create a new Layout (which is  a Border Layout...
31941                 
31942                 var clayout = cfg.layout;
31943                 clayout.el  = this.el.createChild();
31944                 clayout.items   = clayout.items  || [];
31945                 
31946                 delete cfg.layout;
31947                 
31948                 // replace this exitems with the clayout ones..
31949                 xitems = clayout.items;
31950                  
31951                 // force background off if it's in center...
31952                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31953                     cfg.background = false;
31954                 }
31955                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31956                 
31957                 
31958                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31959                 //console.log('adding nested layout panel '  + cfg.toSource());
31960                 this.add(region, ret);
31961                 nb = {}; /// find first...
31962                 break;
31963             
31964             case 'Grid':
31965                 
31966                 // needs grid and region
31967                 
31968                 //var el = this.getRegion(region).el.createChild();
31969                 /*
31970                  *var el = this.el.createChild();
31971                 // create the grid first...
31972                 cfg.grid.container = el;
31973                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31974                 */
31975                 
31976                 if (region == 'center' && this.active ) {
31977                     cfg.background = false;
31978                 }
31979                 
31980                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31981                 
31982                 this.add(region, ret);
31983                 /*
31984                 if (cfg.background) {
31985                     // render grid on panel activation (if panel background)
31986                     ret.on('activate', function(gp) {
31987                         if (!gp.grid.rendered) {
31988                     //        gp.grid.render(el);
31989                         }
31990                     });
31991                 } else {
31992                   //  cfg.grid.render(el);
31993                 }
31994                 */
31995                 break;
31996            
31997            
31998             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31999                 // it was the old xcomponent building that caused this before.
32000                 // espeically if border is the top element in the tree.
32001                 ret = this;
32002                 break; 
32003                 
32004                     
32005                 
32006                 
32007                 
32008             default:
32009                 /*
32010                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32011                     
32012                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32013                     this.add(region, ret);
32014                 } else {
32015                 */
32016                     Roo.log(cfg);
32017                     throw "Can not add '" + cfg.xtype + "' to Border";
32018                     return null;
32019              
32020                                 
32021              
32022         }
32023         this.beginUpdate();
32024         // add children..
32025         var region = '';
32026         var abn = {};
32027         Roo.each(xitems, function(i)  {
32028             region = nb && i.region ? i.region : false;
32029             
32030             var add = ret.addxtype(i);
32031            
32032             if (region) {
32033                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32034                 if (!i.background) {
32035                     abn[region] = nb[region] ;
32036                 }
32037             }
32038             
32039         });
32040         this.endUpdate();
32041
32042         // make the last non-background panel active..
32043         //if (nb) { Roo.log(abn); }
32044         if (nb) {
32045             
32046             for(var r in abn) {
32047                 region = this.getRegion(r);
32048                 if (region) {
32049                     // tried using nb[r], but it does not work..
32050                      
32051                     region.showPanel(abn[r]);
32052                    
32053                 }
32054             }
32055         }
32056         return ret;
32057         
32058     },
32059     
32060     
32061 // private
32062     factory : function(cfg)
32063     {
32064         
32065         var validRegions = Roo.bootstrap.layout.Border.regions;
32066
32067         var target = cfg.region;
32068         cfg.mgr = this;
32069         
32070         var r = Roo.bootstrap.layout;
32071         Roo.log(target);
32072         switch(target){
32073             case "north":
32074                 return new r.North(cfg);
32075             case "south":
32076                 return new r.South(cfg);
32077             case "east":
32078                 return new r.East(cfg);
32079             case "west":
32080                 return new r.West(cfg);
32081             case "center":
32082                 return new r.Center(cfg);
32083         }
32084         throw 'Layout region "'+target+'" not supported.';
32085     }
32086     
32087     
32088 });
32089  /*
32090  * Based on:
32091  * Ext JS Library 1.1.1
32092  * Copyright(c) 2006-2007, Ext JS, LLC.
32093  *
32094  * Originally Released Under LGPL - original licence link has changed is not relivant.
32095  *
32096  * Fork - LGPL
32097  * <script type="text/javascript">
32098  */
32099  
32100 /**
32101  * @class Roo.bootstrap.layout.Basic
32102  * @extends Roo.util.Observable
32103  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32104  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32105  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32106  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32107  * @cfg {string}   region  the region that it inhabits..
32108  * @cfg {bool}   skipConfig skip config?
32109  * 
32110
32111  */
32112 Roo.bootstrap.layout.Basic = function(config){
32113     
32114     this.mgr = config.mgr;
32115     
32116     this.position = config.region;
32117     
32118     var skipConfig = config.skipConfig;
32119     
32120     this.events = {
32121         /**
32122          * @scope Roo.BasicLayoutRegion
32123          */
32124         
32125         /**
32126          * @event beforeremove
32127          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32128          * @param {Roo.LayoutRegion} this
32129          * @param {Roo.ContentPanel} panel The panel
32130          * @param {Object} e The cancel event object
32131          */
32132         "beforeremove" : true,
32133         /**
32134          * @event invalidated
32135          * Fires when the layout for this region is changed.
32136          * @param {Roo.LayoutRegion} this
32137          */
32138         "invalidated" : true,
32139         /**
32140          * @event visibilitychange
32141          * Fires when this region is shown or hidden 
32142          * @param {Roo.LayoutRegion} this
32143          * @param {Boolean} visibility true or false
32144          */
32145         "visibilitychange" : true,
32146         /**
32147          * @event paneladded
32148          * Fires when a panel is added. 
32149          * @param {Roo.LayoutRegion} this
32150          * @param {Roo.ContentPanel} panel The panel
32151          */
32152         "paneladded" : true,
32153         /**
32154          * @event panelremoved
32155          * Fires when a panel is removed. 
32156          * @param {Roo.LayoutRegion} this
32157          * @param {Roo.ContentPanel} panel The panel
32158          */
32159         "panelremoved" : true,
32160         /**
32161          * @event beforecollapse
32162          * Fires when this region before collapse.
32163          * @param {Roo.LayoutRegion} this
32164          */
32165         "beforecollapse" : true,
32166         /**
32167          * @event collapsed
32168          * Fires when this region is collapsed.
32169          * @param {Roo.LayoutRegion} this
32170          */
32171         "collapsed" : true,
32172         /**
32173          * @event expanded
32174          * Fires when this region is expanded.
32175          * @param {Roo.LayoutRegion} this
32176          */
32177         "expanded" : true,
32178         /**
32179          * @event slideshow
32180          * Fires when this region is slid into view.
32181          * @param {Roo.LayoutRegion} this
32182          */
32183         "slideshow" : true,
32184         /**
32185          * @event slidehide
32186          * Fires when this region slides out of view. 
32187          * @param {Roo.LayoutRegion} this
32188          */
32189         "slidehide" : true,
32190         /**
32191          * @event panelactivated
32192          * Fires when a panel is activated. 
32193          * @param {Roo.LayoutRegion} this
32194          * @param {Roo.ContentPanel} panel The activated panel
32195          */
32196         "panelactivated" : true,
32197         /**
32198          * @event resized
32199          * Fires when the user resizes this region. 
32200          * @param {Roo.LayoutRegion} this
32201          * @param {Number} newSize The new size (width for east/west, height for north/south)
32202          */
32203         "resized" : true
32204     };
32205     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32206     this.panels = new Roo.util.MixedCollection();
32207     this.panels.getKey = this.getPanelId.createDelegate(this);
32208     this.box = null;
32209     this.activePanel = null;
32210     // ensure listeners are added...
32211     
32212     if (config.listeners || config.events) {
32213         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32214             listeners : config.listeners || {},
32215             events : config.events || {}
32216         });
32217     }
32218     
32219     if(skipConfig !== true){
32220         this.applyConfig(config);
32221     }
32222 };
32223
32224 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32225 {
32226     getPanelId : function(p){
32227         return p.getId();
32228     },
32229     
32230     applyConfig : function(config){
32231         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32232         this.config = config;
32233         
32234     },
32235     
32236     /**
32237      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32238      * the width, for horizontal (north, south) the height.
32239      * @param {Number} newSize The new width or height
32240      */
32241     resizeTo : function(newSize){
32242         var el = this.el ? this.el :
32243                  (this.activePanel ? this.activePanel.getEl() : null);
32244         if(el){
32245             switch(this.position){
32246                 case "east":
32247                 case "west":
32248                     el.setWidth(newSize);
32249                     this.fireEvent("resized", this, newSize);
32250                 break;
32251                 case "north":
32252                 case "south":
32253                     el.setHeight(newSize);
32254                     this.fireEvent("resized", this, newSize);
32255                 break;                
32256             }
32257         }
32258     },
32259     
32260     getBox : function(){
32261         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32262     },
32263     
32264     getMargins : function(){
32265         return this.margins;
32266     },
32267     
32268     updateBox : function(box){
32269         this.box = box;
32270         var el = this.activePanel.getEl();
32271         el.dom.style.left = box.x + "px";
32272         el.dom.style.top = box.y + "px";
32273         this.activePanel.setSize(box.width, box.height);
32274     },
32275     
32276     /**
32277      * Returns the container element for this region.
32278      * @return {Roo.Element}
32279      */
32280     getEl : function(){
32281         return this.activePanel;
32282     },
32283     
32284     /**
32285      * Returns true if this region is currently visible.
32286      * @return {Boolean}
32287      */
32288     isVisible : function(){
32289         return this.activePanel ? true : false;
32290     },
32291     
32292     setActivePanel : function(panel){
32293         panel = this.getPanel(panel);
32294         if(this.activePanel && this.activePanel != panel){
32295             this.activePanel.setActiveState(false);
32296             this.activePanel.getEl().setLeftTop(-10000,-10000);
32297         }
32298         this.activePanel = panel;
32299         panel.setActiveState(true);
32300         if(this.box){
32301             panel.setSize(this.box.width, this.box.height);
32302         }
32303         this.fireEvent("panelactivated", this, panel);
32304         this.fireEvent("invalidated");
32305     },
32306     
32307     /**
32308      * Show the specified panel.
32309      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32310      * @return {Roo.ContentPanel} The shown panel or null
32311      */
32312     showPanel : function(panel){
32313         panel = this.getPanel(panel);
32314         if(panel){
32315             this.setActivePanel(panel);
32316         }
32317         return panel;
32318     },
32319     
32320     /**
32321      * Get the active panel for this region.
32322      * @return {Roo.ContentPanel} The active panel or null
32323      */
32324     getActivePanel : function(){
32325         return this.activePanel;
32326     },
32327     
32328     /**
32329      * Add the passed ContentPanel(s)
32330      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32331      * @return {Roo.ContentPanel} The panel added (if only one was added)
32332      */
32333     add : function(panel){
32334         if(arguments.length > 1){
32335             for(var i = 0, len = arguments.length; i < len; i++) {
32336                 this.add(arguments[i]);
32337             }
32338             return null;
32339         }
32340         if(this.hasPanel(panel)){
32341             this.showPanel(panel);
32342             return panel;
32343         }
32344         var el = panel.getEl();
32345         if(el.dom.parentNode != this.mgr.el.dom){
32346             this.mgr.el.dom.appendChild(el.dom);
32347         }
32348         if(panel.setRegion){
32349             panel.setRegion(this);
32350         }
32351         this.panels.add(panel);
32352         el.setStyle("position", "absolute");
32353         if(!panel.background){
32354             this.setActivePanel(panel);
32355             if(this.config.initialSize && this.panels.getCount()==1){
32356                 this.resizeTo(this.config.initialSize);
32357             }
32358         }
32359         this.fireEvent("paneladded", this, panel);
32360         return panel;
32361     },
32362     
32363     /**
32364      * Returns true if the panel is in this region.
32365      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32366      * @return {Boolean}
32367      */
32368     hasPanel : function(panel){
32369         if(typeof panel == "object"){ // must be panel obj
32370             panel = panel.getId();
32371         }
32372         return this.getPanel(panel) ? true : false;
32373     },
32374     
32375     /**
32376      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32377      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32378      * @param {Boolean} preservePanel Overrides the config preservePanel option
32379      * @return {Roo.ContentPanel} The panel that was removed
32380      */
32381     remove : function(panel, preservePanel){
32382         panel = this.getPanel(panel);
32383         if(!panel){
32384             return null;
32385         }
32386         var e = {};
32387         this.fireEvent("beforeremove", this, panel, e);
32388         if(e.cancel === true){
32389             return null;
32390         }
32391         var panelId = panel.getId();
32392         this.panels.removeKey(panelId);
32393         return panel;
32394     },
32395     
32396     /**
32397      * Returns the panel specified or null if it's not in this region.
32398      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32399      * @return {Roo.ContentPanel}
32400      */
32401     getPanel : function(id){
32402         if(typeof id == "object"){ // must be panel obj
32403             return id;
32404         }
32405         return this.panels.get(id);
32406     },
32407     
32408     /**
32409      * Returns this regions position (north/south/east/west/center).
32410      * @return {String} 
32411      */
32412     getPosition: function(){
32413         return this.position;    
32414     }
32415 });/*
32416  * Based on:
32417  * Ext JS Library 1.1.1
32418  * Copyright(c) 2006-2007, Ext JS, LLC.
32419  *
32420  * Originally Released Under LGPL - original licence link has changed is not relivant.
32421  *
32422  * Fork - LGPL
32423  * <script type="text/javascript">
32424  */
32425  
32426 /**
32427  * @class Roo.bootstrap.layout.Region
32428  * @extends Roo.bootstrap.layout.Basic
32429  * This class represents a region in a layout manager.
32430  
32431  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32432  * @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})
32433  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32434  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32435  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32436  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32437  * @cfg {String}    title           The title for the region (overrides panel titles)
32438  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32439  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32440  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32441  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32442  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32443  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32444  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32445  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32446  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32447  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32448
32449  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32450  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32451  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32452  * @cfg {Number}    width           For East/West panels
32453  * @cfg {Number}    height          For North/South panels
32454  * @cfg {Boolean}   split           To show the splitter
32455  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32456  * 
32457  * @cfg {string}   cls             Extra CSS classes to add to region
32458  * 
32459  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32460  * @cfg {string}   region  the region that it inhabits..
32461  *
32462
32463  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32464  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32465
32466  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32467  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32468  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32469  */
32470 Roo.bootstrap.layout.Region = function(config)
32471 {
32472     this.applyConfig(config);
32473
32474     var mgr = config.mgr;
32475     var pos = config.region;
32476     config.skipConfig = true;
32477     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32478     
32479     if (mgr.el) {
32480         this.onRender(mgr.el);   
32481     }
32482      
32483     this.visible = true;
32484     this.collapsed = false;
32485 };
32486
32487 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32488
32489     position: '', // set by wrapper (eg. north/south etc..)
32490
32491     createBody : function(){
32492         /** This region's body element 
32493         * @type Roo.Element */
32494         this.bodyEl = this.el.createChild({
32495                 tag: "div",
32496                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32497         });
32498     },
32499
32500     onRender: function(ctr, pos)
32501     {
32502         var dh = Roo.DomHelper;
32503         /** This region's container element 
32504         * @type Roo.Element */
32505         this.el = dh.append(ctr.dom, {
32506                 tag: "div",
32507                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32508             }, true);
32509         /** This region's title element 
32510         * @type Roo.Element */
32511     
32512         this.titleEl = dh.append(this.el.dom,
32513             {
32514                     tag: "div",
32515                     unselectable: "on",
32516                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32517                     children:[
32518                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32519                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32520                     ]}, true);
32521         
32522         this.titleEl.enableDisplayMode();
32523         /** This region's title text element 
32524         * @type HTMLElement */
32525         this.titleTextEl = this.titleEl.dom.firstChild;
32526         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32527         /*
32528         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32529         this.closeBtn.enableDisplayMode();
32530         this.closeBtn.on("click", this.closeClicked, this);
32531         this.closeBtn.hide();
32532     */
32533         this.createBody(this.config);
32534         if(this.config.hideWhenEmpty){
32535             this.hide();
32536             this.on("paneladded", this.validateVisibility, this);
32537             this.on("panelremoved", this.validateVisibility, this);
32538         }
32539         if(this.autoScroll){
32540             this.bodyEl.setStyle("overflow", "auto");
32541         }else{
32542             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32543         }
32544         //if(c.titlebar !== false){
32545             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32546                 this.titleEl.hide();
32547             }else{
32548                 this.titleEl.show();
32549                 if(this.config.title){
32550                     this.titleTextEl.innerHTML = this.config.title;
32551                 }
32552             }
32553         //}
32554         if(this.config.collapsed){
32555             this.collapse(true);
32556         }
32557         if(this.config.hidden){
32558             this.hide();
32559         }
32560     },
32561     
32562     applyConfig : function(c)
32563     {
32564         /*
32565          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32566             var dh = Roo.DomHelper;
32567             if(c.titlebar !== false){
32568                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32569                 this.collapseBtn.on("click", this.collapse, this);
32570                 this.collapseBtn.enableDisplayMode();
32571                 /*
32572                 if(c.showPin === true || this.showPin){
32573                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32574                     this.stickBtn.enableDisplayMode();
32575                     this.stickBtn.on("click", this.expand, this);
32576                     this.stickBtn.hide();
32577                 }
32578                 
32579             }
32580             */
32581             /** This region's collapsed element
32582             * @type Roo.Element */
32583             /*
32584              *
32585             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32586                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32587             ]}, true);
32588             
32589             if(c.floatable !== false){
32590                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32591                this.collapsedEl.on("click", this.collapseClick, this);
32592             }
32593
32594             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32595                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32596                    id: "message", unselectable: "on", style:{"float":"left"}});
32597                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32598              }
32599             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32600             this.expandBtn.on("click", this.expand, this);
32601             
32602         }
32603         
32604         if(this.collapseBtn){
32605             this.collapseBtn.setVisible(c.collapsible == true);
32606         }
32607         
32608         this.cmargins = c.cmargins || this.cmargins ||
32609                          (this.position == "west" || this.position == "east" ?
32610                              {top: 0, left: 2, right:2, bottom: 0} :
32611                              {top: 2, left: 0, right:0, bottom: 2});
32612         */
32613         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32614         
32615         
32616         this.bottomTabs = c.tabPosition != "top";
32617         
32618         this.autoScroll = c.autoScroll || false;
32619         
32620         
32621        
32622         
32623         this.duration = c.duration || .30;
32624         this.slideDuration = c.slideDuration || .45;
32625         this.config = c;
32626        
32627     },
32628     /**
32629      * Returns true if this region is currently visible.
32630      * @return {Boolean}
32631      */
32632     isVisible : function(){
32633         return this.visible;
32634     },
32635
32636     /**
32637      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32638      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32639      */
32640     //setCollapsedTitle : function(title){
32641     //    title = title || "&#160;";
32642      //   if(this.collapsedTitleTextEl){
32643       //      this.collapsedTitleTextEl.innerHTML = title;
32644        // }
32645     //},
32646
32647     getBox : function(){
32648         var b;
32649       //  if(!this.collapsed){
32650             b = this.el.getBox(false, true);
32651        // }else{
32652           //  b = this.collapsedEl.getBox(false, true);
32653         //}
32654         return b;
32655     },
32656
32657     getMargins : function(){
32658         return this.margins;
32659         //return this.collapsed ? this.cmargins : this.margins;
32660     },
32661 /*
32662     highlight : function(){
32663         this.el.addClass("x-layout-panel-dragover");
32664     },
32665
32666     unhighlight : function(){
32667         this.el.removeClass("x-layout-panel-dragover");
32668     },
32669 */
32670     updateBox : function(box)
32671     {
32672         this.box = box;
32673         if(!this.collapsed){
32674             this.el.dom.style.left = box.x + "px";
32675             this.el.dom.style.top = box.y + "px";
32676             this.updateBody(box.width, box.height);
32677         }else{
32678             this.collapsedEl.dom.style.left = box.x + "px";
32679             this.collapsedEl.dom.style.top = box.y + "px";
32680             this.collapsedEl.setSize(box.width, box.height);
32681         }
32682         if(this.tabs){
32683             this.tabs.autoSizeTabs();
32684         }
32685     },
32686
32687     updateBody : function(w, h)
32688     {
32689         if(w !== null){
32690             this.el.setWidth(w);
32691             w -= this.el.getBorderWidth("rl");
32692             if(this.config.adjustments){
32693                 w += this.config.adjustments[0];
32694             }
32695         }
32696         if(h !== null){
32697             this.el.setHeight(h);
32698             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32699             h -= this.el.getBorderWidth("tb");
32700             if(this.config.adjustments){
32701                 h += this.config.adjustments[1];
32702             }
32703             this.bodyEl.setHeight(h);
32704             if(this.tabs){
32705                 h = this.tabs.syncHeight(h);
32706             }
32707         }
32708         if(this.panelSize){
32709             w = w !== null ? w : this.panelSize.width;
32710             h = h !== null ? h : this.panelSize.height;
32711         }
32712         if(this.activePanel){
32713             var el = this.activePanel.getEl();
32714             w = w !== null ? w : el.getWidth();
32715             h = h !== null ? h : el.getHeight();
32716             this.panelSize = {width: w, height: h};
32717             this.activePanel.setSize(w, h);
32718         }
32719         if(Roo.isIE && this.tabs){
32720             this.tabs.el.repaint();
32721         }
32722     },
32723
32724     /**
32725      * Returns the container element for this region.
32726      * @return {Roo.Element}
32727      */
32728     getEl : function(){
32729         return this.el;
32730     },
32731
32732     /**
32733      * Hides this region.
32734      */
32735     hide : function(){
32736         //if(!this.collapsed){
32737             this.el.dom.style.left = "-2000px";
32738             this.el.hide();
32739         //}else{
32740          //   this.collapsedEl.dom.style.left = "-2000px";
32741          //   this.collapsedEl.hide();
32742        // }
32743         this.visible = false;
32744         this.fireEvent("visibilitychange", this, false);
32745     },
32746
32747     /**
32748      * Shows this region if it was previously hidden.
32749      */
32750     show : function(){
32751         //if(!this.collapsed){
32752             this.el.show();
32753         //}else{
32754         //    this.collapsedEl.show();
32755        // }
32756         this.visible = true;
32757         this.fireEvent("visibilitychange", this, true);
32758     },
32759 /*
32760     closeClicked : function(){
32761         if(this.activePanel){
32762             this.remove(this.activePanel);
32763         }
32764     },
32765
32766     collapseClick : function(e){
32767         if(this.isSlid){
32768            e.stopPropagation();
32769            this.slideIn();
32770         }else{
32771            e.stopPropagation();
32772            this.slideOut();
32773         }
32774     },
32775 */
32776     /**
32777      * Collapses this region.
32778      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32779      */
32780     /*
32781     collapse : function(skipAnim, skipCheck = false){
32782         if(this.collapsed) {
32783             return;
32784         }
32785         
32786         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32787             
32788             this.collapsed = true;
32789             if(this.split){
32790                 this.split.el.hide();
32791             }
32792             if(this.config.animate && skipAnim !== true){
32793                 this.fireEvent("invalidated", this);
32794                 this.animateCollapse();
32795             }else{
32796                 this.el.setLocation(-20000,-20000);
32797                 this.el.hide();
32798                 this.collapsedEl.show();
32799                 this.fireEvent("collapsed", this);
32800                 this.fireEvent("invalidated", this);
32801             }
32802         }
32803         
32804     },
32805 */
32806     animateCollapse : function(){
32807         // overridden
32808     },
32809
32810     /**
32811      * Expands this region if it was previously collapsed.
32812      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32813      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32814      */
32815     /*
32816     expand : function(e, skipAnim){
32817         if(e) {
32818             e.stopPropagation();
32819         }
32820         if(!this.collapsed || this.el.hasActiveFx()) {
32821             return;
32822         }
32823         if(this.isSlid){
32824             this.afterSlideIn();
32825             skipAnim = true;
32826         }
32827         this.collapsed = false;
32828         if(this.config.animate && skipAnim !== true){
32829             this.animateExpand();
32830         }else{
32831             this.el.show();
32832             if(this.split){
32833                 this.split.el.show();
32834             }
32835             this.collapsedEl.setLocation(-2000,-2000);
32836             this.collapsedEl.hide();
32837             this.fireEvent("invalidated", this);
32838             this.fireEvent("expanded", this);
32839         }
32840     },
32841 */
32842     animateExpand : function(){
32843         // overridden
32844     },
32845
32846     initTabs : function()
32847     {
32848         this.bodyEl.setStyle("overflow", "hidden");
32849         var ts = new Roo.bootstrap.panel.Tabs({
32850                 el: this.bodyEl.dom,
32851                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32852                 disableTooltips: this.config.disableTabTips,
32853                 toolbar : this.config.toolbar
32854             });
32855         
32856         if(this.config.hideTabs){
32857             ts.stripWrap.setDisplayed(false);
32858         }
32859         this.tabs = ts;
32860         ts.resizeTabs = this.config.resizeTabs === true;
32861         ts.minTabWidth = this.config.minTabWidth || 40;
32862         ts.maxTabWidth = this.config.maxTabWidth || 250;
32863         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32864         ts.monitorResize = false;
32865         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32866         ts.bodyEl.addClass('roo-layout-tabs-body');
32867         this.panels.each(this.initPanelAsTab, this);
32868     },
32869
32870     initPanelAsTab : function(panel){
32871         var ti = this.tabs.addTab(
32872                     panel.getEl().id,
32873                     panel.getTitle(), null,
32874                     this.config.closeOnTab && panel.isClosable()
32875             );
32876         if(panel.tabTip !== undefined){
32877             ti.setTooltip(panel.tabTip);
32878         }
32879         ti.on("activate", function(){
32880               this.setActivePanel(panel);
32881         }, this);
32882         
32883         if(this.config.closeOnTab){
32884             ti.on("beforeclose", function(t, e){
32885                 e.cancel = true;
32886                 this.remove(panel);
32887             }, this);
32888         }
32889         return ti;
32890     },
32891
32892     updatePanelTitle : function(panel, title)
32893     {
32894         if(this.activePanel == panel){
32895             this.updateTitle(title);
32896         }
32897         if(this.tabs){
32898             var ti = this.tabs.getTab(panel.getEl().id);
32899             ti.setText(title);
32900             if(panel.tabTip !== undefined){
32901                 ti.setTooltip(panel.tabTip);
32902             }
32903         }
32904     },
32905
32906     updateTitle : function(title){
32907         if(this.titleTextEl && !this.config.title){
32908             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32909         }
32910     },
32911
32912     setActivePanel : function(panel)
32913     {
32914         panel = this.getPanel(panel);
32915         if(this.activePanel && this.activePanel != panel){
32916             this.activePanel.setActiveState(false);
32917         }
32918         this.activePanel = panel;
32919         panel.setActiveState(true);
32920         if(this.panelSize){
32921             panel.setSize(this.panelSize.width, this.panelSize.height);
32922         }
32923         if(this.closeBtn){
32924             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32925         }
32926         this.updateTitle(panel.getTitle());
32927         if(this.tabs){
32928             this.fireEvent("invalidated", this);
32929         }
32930         this.fireEvent("panelactivated", this, panel);
32931     },
32932
32933     /**
32934      * Shows the specified panel.
32935      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32936      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32937      */
32938     showPanel : function(panel)
32939     {
32940         panel = this.getPanel(panel);
32941         if(panel){
32942             if(this.tabs){
32943                 var tab = this.tabs.getTab(panel.getEl().id);
32944                 if(tab.isHidden()){
32945                     this.tabs.unhideTab(tab.id);
32946                 }
32947                 tab.activate();
32948             }else{
32949                 this.setActivePanel(panel);
32950             }
32951         }
32952         return panel;
32953     },
32954
32955     /**
32956      * Get the active panel for this region.
32957      * @return {Roo.ContentPanel} The active panel or null
32958      */
32959     getActivePanel : function(){
32960         return this.activePanel;
32961     },
32962
32963     validateVisibility : function(){
32964         if(this.panels.getCount() < 1){
32965             this.updateTitle("&#160;");
32966             this.closeBtn.hide();
32967             this.hide();
32968         }else{
32969             if(!this.isVisible()){
32970                 this.show();
32971             }
32972         }
32973     },
32974
32975     /**
32976      * Adds the passed ContentPanel(s) to this region.
32977      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32978      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32979      */
32980     add : function(panel){
32981         if(arguments.length > 1){
32982             for(var i = 0, len = arguments.length; i < len; i++) {
32983                 this.add(arguments[i]);
32984             }
32985             return null;
32986         }
32987         if(this.hasPanel(panel)){
32988             this.showPanel(panel);
32989             return panel;
32990         }
32991         panel.setRegion(this);
32992         this.panels.add(panel);
32993         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32994             this.bodyEl.dom.appendChild(panel.getEl().dom);
32995             if(panel.background !== true){
32996                 this.setActivePanel(panel);
32997             }
32998             this.fireEvent("paneladded", this, panel);
32999             return panel;
33000         }
33001         if(!this.tabs){
33002             this.initTabs();
33003         }else{
33004             this.initPanelAsTab(panel);
33005         }
33006         
33007         
33008         if(panel.background !== true){
33009             this.tabs.activate(panel.getEl().id);
33010         }
33011         this.fireEvent("paneladded", this, panel);
33012         return panel;
33013     },
33014
33015     /**
33016      * Hides the tab for the specified panel.
33017      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33018      */
33019     hidePanel : function(panel){
33020         if(this.tabs && (panel = this.getPanel(panel))){
33021             this.tabs.hideTab(panel.getEl().id);
33022         }
33023     },
33024
33025     /**
33026      * Unhides the tab for a previously hidden panel.
33027      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33028      */
33029     unhidePanel : function(panel){
33030         if(this.tabs && (panel = this.getPanel(panel))){
33031             this.tabs.unhideTab(panel.getEl().id);
33032         }
33033     },
33034
33035     clearPanels : function(){
33036         while(this.panels.getCount() > 0){
33037              this.remove(this.panels.first());
33038         }
33039     },
33040
33041     /**
33042      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33043      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33044      * @param {Boolean} preservePanel Overrides the config preservePanel option
33045      * @return {Roo.ContentPanel} The panel that was removed
33046      */
33047     remove : function(panel, preservePanel)
33048     {
33049         panel = this.getPanel(panel);
33050         if(!panel){
33051             return null;
33052         }
33053         var e = {};
33054         this.fireEvent("beforeremove", this, panel, e);
33055         if(e.cancel === true){
33056             return null;
33057         }
33058         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33059         var panelId = panel.getId();
33060         this.panels.removeKey(panelId);
33061         if(preservePanel){
33062             document.body.appendChild(panel.getEl().dom);
33063         }
33064         if(this.tabs){
33065             this.tabs.removeTab(panel.getEl().id);
33066         }else if (!preservePanel){
33067             this.bodyEl.dom.removeChild(panel.getEl().dom);
33068         }
33069         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33070             var p = this.panels.first();
33071             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33072             tempEl.appendChild(p.getEl().dom);
33073             this.bodyEl.update("");
33074             this.bodyEl.dom.appendChild(p.getEl().dom);
33075             tempEl = null;
33076             this.updateTitle(p.getTitle());
33077             this.tabs = null;
33078             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33079             this.setActivePanel(p);
33080         }
33081         panel.setRegion(null);
33082         if(this.activePanel == panel){
33083             this.activePanel = null;
33084         }
33085         if(this.config.autoDestroy !== false && preservePanel !== true){
33086             try{panel.destroy();}catch(e){}
33087         }
33088         this.fireEvent("panelremoved", this, panel);
33089         return panel;
33090     },
33091
33092     /**
33093      * Returns the TabPanel component used by this region
33094      * @return {Roo.TabPanel}
33095      */
33096     getTabs : function(){
33097         return this.tabs;
33098     },
33099
33100     createTool : function(parentEl, className){
33101         var btn = Roo.DomHelper.append(parentEl, {
33102             tag: "div",
33103             cls: "x-layout-tools-button",
33104             children: [ {
33105                 tag: "div",
33106                 cls: "roo-layout-tools-button-inner " + className,
33107                 html: "&#160;"
33108             }]
33109         }, true);
33110         btn.addClassOnOver("roo-layout-tools-button-over");
33111         return btn;
33112     }
33113 });/*
33114  * Based on:
33115  * Ext JS Library 1.1.1
33116  * Copyright(c) 2006-2007, Ext JS, LLC.
33117  *
33118  * Originally Released Under LGPL - original licence link has changed is not relivant.
33119  *
33120  * Fork - LGPL
33121  * <script type="text/javascript">
33122  */
33123  
33124
33125
33126 /**
33127  * @class Roo.SplitLayoutRegion
33128  * @extends Roo.LayoutRegion
33129  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33130  */
33131 Roo.bootstrap.layout.Split = function(config){
33132     this.cursor = config.cursor;
33133     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33134 };
33135
33136 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33137 {
33138     splitTip : "Drag to resize.",
33139     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33140     useSplitTips : false,
33141
33142     applyConfig : function(config){
33143         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33144     },
33145     
33146     onRender : function(ctr,pos) {
33147         
33148         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33149         if(!this.config.split){
33150             return;
33151         }
33152         if(!this.split){
33153             
33154             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33155                             tag: "div",
33156                             id: this.el.id + "-split",
33157                             cls: "roo-layout-split roo-layout-split-"+this.position,
33158                             html: "&#160;"
33159             });
33160             /** The SplitBar for this region 
33161             * @type Roo.SplitBar */
33162             // does not exist yet...
33163             Roo.log([this.position, this.orientation]);
33164             
33165             this.split = new Roo.bootstrap.SplitBar({
33166                 dragElement : splitEl,
33167                 resizingElement: this.el,
33168                 orientation : this.orientation
33169             });
33170             
33171             this.split.on("moved", this.onSplitMove, this);
33172             this.split.useShim = this.config.useShim === true;
33173             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33174             if(this.useSplitTips){
33175                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33176             }
33177             //if(config.collapsible){
33178             //    this.split.el.on("dblclick", this.collapse,  this);
33179             //}
33180         }
33181         if(typeof this.config.minSize != "undefined"){
33182             this.split.minSize = this.config.minSize;
33183         }
33184         if(typeof this.config.maxSize != "undefined"){
33185             this.split.maxSize = this.config.maxSize;
33186         }
33187         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33188             this.hideSplitter();
33189         }
33190         
33191     },
33192
33193     getHMaxSize : function(){
33194          var cmax = this.config.maxSize || 10000;
33195          var center = this.mgr.getRegion("center");
33196          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33197     },
33198
33199     getVMaxSize : function(){
33200          var cmax = this.config.maxSize || 10000;
33201          var center = this.mgr.getRegion("center");
33202          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33203     },
33204
33205     onSplitMove : function(split, newSize){
33206         this.fireEvent("resized", this, newSize);
33207     },
33208     
33209     /** 
33210      * Returns the {@link Roo.SplitBar} for this region.
33211      * @return {Roo.SplitBar}
33212      */
33213     getSplitBar : function(){
33214         return this.split;
33215     },
33216     
33217     hide : function(){
33218         this.hideSplitter();
33219         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33220     },
33221
33222     hideSplitter : function(){
33223         if(this.split){
33224             this.split.el.setLocation(-2000,-2000);
33225             this.split.el.hide();
33226         }
33227     },
33228
33229     show : function(){
33230         if(this.split){
33231             this.split.el.show();
33232         }
33233         Roo.bootstrap.layout.Split.superclass.show.call(this);
33234     },
33235     
33236     beforeSlide: function(){
33237         if(Roo.isGecko){// firefox overflow auto bug workaround
33238             this.bodyEl.clip();
33239             if(this.tabs) {
33240                 this.tabs.bodyEl.clip();
33241             }
33242             if(this.activePanel){
33243                 this.activePanel.getEl().clip();
33244                 
33245                 if(this.activePanel.beforeSlide){
33246                     this.activePanel.beforeSlide();
33247                 }
33248             }
33249         }
33250     },
33251     
33252     afterSlide : function(){
33253         if(Roo.isGecko){// firefox overflow auto bug workaround
33254             this.bodyEl.unclip();
33255             if(this.tabs) {
33256                 this.tabs.bodyEl.unclip();
33257             }
33258             if(this.activePanel){
33259                 this.activePanel.getEl().unclip();
33260                 if(this.activePanel.afterSlide){
33261                     this.activePanel.afterSlide();
33262                 }
33263             }
33264         }
33265     },
33266
33267     initAutoHide : function(){
33268         if(this.autoHide !== false){
33269             if(!this.autoHideHd){
33270                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33271                 this.autoHideHd = {
33272                     "mouseout": function(e){
33273                         if(!e.within(this.el, true)){
33274                             st.delay(500);
33275                         }
33276                     },
33277                     "mouseover" : function(e){
33278                         st.cancel();
33279                     },
33280                     scope : this
33281                 };
33282             }
33283             this.el.on(this.autoHideHd);
33284         }
33285     },
33286
33287     clearAutoHide : function(){
33288         if(this.autoHide !== false){
33289             this.el.un("mouseout", this.autoHideHd.mouseout);
33290             this.el.un("mouseover", this.autoHideHd.mouseover);
33291         }
33292     },
33293
33294     clearMonitor : function(){
33295         Roo.get(document).un("click", this.slideInIf, this);
33296     },
33297
33298     // these names are backwards but not changed for compat
33299     slideOut : function(){
33300         if(this.isSlid || this.el.hasActiveFx()){
33301             return;
33302         }
33303         this.isSlid = true;
33304         if(this.collapseBtn){
33305             this.collapseBtn.hide();
33306         }
33307         this.closeBtnState = this.closeBtn.getStyle('display');
33308         this.closeBtn.hide();
33309         if(this.stickBtn){
33310             this.stickBtn.show();
33311         }
33312         this.el.show();
33313         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33314         this.beforeSlide();
33315         this.el.setStyle("z-index", 10001);
33316         this.el.slideIn(this.getSlideAnchor(), {
33317             callback: function(){
33318                 this.afterSlide();
33319                 this.initAutoHide();
33320                 Roo.get(document).on("click", this.slideInIf, this);
33321                 this.fireEvent("slideshow", this);
33322             },
33323             scope: this,
33324             block: true
33325         });
33326     },
33327
33328     afterSlideIn : function(){
33329         this.clearAutoHide();
33330         this.isSlid = false;
33331         this.clearMonitor();
33332         this.el.setStyle("z-index", "");
33333         if(this.collapseBtn){
33334             this.collapseBtn.show();
33335         }
33336         this.closeBtn.setStyle('display', this.closeBtnState);
33337         if(this.stickBtn){
33338             this.stickBtn.hide();
33339         }
33340         this.fireEvent("slidehide", this);
33341     },
33342
33343     slideIn : function(cb){
33344         if(!this.isSlid || this.el.hasActiveFx()){
33345             Roo.callback(cb);
33346             return;
33347         }
33348         this.isSlid = false;
33349         this.beforeSlide();
33350         this.el.slideOut(this.getSlideAnchor(), {
33351             callback: function(){
33352                 this.el.setLeftTop(-10000, -10000);
33353                 this.afterSlide();
33354                 this.afterSlideIn();
33355                 Roo.callback(cb);
33356             },
33357             scope: this,
33358             block: true
33359         });
33360     },
33361     
33362     slideInIf : function(e){
33363         if(!e.within(this.el)){
33364             this.slideIn();
33365         }
33366     },
33367
33368     animateCollapse : function(){
33369         this.beforeSlide();
33370         this.el.setStyle("z-index", 20000);
33371         var anchor = this.getSlideAnchor();
33372         this.el.slideOut(anchor, {
33373             callback : function(){
33374                 this.el.setStyle("z-index", "");
33375                 this.collapsedEl.slideIn(anchor, {duration:.3});
33376                 this.afterSlide();
33377                 this.el.setLocation(-10000,-10000);
33378                 this.el.hide();
33379                 this.fireEvent("collapsed", this);
33380             },
33381             scope: this,
33382             block: true
33383         });
33384     },
33385
33386     animateExpand : function(){
33387         this.beforeSlide();
33388         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33389         this.el.setStyle("z-index", 20000);
33390         this.collapsedEl.hide({
33391             duration:.1
33392         });
33393         this.el.slideIn(this.getSlideAnchor(), {
33394             callback : function(){
33395                 this.el.setStyle("z-index", "");
33396                 this.afterSlide();
33397                 if(this.split){
33398                     this.split.el.show();
33399                 }
33400                 this.fireEvent("invalidated", this);
33401                 this.fireEvent("expanded", this);
33402             },
33403             scope: this,
33404             block: true
33405         });
33406     },
33407
33408     anchors : {
33409         "west" : "left",
33410         "east" : "right",
33411         "north" : "top",
33412         "south" : "bottom"
33413     },
33414
33415     sanchors : {
33416         "west" : "l",
33417         "east" : "r",
33418         "north" : "t",
33419         "south" : "b"
33420     },
33421
33422     canchors : {
33423         "west" : "tl-tr",
33424         "east" : "tr-tl",
33425         "north" : "tl-bl",
33426         "south" : "bl-tl"
33427     },
33428
33429     getAnchor : function(){
33430         return this.anchors[this.position];
33431     },
33432
33433     getCollapseAnchor : function(){
33434         return this.canchors[this.position];
33435     },
33436
33437     getSlideAnchor : function(){
33438         return this.sanchors[this.position];
33439     },
33440
33441     getAlignAdj : function(){
33442         var cm = this.cmargins;
33443         switch(this.position){
33444             case "west":
33445                 return [0, 0];
33446             break;
33447             case "east":
33448                 return [0, 0];
33449             break;
33450             case "north":
33451                 return [0, 0];
33452             break;
33453             case "south":
33454                 return [0, 0];
33455             break;
33456         }
33457     },
33458
33459     getExpandAdj : function(){
33460         var c = this.collapsedEl, cm = this.cmargins;
33461         switch(this.position){
33462             case "west":
33463                 return [-(cm.right+c.getWidth()+cm.left), 0];
33464             break;
33465             case "east":
33466                 return [cm.right+c.getWidth()+cm.left, 0];
33467             break;
33468             case "north":
33469                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33470             break;
33471             case "south":
33472                 return [0, cm.top+cm.bottom+c.getHeight()];
33473             break;
33474         }
33475     }
33476 });/*
33477  * Based on:
33478  * Ext JS Library 1.1.1
33479  * Copyright(c) 2006-2007, Ext JS, LLC.
33480  *
33481  * Originally Released Under LGPL - original licence link has changed is not relivant.
33482  *
33483  * Fork - LGPL
33484  * <script type="text/javascript">
33485  */
33486 /*
33487  * These classes are private internal classes
33488  */
33489 Roo.bootstrap.layout.Center = function(config){
33490     config.region = "center";
33491     Roo.bootstrap.layout.Region.call(this, config);
33492     this.visible = true;
33493     this.minWidth = config.minWidth || 20;
33494     this.minHeight = config.minHeight || 20;
33495 };
33496
33497 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33498     hide : function(){
33499         // center panel can't be hidden
33500     },
33501     
33502     show : function(){
33503         // center panel can't be hidden
33504     },
33505     
33506     getMinWidth: function(){
33507         return this.minWidth;
33508     },
33509     
33510     getMinHeight: function(){
33511         return this.minHeight;
33512     }
33513 });
33514
33515
33516
33517
33518  
33519
33520
33521
33522
33523
33524 Roo.bootstrap.layout.North = function(config)
33525 {
33526     config.region = 'north';
33527     config.cursor = 'n-resize';
33528     
33529     Roo.bootstrap.layout.Split.call(this, config);
33530     
33531     
33532     if(this.split){
33533         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33534         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33535         this.split.el.addClass("roo-layout-split-v");
33536     }
33537     var size = config.initialSize || config.height;
33538     if(typeof size != "undefined"){
33539         this.el.setHeight(size);
33540     }
33541 };
33542 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33543 {
33544     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33545     
33546     
33547     
33548     getBox : function(){
33549         if(this.collapsed){
33550             return this.collapsedEl.getBox();
33551         }
33552         var box = this.el.getBox();
33553         if(this.split){
33554             box.height += this.split.el.getHeight();
33555         }
33556         return box;
33557     },
33558     
33559     updateBox : function(box){
33560         if(this.split && !this.collapsed){
33561             box.height -= this.split.el.getHeight();
33562             this.split.el.setLeft(box.x);
33563             this.split.el.setTop(box.y+box.height);
33564             this.split.el.setWidth(box.width);
33565         }
33566         if(this.collapsed){
33567             this.updateBody(box.width, null);
33568         }
33569         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33570     }
33571 });
33572
33573
33574
33575
33576
33577 Roo.bootstrap.layout.South = function(config){
33578     config.region = 'south';
33579     config.cursor = 's-resize';
33580     Roo.bootstrap.layout.Split.call(this, config);
33581     if(this.split){
33582         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33583         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33584         this.split.el.addClass("roo-layout-split-v");
33585     }
33586     var size = config.initialSize || config.height;
33587     if(typeof size != "undefined"){
33588         this.el.setHeight(size);
33589     }
33590 };
33591
33592 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33593     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33594     getBox : function(){
33595         if(this.collapsed){
33596             return this.collapsedEl.getBox();
33597         }
33598         var box = this.el.getBox();
33599         if(this.split){
33600             var sh = this.split.el.getHeight();
33601             box.height += sh;
33602             box.y -= sh;
33603         }
33604         return box;
33605     },
33606     
33607     updateBox : function(box){
33608         if(this.split && !this.collapsed){
33609             var sh = this.split.el.getHeight();
33610             box.height -= sh;
33611             box.y += sh;
33612             this.split.el.setLeft(box.x);
33613             this.split.el.setTop(box.y-sh);
33614             this.split.el.setWidth(box.width);
33615         }
33616         if(this.collapsed){
33617             this.updateBody(box.width, null);
33618         }
33619         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33620     }
33621 });
33622
33623 Roo.bootstrap.layout.East = function(config){
33624     config.region = "east";
33625     config.cursor = "e-resize";
33626     Roo.bootstrap.layout.Split.call(this, config);
33627     if(this.split){
33628         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33629         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33630         this.split.el.addClass("roo-layout-split-h");
33631     }
33632     var size = config.initialSize || config.width;
33633     if(typeof size != "undefined"){
33634         this.el.setWidth(size);
33635     }
33636 };
33637 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33638     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33639     getBox : function(){
33640         if(this.collapsed){
33641             return this.collapsedEl.getBox();
33642         }
33643         var box = this.el.getBox();
33644         if(this.split){
33645             var sw = this.split.el.getWidth();
33646             box.width += sw;
33647             box.x -= sw;
33648         }
33649         return box;
33650     },
33651
33652     updateBox : function(box){
33653         if(this.split && !this.collapsed){
33654             var sw = this.split.el.getWidth();
33655             box.width -= sw;
33656             this.split.el.setLeft(box.x);
33657             this.split.el.setTop(box.y);
33658             this.split.el.setHeight(box.height);
33659             box.x += sw;
33660         }
33661         if(this.collapsed){
33662             this.updateBody(null, box.height);
33663         }
33664         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33665     }
33666 });
33667
33668 Roo.bootstrap.layout.West = function(config){
33669     config.region = "west";
33670     config.cursor = "w-resize";
33671     
33672     Roo.bootstrap.layout.Split.call(this, config);
33673     if(this.split){
33674         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33675         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33676         this.split.el.addClass("roo-layout-split-h");
33677     }
33678     
33679 };
33680 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33681     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33682     
33683     onRender: function(ctr, pos)
33684     {
33685         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33686         var size = this.config.initialSize || this.config.width;
33687         if(typeof size != "undefined"){
33688             this.el.setWidth(size);
33689         }
33690     },
33691     
33692     getBox : function(){
33693         if(this.collapsed){
33694             return this.collapsedEl.getBox();
33695         }
33696         var box = this.el.getBox();
33697         if(this.split){
33698             box.width += this.split.el.getWidth();
33699         }
33700         return box;
33701     },
33702     
33703     updateBox : function(box){
33704         if(this.split && !this.collapsed){
33705             var sw = this.split.el.getWidth();
33706             box.width -= sw;
33707             this.split.el.setLeft(box.x+box.width);
33708             this.split.el.setTop(box.y);
33709             this.split.el.setHeight(box.height);
33710         }
33711         if(this.collapsed){
33712             this.updateBody(null, box.height);
33713         }
33714         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33715     }
33716 });
33717 Roo.namespace("Roo.bootstrap.panel");/*
33718  * Based on:
33719  * Ext JS Library 1.1.1
33720  * Copyright(c) 2006-2007, Ext JS, LLC.
33721  *
33722  * Originally Released Under LGPL - original licence link has changed is not relivant.
33723  *
33724  * Fork - LGPL
33725  * <script type="text/javascript">
33726  */
33727 /**
33728  * @class Roo.ContentPanel
33729  * @extends Roo.util.Observable
33730  * A basic ContentPanel element.
33731  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33732  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33733  * @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
33734  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33735  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33736  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33737  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33738  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33739  * @cfg {String} title          The title for this panel
33740  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33741  * @cfg {String} url            Calls {@link #setUrl} with this value
33742  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33743  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33744  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33745  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33746
33747  * @constructor
33748  * Create a new ContentPanel.
33749  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33750  * @param {String/Object} config A string to set only the title or a config object
33751  * @param {String} content (optional) Set the HTML content for this panel
33752  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33753  */
33754 Roo.bootstrap.panel.Content = function( config){
33755     
33756     var el = config.el;
33757     var content = config.content;
33758
33759     if(config.autoCreate){ // xtype is available if this is called from factory
33760         el = Roo.id();
33761     }
33762     this.el = Roo.get(el);
33763     if(!this.el && config && config.autoCreate){
33764         if(typeof config.autoCreate == "object"){
33765             if(!config.autoCreate.id){
33766                 config.autoCreate.id = config.id||el;
33767             }
33768             this.el = Roo.DomHelper.append(document.body,
33769                         config.autoCreate, true);
33770         }else{
33771             var elcfg =  {   tag: "div",
33772                             cls: "roo-layout-inactive-content",
33773                             id: config.id||el
33774                             };
33775             if (config.html) {
33776                 elcfg.html = config.html;
33777                 
33778             }
33779                         
33780             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33781         }
33782     } 
33783     this.closable = false;
33784     this.loaded = false;
33785     this.active = false;
33786    
33787       
33788     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33789         
33790         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33791         
33792         this.wrapEl = this.el.wrap();
33793         var ti = [];
33794         if (config.toolbar.items) {
33795             ti = config.toolbar.items ;
33796             delete config.toolbar.items ;
33797         }
33798         
33799         var nitems = [];
33800         this.toolbar.render(this.wrapEl, 'before');
33801         for(var i =0;i < ti.length;i++) {
33802           //  Roo.log(['add child', items[i]]);
33803             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33804         }
33805         this.toolbar.items = nitems;
33806         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33807         delete config.toolbar;
33808         
33809     }
33810     /*
33811     // xtype created footer. - not sure if will work as we normally have to render first..
33812     if (this.footer && !this.footer.el && this.footer.xtype) {
33813         if (!this.wrapEl) {
33814             this.wrapEl = this.el.wrap();
33815         }
33816     
33817         this.footer.container = this.wrapEl.createChild();
33818          
33819         this.footer = Roo.factory(this.footer, Roo);
33820         
33821     }
33822     */
33823     
33824      if(typeof config == "string"){
33825         this.title = config;
33826     }else{
33827         Roo.apply(this, config);
33828     }
33829     
33830     if(this.resizeEl){
33831         this.resizeEl = Roo.get(this.resizeEl, true);
33832     }else{
33833         this.resizeEl = this.el;
33834     }
33835     // handle view.xtype
33836     
33837  
33838     
33839     
33840     this.addEvents({
33841         /**
33842          * @event activate
33843          * Fires when this panel is activated. 
33844          * @param {Roo.ContentPanel} this
33845          */
33846         "activate" : true,
33847         /**
33848          * @event deactivate
33849          * Fires when this panel is activated. 
33850          * @param {Roo.ContentPanel} this
33851          */
33852         "deactivate" : true,
33853
33854         /**
33855          * @event resize
33856          * Fires when this panel is resized if fitToFrame is true.
33857          * @param {Roo.ContentPanel} this
33858          * @param {Number} width The width after any component adjustments
33859          * @param {Number} height The height after any component adjustments
33860          */
33861         "resize" : true,
33862         
33863          /**
33864          * @event render
33865          * Fires when this tab is created
33866          * @param {Roo.ContentPanel} this
33867          */
33868         "render" : true
33869         
33870         
33871         
33872     });
33873     
33874
33875     
33876     
33877     if(this.autoScroll){
33878         this.resizeEl.setStyle("overflow", "auto");
33879     } else {
33880         // fix randome scrolling
33881         //this.el.on('scroll', function() {
33882         //    Roo.log('fix random scolling');
33883         //    this.scrollTo('top',0); 
33884         //});
33885     }
33886     content = content || this.content;
33887     if(content){
33888         this.setContent(content);
33889     }
33890     if(config && config.url){
33891         this.setUrl(this.url, this.params, this.loadOnce);
33892     }
33893     
33894     
33895     
33896     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33897     
33898     if (this.view && typeof(this.view.xtype) != 'undefined') {
33899         this.view.el = this.el.appendChild(document.createElement("div"));
33900         this.view = Roo.factory(this.view); 
33901         this.view.render  &&  this.view.render(false, '');  
33902     }
33903     
33904     
33905     this.fireEvent('render', this);
33906 };
33907
33908 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33909     tabTip:'',
33910     setRegion : function(region){
33911         this.region = region;
33912         if(region){
33913            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33914         }else{
33915            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33916         } 
33917     },
33918     
33919     /**
33920      * Returns the toolbar for this Panel if one was configured. 
33921      * @return {Roo.Toolbar} 
33922      */
33923     getToolbar : function(){
33924         return this.toolbar;
33925     },
33926     
33927     setActiveState : function(active){
33928         this.active = active;
33929         if(!active){
33930             this.fireEvent("deactivate", this);
33931         }else{
33932             this.fireEvent("activate", this);
33933         }
33934     },
33935     /**
33936      * Updates this panel's element
33937      * @param {String} content The new content
33938      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33939     */
33940     setContent : function(content, loadScripts){
33941         this.el.update(content, loadScripts);
33942     },
33943
33944     ignoreResize : function(w, h){
33945         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33946             return true;
33947         }else{
33948             this.lastSize = {width: w, height: h};
33949             return false;
33950         }
33951     },
33952     /**
33953      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33954      * @return {Roo.UpdateManager} The UpdateManager
33955      */
33956     getUpdateManager : function(){
33957         return this.el.getUpdateManager();
33958     },
33959      /**
33960      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33961      * @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:
33962 <pre><code>
33963 panel.load({
33964     url: "your-url.php",
33965     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33966     callback: yourFunction,
33967     scope: yourObject, //(optional scope)
33968     discardUrl: false,
33969     nocache: false,
33970     text: "Loading...",
33971     timeout: 30,
33972     scripts: false
33973 });
33974 </code></pre>
33975      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33976      * 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.
33977      * @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}
33978      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33979      * @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.
33980      * @return {Roo.ContentPanel} this
33981      */
33982     load : function(){
33983         var um = this.el.getUpdateManager();
33984         um.update.apply(um, arguments);
33985         return this;
33986     },
33987
33988
33989     /**
33990      * 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.
33991      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33992      * @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)
33993      * @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)
33994      * @return {Roo.UpdateManager} The UpdateManager
33995      */
33996     setUrl : function(url, params, loadOnce){
33997         if(this.refreshDelegate){
33998             this.removeListener("activate", this.refreshDelegate);
33999         }
34000         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34001         this.on("activate", this.refreshDelegate);
34002         return this.el.getUpdateManager();
34003     },
34004     
34005     _handleRefresh : function(url, params, loadOnce){
34006         if(!loadOnce || !this.loaded){
34007             var updater = this.el.getUpdateManager();
34008             updater.update(url, params, this._setLoaded.createDelegate(this));
34009         }
34010     },
34011     
34012     _setLoaded : function(){
34013         this.loaded = true;
34014     }, 
34015     
34016     /**
34017      * Returns this panel's id
34018      * @return {String} 
34019      */
34020     getId : function(){
34021         return this.el.id;
34022     },
34023     
34024     /** 
34025      * Returns this panel's element - used by regiosn to add.
34026      * @return {Roo.Element} 
34027      */
34028     getEl : function(){
34029         return this.wrapEl || this.el;
34030     },
34031     
34032    
34033     
34034     adjustForComponents : function(width, height)
34035     {
34036         //Roo.log('adjustForComponents ');
34037         if(this.resizeEl != this.el){
34038             width -= this.el.getFrameWidth('lr');
34039             height -= this.el.getFrameWidth('tb');
34040         }
34041         if(this.toolbar){
34042             var te = this.toolbar.getEl();
34043             height -= te.getHeight();
34044             te.setWidth(width);
34045         }
34046         if(this.footer){
34047             var te = this.footer.getEl();
34048             Roo.log("footer:" + te.getHeight());
34049             
34050             height -= te.getHeight();
34051             te.setWidth(width);
34052         }
34053         
34054         
34055         if(this.adjustments){
34056             width += this.adjustments[0];
34057             height += this.adjustments[1];
34058         }
34059         return {"width": width, "height": height};
34060     },
34061     
34062     setSize : function(width, height){
34063         if(this.fitToFrame && !this.ignoreResize(width, height)){
34064             if(this.fitContainer && this.resizeEl != this.el){
34065                 this.el.setSize(width, height);
34066             }
34067             var size = this.adjustForComponents(width, height);
34068             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34069             this.fireEvent('resize', this, size.width, size.height);
34070         }
34071     },
34072     
34073     /**
34074      * Returns this panel's title
34075      * @return {String} 
34076      */
34077     getTitle : function(){
34078         return this.title;
34079     },
34080     
34081     /**
34082      * Set this panel's title
34083      * @param {String} title
34084      */
34085     setTitle : function(title){
34086         this.title = title;
34087         if(this.region){
34088             this.region.updatePanelTitle(this, title);
34089         }
34090     },
34091     
34092     /**
34093      * Returns true is this panel was configured to be closable
34094      * @return {Boolean} 
34095      */
34096     isClosable : function(){
34097         return this.closable;
34098     },
34099     
34100     beforeSlide : function(){
34101         this.el.clip();
34102         this.resizeEl.clip();
34103     },
34104     
34105     afterSlide : function(){
34106         this.el.unclip();
34107         this.resizeEl.unclip();
34108     },
34109     
34110     /**
34111      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34112      *   Will fail silently if the {@link #setUrl} method has not been called.
34113      *   This does not activate the panel, just updates its content.
34114      */
34115     refresh : function(){
34116         if(this.refreshDelegate){
34117            this.loaded = false;
34118            this.refreshDelegate();
34119         }
34120     },
34121     
34122     /**
34123      * Destroys this panel
34124      */
34125     destroy : function(){
34126         this.el.removeAllListeners();
34127         var tempEl = document.createElement("span");
34128         tempEl.appendChild(this.el.dom);
34129         tempEl.innerHTML = "";
34130         this.el.remove();
34131         this.el = null;
34132     },
34133     
34134     /**
34135      * form - if the content panel contains a form - this is a reference to it.
34136      * @type {Roo.form.Form}
34137      */
34138     form : false,
34139     /**
34140      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34141      *    This contains a reference to it.
34142      * @type {Roo.View}
34143      */
34144     view : false,
34145     
34146       /**
34147      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34148      * <pre><code>
34149
34150 layout.addxtype({
34151        xtype : 'Form',
34152        items: [ .... ]
34153    }
34154 );
34155
34156 </code></pre>
34157      * @param {Object} cfg Xtype definition of item to add.
34158      */
34159     
34160     
34161     getChildContainer: function () {
34162         return this.getEl();
34163     }
34164     
34165     
34166     /*
34167         var  ret = new Roo.factory(cfg);
34168         return ret;
34169         
34170         
34171         // add form..
34172         if (cfg.xtype.match(/^Form$/)) {
34173             
34174             var el;
34175             //if (this.footer) {
34176             //    el = this.footer.container.insertSibling(false, 'before');
34177             //} else {
34178                 el = this.el.createChild();
34179             //}
34180
34181             this.form = new  Roo.form.Form(cfg);
34182             
34183             
34184             if ( this.form.allItems.length) {
34185                 this.form.render(el.dom);
34186             }
34187             return this.form;
34188         }
34189         // should only have one of theses..
34190         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34191             // views.. should not be just added - used named prop 'view''
34192             
34193             cfg.el = this.el.appendChild(document.createElement("div"));
34194             // factory?
34195             
34196             var ret = new Roo.factory(cfg);
34197              
34198              ret.render && ret.render(false, ''); // render blank..
34199             this.view = ret;
34200             return ret;
34201         }
34202         return false;
34203     }
34204     \*/
34205 });
34206  
34207 /**
34208  * @class Roo.bootstrap.panel.Grid
34209  * @extends Roo.bootstrap.panel.Content
34210  * @constructor
34211  * Create a new GridPanel.
34212  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34213  * @param {Object} config A the config object
34214   
34215  */
34216
34217
34218
34219 Roo.bootstrap.panel.Grid = function(config)
34220 {
34221     
34222       
34223     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34224         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34225
34226     config.el = this.wrapper;
34227     //this.el = this.wrapper;
34228     
34229       if (config.container) {
34230         // ctor'ed from a Border/panel.grid
34231         
34232         
34233         this.wrapper.setStyle("overflow", "hidden");
34234         this.wrapper.addClass('roo-grid-container');
34235
34236     }
34237     
34238     
34239     if(config.toolbar){
34240         var tool_el = this.wrapper.createChild();    
34241         this.toolbar = Roo.factory(config.toolbar);
34242         var ti = [];
34243         if (config.toolbar.items) {
34244             ti = config.toolbar.items ;
34245             delete config.toolbar.items ;
34246         }
34247         
34248         var nitems = [];
34249         this.toolbar.render(tool_el);
34250         for(var i =0;i < ti.length;i++) {
34251           //  Roo.log(['add child', items[i]]);
34252             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34253         }
34254         this.toolbar.items = nitems;
34255         
34256         delete config.toolbar;
34257     }
34258     
34259     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34260     config.grid.scrollBody = true;;
34261     config.grid.monitorWindowResize = false; // turn off autosizing
34262     config.grid.autoHeight = false;
34263     config.grid.autoWidth = false;
34264     
34265     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34266     
34267     if (config.background) {
34268         // render grid on panel activation (if panel background)
34269         this.on('activate', function(gp) {
34270             if (!gp.grid.rendered) {
34271                 gp.grid.render(el);
34272                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34273
34274             }
34275         });
34276             
34277     } else {
34278         this.grid.render(this.wrapper);
34279         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34280
34281     }
34282     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34283     // ??? needed ??? config.el = this.wrapper;
34284     
34285     
34286     
34287   
34288     // xtype created footer. - not sure if will work as we normally have to render first..
34289     if (this.footer && !this.footer.el && this.footer.xtype) {
34290         
34291         var ctr = this.grid.getView().getFooterPanel(true);
34292         this.footer.dataSource = this.grid.dataSource;
34293         this.footer = Roo.factory(this.footer, Roo);
34294         this.footer.render(ctr);
34295         
34296     }
34297     
34298     
34299     
34300     
34301      
34302 };
34303
34304 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34305     getId : function(){
34306         return this.grid.id;
34307     },
34308     
34309     /**
34310      * Returns the grid for this panel
34311      * @return {Roo.bootstrap.Table} 
34312      */
34313     getGrid : function(){
34314         return this.grid;    
34315     },
34316     
34317     setSize : function(width, height){
34318         if(!this.ignoreResize(width, height)){
34319             var grid = this.grid;
34320             var size = this.adjustForComponents(width, height);
34321             var gridel = grid.getGridEl();
34322             gridel.setSize(size.width, size.height);
34323             /*
34324             var thd = grid.getGridEl().select('thead',true).first();
34325             var tbd = grid.getGridEl().select('tbody', true).first();
34326             if (tbd) {
34327                 tbd.setSize(width, height - thd.getHeight());
34328             }
34329             */
34330             grid.autoSize();
34331         }
34332     },
34333      
34334     
34335     
34336     beforeSlide : function(){
34337         this.grid.getView().scroller.clip();
34338     },
34339     
34340     afterSlide : function(){
34341         this.grid.getView().scroller.unclip();
34342     },
34343     
34344     destroy : function(){
34345         this.grid.destroy();
34346         delete this.grid;
34347         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34348     }
34349 });
34350
34351 /**
34352  * @class Roo.bootstrap.panel.Nest
34353  * @extends Roo.bootstrap.panel.Content
34354  * @constructor
34355  * Create a new Panel, that can contain a layout.Border.
34356  * 
34357  * 
34358  * @param {Roo.BorderLayout} layout The layout for this panel
34359  * @param {String/Object} config A string to set only the title or a config object
34360  */
34361 Roo.bootstrap.panel.Nest = function(config)
34362 {
34363     // construct with only one argument..
34364     /* FIXME - implement nicer consturctors
34365     if (layout.layout) {
34366         config = layout;
34367         layout = config.layout;
34368         delete config.layout;
34369     }
34370     if (layout.xtype && !layout.getEl) {
34371         // then layout needs constructing..
34372         layout = Roo.factory(layout, Roo);
34373     }
34374     */
34375     
34376     config.el =  config.layout.getEl();
34377     
34378     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34379     
34380     config.layout.monitorWindowResize = false; // turn off autosizing
34381     this.layout = config.layout;
34382     this.layout.getEl().addClass("roo-layout-nested-layout");
34383     
34384     
34385     
34386     
34387 };
34388
34389 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34390
34391     setSize : function(width, height){
34392         if(!this.ignoreResize(width, height)){
34393             var size = this.adjustForComponents(width, height);
34394             var el = this.layout.getEl();
34395             el.setSize(size.width, size.height);
34396             var touch = el.dom.offsetWidth;
34397             this.layout.layout();
34398             // ie requires a double layout on the first pass
34399             if(Roo.isIE && !this.initialized){
34400                 this.initialized = true;
34401                 this.layout.layout();
34402             }
34403         }
34404     },
34405     
34406     // activate all subpanels if not currently active..
34407     
34408     setActiveState : function(active){
34409         this.active = active;
34410         if(!active){
34411             this.fireEvent("deactivate", this);
34412             return;
34413         }
34414         
34415         this.fireEvent("activate", this);
34416         // not sure if this should happen before or after..
34417         if (!this.layout) {
34418             return; // should not happen..
34419         }
34420         var reg = false;
34421         for (var r in this.layout.regions) {
34422             reg = this.layout.getRegion(r);
34423             if (reg.getActivePanel()) {
34424                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34425                 reg.setActivePanel(reg.getActivePanel());
34426                 continue;
34427             }
34428             if (!reg.panels.length) {
34429                 continue;
34430             }
34431             reg.showPanel(reg.getPanel(0));
34432         }
34433         
34434         
34435         
34436         
34437     },
34438     
34439     /**
34440      * Returns the nested BorderLayout for this panel
34441      * @return {Roo.BorderLayout} 
34442      */
34443     getLayout : function(){
34444         return this.layout;
34445     },
34446     
34447      /**
34448      * Adds a xtype elements to the layout of the nested panel
34449      * <pre><code>
34450
34451 panel.addxtype({
34452        xtype : 'ContentPanel',
34453        region: 'west',
34454        items: [ .... ]
34455    }
34456 );
34457
34458 panel.addxtype({
34459         xtype : 'NestedLayoutPanel',
34460         region: 'west',
34461         layout: {
34462            center: { },
34463            west: { }   
34464         },
34465         items : [ ... list of content panels or nested layout panels.. ]
34466    }
34467 );
34468 </code></pre>
34469      * @param {Object} cfg Xtype definition of item to add.
34470      */
34471     addxtype : function(cfg) {
34472         return this.layout.addxtype(cfg);
34473     
34474     }
34475 });        /*
34476  * Based on:
34477  * Ext JS Library 1.1.1
34478  * Copyright(c) 2006-2007, Ext JS, LLC.
34479  *
34480  * Originally Released Under LGPL - original licence link has changed is not relivant.
34481  *
34482  * Fork - LGPL
34483  * <script type="text/javascript">
34484  */
34485 /**
34486  * @class Roo.TabPanel
34487  * @extends Roo.util.Observable
34488  * A lightweight tab container.
34489  * <br><br>
34490  * Usage:
34491  * <pre><code>
34492 // basic tabs 1, built from existing content
34493 var tabs = new Roo.TabPanel("tabs1");
34494 tabs.addTab("script", "View Script");
34495 tabs.addTab("markup", "View Markup");
34496 tabs.activate("script");
34497
34498 // more advanced tabs, built from javascript
34499 var jtabs = new Roo.TabPanel("jtabs");
34500 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34501
34502 // set up the UpdateManager
34503 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34504 var updater = tab2.getUpdateManager();
34505 updater.setDefaultUrl("ajax1.htm");
34506 tab2.on('activate', updater.refresh, updater, true);
34507
34508 // Use setUrl for Ajax loading
34509 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34510 tab3.setUrl("ajax2.htm", null, true);
34511
34512 // Disabled tab
34513 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34514 tab4.disable();
34515
34516 jtabs.activate("jtabs-1");
34517  * </code></pre>
34518  * @constructor
34519  * Create a new TabPanel.
34520  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34521  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34522  */
34523 Roo.bootstrap.panel.Tabs = function(config){
34524     /**
34525     * The container element for this TabPanel.
34526     * @type Roo.Element
34527     */
34528     this.el = Roo.get(config.el);
34529     delete config.el;
34530     if(config){
34531         if(typeof config == "boolean"){
34532             this.tabPosition = config ? "bottom" : "top";
34533         }else{
34534             Roo.apply(this, config);
34535         }
34536     }
34537     
34538     if(this.tabPosition == "bottom"){
34539         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34540         this.el.addClass("roo-tabs-bottom");
34541     }
34542     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34543     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34544     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34545     if(Roo.isIE){
34546         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34547     }
34548     if(this.tabPosition != "bottom"){
34549         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34550          * @type Roo.Element
34551          */
34552         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34553         this.el.addClass("roo-tabs-top");
34554     }
34555     this.items = [];
34556
34557     this.bodyEl.setStyle("position", "relative");
34558
34559     this.active = null;
34560     this.activateDelegate = this.activate.createDelegate(this);
34561
34562     this.addEvents({
34563         /**
34564          * @event tabchange
34565          * Fires when the active tab changes
34566          * @param {Roo.TabPanel} this
34567          * @param {Roo.TabPanelItem} activePanel The new active tab
34568          */
34569         "tabchange": true,
34570         /**
34571          * @event beforetabchange
34572          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34573          * @param {Roo.TabPanel} this
34574          * @param {Object} e Set cancel to true on this object to cancel the tab change
34575          * @param {Roo.TabPanelItem} tab The tab being changed to
34576          */
34577         "beforetabchange" : true
34578     });
34579
34580     Roo.EventManager.onWindowResize(this.onResize, this);
34581     this.cpad = this.el.getPadding("lr");
34582     this.hiddenCount = 0;
34583
34584
34585     // toolbar on the tabbar support...
34586     if (this.toolbar) {
34587         alert("no toolbar support yet");
34588         this.toolbar  = false;
34589         /*
34590         var tcfg = this.toolbar;
34591         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34592         this.toolbar = new Roo.Toolbar(tcfg);
34593         if (Roo.isSafari) {
34594             var tbl = tcfg.container.child('table', true);
34595             tbl.setAttribute('width', '100%');
34596         }
34597         */
34598         
34599     }
34600    
34601
34602
34603     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34604 };
34605
34606 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34607     /*
34608      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34609      */
34610     tabPosition : "top",
34611     /*
34612      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34613      */
34614     currentTabWidth : 0,
34615     /*
34616      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34617      */
34618     minTabWidth : 40,
34619     /*
34620      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34621      */
34622     maxTabWidth : 250,
34623     /*
34624      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34625      */
34626     preferredTabWidth : 175,
34627     /*
34628      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34629      */
34630     resizeTabs : false,
34631     /*
34632      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34633      */
34634     monitorResize : true,
34635     /*
34636      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34637      */
34638     toolbar : false,
34639
34640     /**
34641      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34642      * @param {String} id The id of the div to use <b>or create</b>
34643      * @param {String} text The text for the tab
34644      * @param {String} content (optional) Content to put in the TabPanelItem body
34645      * @param {Boolean} closable (optional) True to create a close icon on the tab
34646      * @return {Roo.TabPanelItem} The created TabPanelItem
34647      */
34648     addTab : function(id, text, content, closable)
34649     {
34650         var item = new Roo.bootstrap.panel.TabItem({
34651             panel: this,
34652             id : id,
34653             text : text,
34654             closable : closable
34655         });
34656         this.addTabItem(item);
34657         if(content){
34658             item.setContent(content);
34659         }
34660         return item;
34661     },
34662
34663     /**
34664      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34665      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34666      * @return {Roo.TabPanelItem}
34667      */
34668     getTab : function(id){
34669         return this.items[id];
34670     },
34671
34672     /**
34673      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34674      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34675      */
34676     hideTab : function(id){
34677         var t = this.items[id];
34678         if(!t.isHidden()){
34679            t.setHidden(true);
34680            this.hiddenCount++;
34681            this.autoSizeTabs();
34682         }
34683     },
34684
34685     /**
34686      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34687      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34688      */
34689     unhideTab : function(id){
34690         var t = this.items[id];
34691         if(t.isHidden()){
34692            t.setHidden(false);
34693            this.hiddenCount--;
34694            this.autoSizeTabs();
34695         }
34696     },
34697
34698     /**
34699      * Adds an existing {@link Roo.TabPanelItem}.
34700      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34701      */
34702     addTabItem : function(item){
34703         this.items[item.id] = item;
34704         this.items.push(item);
34705       //  if(this.resizeTabs){
34706     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34707   //         this.autoSizeTabs();
34708 //        }else{
34709 //            item.autoSize();
34710        // }
34711     },
34712
34713     /**
34714      * Removes a {@link Roo.TabPanelItem}.
34715      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34716      */
34717     removeTab : function(id){
34718         var items = this.items;
34719         var tab = items[id];
34720         if(!tab) { return; }
34721         var index = items.indexOf(tab);
34722         if(this.active == tab && items.length > 1){
34723             var newTab = this.getNextAvailable(index);
34724             if(newTab) {
34725                 newTab.activate();
34726             }
34727         }
34728         this.stripEl.dom.removeChild(tab.pnode.dom);
34729         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34730             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34731         }
34732         items.splice(index, 1);
34733         delete this.items[tab.id];
34734         tab.fireEvent("close", tab);
34735         tab.purgeListeners();
34736         this.autoSizeTabs();
34737     },
34738
34739     getNextAvailable : function(start){
34740         var items = this.items;
34741         var index = start;
34742         // look for a next tab that will slide over to
34743         // replace the one being removed
34744         while(index < items.length){
34745             var item = items[++index];
34746             if(item && !item.isHidden()){
34747                 return item;
34748             }
34749         }
34750         // if one isn't found select the previous tab (on the left)
34751         index = start;
34752         while(index >= 0){
34753             var item = items[--index];
34754             if(item && !item.isHidden()){
34755                 return item;
34756             }
34757         }
34758         return null;
34759     },
34760
34761     /**
34762      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34763      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34764      */
34765     disableTab : function(id){
34766         var tab = this.items[id];
34767         if(tab && this.active != tab){
34768             tab.disable();
34769         }
34770     },
34771
34772     /**
34773      * Enables a {@link Roo.TabPanelItem} that is disabled.
34774      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34775      */
34776     enableTab : function(id){
34777         var tab = this.items[id];
34778         tab.enable();
34779     },
34780
34781     /**
34782      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34783      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34784      * @return {Roo.TabPanelItem} The TabPanelItem.
34785      */
34786     activate : function(id){
34787         var tab = this.items[id];
34788         if(!tab){
34789             return null;
34790         }
34791         if(tab == this.active || tab.disabled){
34792             return tab;
34793         }
34794         var e = {};
34795         this.fireEvent("beforetabchange", this, e, tab);
34796         if(e.cancel !== true && !tab.disabled){
34797             if(this.active){
34798                 this.active.hide();
34799             }
34800             this.active = this.items[id];
34801             this.active.show();
34802             this.fireEvent("tabchange", this, this.active);
34803         }
34804         return tab;
34805     },
34806
34807     /**
34808      * Gets the active {@link Roo.TabPanelItem}.
34809      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34810      */
34811     getActiveTab : function(){
34812         return this.active;
34813     },
34814
34815     /**
34816      * Updates the tab body element to fit the height of the container element
34817      * for overflow scrolling
34818      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34819      */
34820     syncHeight : function(targetHeight){
34821         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34822         var bm = this.bodyEl.getMargins();
34823         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34824         this.bodyEl.setHeight(newHeight);
34825         return newHeight;
34826     },
34827
34828     onResize : function(){
34829         if(this.monitorResize){
34830             this.autoSizeTabs();
34831         }
34832     },
34833
34834     /**
34835      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34836      */
34837     beginUpdate : function(){
34838         this.updating = true;
34839     },
34840
34841     /**
34842      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34843      */
34844     endUpdate : function(){
34845         this.updating = false;
34846         this.autoSizeTabs();
34847     },
34848
34849     /**
34850      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34851      */
34852     autoSizeTabs : function(){
34853         var count = this.items.length;
34854         var vcount = count - this.hiddenCount;
34855         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34856             return;
34857         }
34858         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34859         var availWidth = Math.floor(w / vcount);
34860         var b = this.stripBody;
34861         if(b.getWidth() > w){
34862             var tabs = this.items;
34863             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34864             if(availWidth < this.minTabWidth){
34865                 /*if(!this.sleft){    // incomplete scrolling code
34866                     this.createScrollButtons();
34867                 }
34868                 this.showScroll();
34869                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34870             }
34871         }else{
34872             if(this.currentTabWidth < this.preferredTabWidth){
34873                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34874             }
34875         }
34876     },
34877
34878     /**
34879      * Returns the number of tabs in this TabPanel.
34880      * @return {Number}
34881      */
34882      getCount : function(){
34883          return this.items.length;
34884      },
34885
34886     /**
34887      * Resizes all the tabs to the passed width
34888      * @param {Number} The new width
34889      */
34890     setTabWidth : function(width){
34891         this.currentTabWidth = width;
34892         for(var i = 0, len = this.items.length; i < len; i++) {
34893                 if(!this.items[i].isHidden()) {
34894                 this.items[i].setWidth(width);
34895             }
34896         }
34897     },
34898
34899     /**
34900      * Destroys this TabPanel
34901      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34902      */
34903     destroy : function(removeEl){
34904         Roo.EventManager.removeResizeListener(this.onResize, this);
34905         for(var i = 0, len = this.items.length; i < len; i++){
34906             this.items[i].purgeListeners();
34907         }
34908         if(removeEl === true){
34909             this.el.update("");
34910             this.el.remove();
34911         }
34912     },
34913     
34914     createStrip : function(container)
34915     {
34916         var strip = document.createElement("nav");
34917         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34918         container.appendChild(strip);
34919         return strip;
34920     },
34921     
34922     createStripList : function(strip)
34923     {
34924         // div wrapper for retard IE
34925         // returns the "tr" element.
34926         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34927         //'<div class="x-tabs-strip-wrap">'+
34928           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34929           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34930         return strip.firstChild; //.firstChild.firstChild.firstChild;
34931     },
34932     createBody : function(container)
34933     {
34934         var body = document.createElement("div");
34935         Roo.id(body, "tab-body");
34936         //Roo.fly(body).addClass("x-tabs-body");
34937         Roo.fly(body).addClass("tab-content");
34938         container.appendChild(body);
34939         return body;
34940     },
34941     createItemBody :function(bodyEl, id){
34942         var body = Roo.getDom(id);
34943         if(!body){
34944             body = document.createElement("div");
34945             body.id = id;
34946         }
34947         //Roo.fly(body).addClass("x-tabs-item-body");
34948         Roo.fly(body).addClass("tab-pane");
34949          bodyEl.insertBefore(body, bodyEl.firstChild);
34950         return body;
34951     },
34952     /** @private */
34953     createStripElements :  function(stripEl, text, closable)
34954     {
34955         var td = document.createElement("li"); // was td..
34956         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34957         //stripEl.appendChild(td);
34958         /*if(closable){
34959             td.className = "x-tabs-closable";
34960             if(!this.closeTpl){
34961                 this.closeTpl = new Roo.Template(
34962                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34963                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34964                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34965                 );
34966             }
34967             var el = this.closeTpl.overwrite(td, {"text": text});
34968             var close = el.getElementsByTagName("div")[0];
34969             var inner = el.getElementsByTagName("em")[0];
34970             return {"el": el, "close": close, "inner": inner};
34971         } else {
34972         */
34973         // not sure what this is..
34974             if(!this.tabTpl){
34975                 //this.tabTpl = new Roo.Template(
34976                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34977                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34978                 //);
34979                 this.tabTpl = new Roo.Template(
34980                    '<a href="#">' +
34981                    '<span unselectable="on"' +
34982                             (this.disableTooltips ? '' : ' title="{text}"') +
34983                             ' >{text}</span></span></a>'
34984                 );
34985                 
34986             }
34987             var el = this.tabTpl.overwrite(td, {"text": text});
34988             var inner = el.getElementsByTagName("span")[0];
34989             return {"el": el, "inner": inner};
34990         //}
34991     }
34992         
34993     
34994 });
34995
34996 /**
34997  * @class Roo.TabPanelItem
34998  * @extends Roo.util.Observable
34999  * Represents an individual item (tab plus body) in a TabPanel.
35000  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35001  * @param {String} id The id of this TabPanelItem
35002  * @param {String} text The text for the tab of this TabPanelItem
35003  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35004  */
35005 Roo.bootstrap.panel.TabItem = function(config){
35006     /**
35007      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35008      * @type Roo.TabPanel
35009      */
35010     this.tabPanel = config.panel;
35011     /**
35012      * The id for this TabPanelItem
35013      * @type String
35014      */
35015     this.id = config.id;
35016     /** @private */
35017     this.disabled = false;
35018     /** @private */
35019     this.text = config.text;
35020     /** @private */
35021     this.loaded = false;
35022     this.closable = config.closable;
35023
35024     /**
35025      * The body element for this TabPanelItem.
35026      * @type Roo.Element
35027      */
35028     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35029     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35030     this.bodyEl.setStyle("display", "block");
35031     this.bodyEl.setStyle("zoom", "1");
35032     //this.hideAction();
35033
35034     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35035     /** @private */
35036     this.el = Roo.get(els.el);
35037     this.inner = Roo.get(els.inner, true);
35038     this.textEl = Roo.get(this.el.dom.firstChild, true);
35039     this.pnode = Roo.get(els.el.parentNode, true);
35040     this.el.on("mousedown", this.onTabMouseDown, this);
35041     this.el.on("click", this.onTabClick, this);
35042     /** @private */
35043     if(config.closable){
35044         var c = Roo.get(els.close, true);
35045         c.dom.title = this.closeText;
35046         c.addClassOnOver("close-over");
35047         c.on("click", this.closeClick, this);
35048      }
35049
35050     this.addEvents({
35051          /**
35052          * @event activate
35053          * Fires when this tab becomes the active tab.
35054          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35055          * @param {Roo.TabPanelItem} this
35056          */
35057         "activate": true,
35058         /**
35059          * @event beforeclose
35060          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35061          * @param {Roo.TabPanelItem} this
35062          * @param {Object} e Set cancel to true on this object to cancel the close.
35063          */
35064         "beforeclose": true,
35065         /**
35066          * @event close
35067          * Fires when this tab is closed.
35068          * @param {Roo.TabPanelItem} this
35069          */
35070          "close": true,
35071         /**
35072          * @event deactivate
35073          * Fires when this tab is no longer the active tab.
35074          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35075          * @param {Roo.TabPanelItem} this
35076          */
35077          "deactivate" : true
35078     });
35079     this.hidden = false;
35080
35081     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35082 };
35083
35084 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35085            {
35086     purgeListeners : function(){
35087        Roo.util.Observable.prototype.purgeListeners.call(this);
35088        this.el.removeAllListeners();
35089     },
35090     /**
35091      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35092      */
35093     show : function(){
35094         this.pnode.addClass("active");
35095         this.showAction();
35096         if(Roo.isOpera){
35097             this.tabPanel.stripWrap.repaint();
35098         }
35099         this.fireEvent("activate", this.tabPanel, this);
35100     },
35101
35102     /**
35103      * Returns true if this tab is the active tab.
35104      * @return {Boolean}
35105      */
35106     isActive : function(){
35107         return this.tabPanel.getActiveTab() == this;
35108     },
35109
35110     /**
35111      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35112      */
35113     hide : function(){
35114         this.pnode.removeClass("active");
35115         this.hideAction();
35116         this.fireEvent("deactivate", this.tabPanel, this);
35117     },
35118
35119     hideAction : function(){
35120         this.bodyEl.hide();
35121         this.bodyEl.setStyle("position", "absolute");
35122         this.bodyEl.setLeft("-20000px");
35123         this.bodyEl.setTop("-20000px");
35124     },
35125
35126     showAction : function(){
35127         this.bodyEl.setStyle("position", "relative");
35128         this.bodyEl.setTop("");
35129         this.bodyEl.setLeft("");
35130         this.bodyEl.show();
35131     },
35132
35133     /**
35134      * Set the tooltip for the tab.
35135      * @param {String} tooltip The tab's tooltip
35136      */
35137     setTooltip : function(text){
35138         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35139             this.textEl.dom.qtip = text;
35140             this.textEl.dom.removeAttribute('title');
35141         }else{
35142             this.textEl.dom.title = text;
35143         }
35144     },
35145
35146     onTabClick : function(e){
35147         e.preventDefault();
35148         this.tabPanel.activate(this.id);
35149     },
35150
35151     onTabMouseDown : function(e){
35152         e.preventDefault();
35153         this.tabPanel.activate(this.id);
35154     },
35155 /*
35156     getWidth : function(){
35157         return this.inner.getWidth();
35158     },
35159
35160     setWidth : function(width){
35161         var iwidth = width - this.pnode.getPadding("lr");
35162         this.inner.setWidth(iwidth);
35163         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35164         this.pnode.setWidth(width);
35165     },
35166 */
35167     /**
35168      * Show or hide the tab
35169      * @param {Boolean} hidden True to hide or false to show.
35170      */
35171     setHidden : function(hidden){
35172         this.hidden = hidden;
35173         this.pnode.setStyle("display", hidden ? "none" : "");
35174     },
35175
35176     /**
35177      * Returns true if this tab is "hidden"
35178      * @return {Boolean}
35179      */
35180     isHidden : function(){
35181         return this.hidden;
35182     },
35183
35184     /**
35185      * Returns the text for this tab
35186      * @return {String}
35187      */
35188     getText : function(){
35189         return this.text;
35190     },
35191     /*
35192     autoSize : function(){
35193         //this.el.beginMeasure();
35194         this.textEl.setWidth(1);
35195         /*
35196          *  #2804 [new] Tabs in Roojs
35197          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35198          */
35199         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35200         //this.el.endMeasure();
35201     //},
35202
35203     /**
35204      * Sets the text for the tab (Note: this also sets the tooltip text)
35205      * @param {String} text The tab's text and tooltip
35206      */
35207     setText : function(text){
35208         this.text = text;
35209         this.textEl.update(text);
35210         this.setTooltip(text);
35211         //if(!this.tabPanel.resizeTabs){
35212         //    this.autoSize();
35213         //}
35214     },
35215     /**
35216      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35217      */
35218     activate : function(){
35219         this.tabPanel.activate(this.id);
35220     },
35221
35222     /**
35223      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35224      */
35225     disable : function(){
35226         if(this.tabPanel.active != this){
35227             this.disabled = true;
35228             this.pnode.addClass("disabled");
35229         }
35230     },
35231
35232     /**
35233      * Enables this TabPanelItem if it was previously disabled.
35234      */
35235     enable : function(){
35236         this.disabled = false;
35237         this.pnode.removeClass("disabled");
35238     },
35239
35240     /**
35241      * Sets the content for this TabPanelItem.
35242      * @param {String} content The content
35243      * @param {Boolean} loadScripts true to look for and load scripts
35244      */
35245     setContent : function(content, loadScripts){
35246         this.bodyEl.update(content, loadScripts);
35247     },
35248
35249     /**
35250      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35251      * @return {Roo.UpdateManager} The UpdateManager
35252      */
35253     getUpdateManager : function(){
35254         return this.bodyEl.getUpdateManager();
35255     },
35256
35257     /**
35258      * Set a URL to be used to load the content for this TabPanelItem.
35259      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35260      * @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)
35261      * @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)
35262      * @return {Roo.UpdateManager} The UpdateManager
35263      */
35264     setUrl : function(url, params, loadOnce){
35265         if(this.refreshDelegate){
35266             this.un('activate', this.refreshDelegate);
35267         }
35268         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35269         this.on("activate", this.refreshDelegate);
35270         return this.bodyEl.getUpdateManager();
35271     },
35272
35273     /** @private */
35274     _handleRefresh : function(url, params, loadOnce){
35275         if(!loadOnce || !this.loaded){
35276             var updater = this.bodyEl.getUpdateManager();
35277             updater.update(url, params, this._setLoaded.createDelegate(this));
35278         }
35279     },
35280
35281     /**
35282      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35283      *   Will fail silently if the setUrl method has not been called.
35284      *   This does not activate the panel, just updates its content.
35285      */
35286     refresh : function(){
35287         if(this.refreshDelegate){
35288            this.loaded = false;
35289            this.refreshDelegate();
35290         }
35291     },
35292
35293     /** @private */
35294     _setLoaded : function(){
35295         this.loaded = true;
35296     },
35297
35298     /** @private */
35299     closeClick : function(e){
35300         var o = {};
35301         e.stopEvent();
35302         this.fireEvent("beforeclose", this, o);
35303         if(o.cancel !== true){
35304             this.tabPanel.removeTab(this.id);
35305         }
35306     },
35307     /**
35308      * The text displayed in the tooltip for the close icon.
35309      * @type String
35310      */
35311     closeText : "Close this tab"
35312 });